面向影响 5 亿+ 网站的 WordPress 预认证 XSS-to-RCE 攻击链的行为优先批量扫描器与证据级 PoC 生成器。
仅用于检测。不进行武器化。专为漏洞赏金计划和蓝队打造。
这是什么? • 快速开始 • Shodan 搜索语法 • 使用方法 • 判定矩阵 • 检测 • 常见问题
在扫描之前,先在整个互联网上寻找可能存在漏洞的 WordPress 实例:
http.component:"wordpress" -http.title:"Just a moment"
查找 WordPress 站点,同时排除 Cloudflare「I'm Under Attack」模式 / 机器人防护页面,这些页面会拦截或质询自动化请求。
http.component:"wordpress" http.title:"Log In"
仅返回 WordPress 登录页面 — 这正是 CVE-2026-64638 的确切攻击面。
http.component:"wordpress" "wp-content" "?ver=7.0" -"?ver=7.0.3"
通过资源版本指纹识别,标记出未应用 7.0.3 补丁的 WordPress 7.0.x 实例。
http.component:"wordpress" http.html:"wp-login.php"
捕获那些 wp-login.php 可访问但未必是当前页面的站点 — 覆盖面更广。
http.component:"wordpress" -http.title:"Just a moment" -http.title:"Attention Required" -org:"Cloudflare"
激进的过滤器,可剔除大多数由 Cloudflare 前置的目标。在配合 --active 大规模扫描时使用 — Cloudflare 会对探测请求进行限速或拦截。
提示: 使用
shodan download导出 Shodan 结果,并将主机名直接管道输入xss2shell_mass.py -i。
2026 年 8 月 7 日,pwn.ai 披露了 CVE-2026-64638(XSS2Shell) — 这是 WordPress Core 中一个严重的预认证跨站脚本漏洞,可在服务器上一路串联升级为远程代码执行。[citation:pwn.ai blog]
该漏洞利用了 PHP 的 strip_tags() 与 WordPress 的 wp_kses_post() 之间的解析器分歧:
strip_tags() 使用紧跟字母的 < 来识别 HTML 标签。< area id=...>(带空格)会被视为文本 — 因此它能存活。wp_kses_post()(KSES)将 < area 识别为有效的 <area> 元素 — 而 <area> 在 KSES 中属于白名单元素。[citation:pwn.ai blog]一次使用特制用户名 < area id=ajaxurl href=/?rest_route=/&_method=GET&_jsonp=alert>... 的失败登录即可同时绕过这两种过滤器,在登录页面上被渲染为实时 DOM,通过 DOM 污染劫持 WordPress 自身的 user-profile.js 脚本,并在 WordPress 源中触发 alert() — 零点击、零认证、零 Cookie。[citation:pwn.ai blog]
升级为已登录的管理员?同样的原语可通过同源方法执行(SOME)窃取应用程序密码,上传恶意插件,并以 www-data 身份执行 PHP。[citation:pwn.ai blog] [citation:hadrian.io blog]
受影响版本: WordPress 6.4 至 7.0.2 — 已在 7.0.3 中修复,并向后移植到 4.7+。
影响范围: 披露时约 5 亿个网站。[citation:pwn.ai blog]
| 资源 | 链接 |
|---|---|
| 原始披露(pwn.ai) | pwn.ai/blog/xss2shell |
| Hadrian 技术分析 | hadrian.io/blog/wordpress-xss2shell |
| WordPress 安全公告(GHSA) | GHSA-52p2-r8wf-jcrf |
| SOME 攻击研究(2022) | pwn.ai/blog/bypass-csp-using-wordpress |
| WordPress 7.0.3 发布 | wordpress.org/news/2026/08/wordpress-7-0-3-release |
这是一个仅用于检测的工具包。它不会将漏洞武器化 — 它为安全研究人员、漏洞赏金猎人和蓝队提供所需的一切,以便:
「版本字符串只能说明代码应该处于什么补丁级别。
只有登录页过滤器(sanitizer)的行为才能说明该漏洞是否真的触发。」
托管主机会在静默中向后移植安全补丁,而不更新版本字符串。登录加固插件则会完全替换错误消息,即使在不安全的版本上也会封死反射通道。仅凭版本判断的扫描器既会产生误报,也会产生漏报。 本扫描器只发送一个良性探测请求,并对实际的过滤器行为进行分类。
git clone https://github.com/jakestone/xss2shell.git
cd xss2shell
pip install -r requirements.txt
# Passive — no probes sent to target, version + endpoint fingerprinting only
python3 xss2shell_mass.py -i domains.txt -o results
# Active — sends ONE benign failed-login per host (authorized assets only!)
python3 xss2shell_mass.py -i domains.txt -o results --active --workers 80
# Single target
python3 make_poc.py --target https://blog.example.com
# Batch from scanner output
python3 make_poc.py --from-results results.csv -o pocs/
在录制视频的同时,在浏览器中打开生成的 .poc.html → 如果 alert() 触发,你就捕获到了预认证 XSS 的证据。
xss2shell_mass.py)usage: xss2shell_mass.py [-h] -i INPUT [-o OUTPUT]
[--active] [--workers WORKERS]
[--timeout TIMEOUT] [--quiet]
| 标志 | 说明 |
|---|---|
-i, --input | 包含主机列表的文件,每行一个主机(裸域名或完整 URL) |
-o, --output | 输出文件的基础路径(生成 .csv + .json) |
--active | 启用行为探测 — 每台主机一次失败登录 |
--workers | 线程池大小(默认:50,连接良好时最多约 200) |
--timeout | HTTP 超时时间(秒)(默认:10) |
--quiet | 仅打印 confirmed_vulnerable、vulnerable 和 likely_vulnerable |
?ver= 参数、wp-content 引用)user-profile.js gadget 已入队、核心资源版本/?rest_route=/&_method=GET&_jsonp=<random> 发起无害的 GET 请求 — JSONP 路径是否开放?--active 标志)发送一次用户名为 < area id=<RANDOM> href=/x2s> 的失败登录,并对 HTML 响应进行分类:
bypass — 带有我们标记的真实 <area> 元素存活 → 确认存在 strip_tags/KSES 不一致escaped — 标记存在但已被实体编码 → 已打补丁或已加固stripped — 显示默认 WP 错误,标签已被移除 → acevomod 或已打补丁closed — 完全没有用户名回显 → 已安装登录加固插件make_poc.py)usage: make_poc.py [-h] [--target TARGET] [--from-results FROM_RESULTS]
[-o OUTDIR]
为每个目标生成已发布的 pwn.ai PoC 页面 — 即能在未修补的 WordPress 上触发 alert() 的精确 HTML 表单。注释中包含三种载荷变体:
| 变体 | href 值 | 适用场景 |
|---|---|---|
| 默认 | /?rest_route=/&_method=GET&_jsonp=alert | 标准 WordPress |
| Envelope | /?rest_route=/&_method=GET&_envelope=1&_jsonp=alert | REST 返回 401(包装为 200) |
| WAF 绕过 | /wp-json/wp/v2/statuses/publish?_jsonp=alert&_method=GET | ?rest_route= 被 WAF 拦截 |
扫描器的判定引擎将版本分类(来自 WordPress.org 的 stable-check API)与行为证据相结合,产生 10 种不同的判定结果:
| 判定结果 | 条件 |
|---|---|
confirmed_vulnerable 🔴 | 版本不安全,且探测标记以 <area> 元素存活,且存在 user-profile.js gadget |
vulnerable 🔴 | 根据 wordpress.org 判断版本不安全;未运行行为探测(请使用 --active 重新运行) |
likely_vulnerable 🟠 | 探测标记存活,但 user-profile.js 未入队(缺少已发布的自动触发 gadget) |
mitigated 🟣 | 版本不安全,但探测标记已被转义/剥离/无回显(静默向后移植或已加固) |
likely_patched 🟢 | 版本隐藏/未知,但探测标记已被转义/剥离 |
patched 🟢 | 版本为 latest 或 outdated(包含安全向后移植) |
not_wordpress ⚫ | 未检测到 WordPress 指纹 |
unreachable ⚫ | 连接失败(超时、SSL、DNS) |
inconclusive 🟡 | WAF 拦截、Cloudflare 质询、版本隐藏且未运行探测,或登录页面缺失 |
error 🟡 | 扫描期间发生意外故障 |
CSV 列: host、url、status、checker_status、wp_version、branch_status、evidence、http、ms、error
checker_status 列映射到 pwn.ai 公共检查器的词汇表(vulnerable / patched / not_wordpress / unreachable / inconclusive / error),便于直接关联。
如果你处于防御方,以下是该漏洞留下的取证信号:
# Primary signal: encoded '<' in the log parameter
POST /wp-login.php → log=%3C... (URL-encoded < in username field)
# Higher confidence: paired with REST pivoting
GET /?rest_route=/&_method=GET&_jsonp=... # JSONP callback
GET /wp-json/wp/v2/statuses/publish?_jsonp=... # WAF-bypass variant