wp2shell 深度拆解:WordPress 近十年首个核心 pre-auth RCE 利用链(CVE-2026-63030 + CVE-2026-60137)
WordPress 核心上一次出现无需插件配合的未认证 RCE 级别漏洞,要追溯到十年前。2026 年 7 月 17 日披露的 wp2shell 打破了这个纪录:两条 CVE(REST API 路由混淆 CVSS 9.8 + WP_Query SQL 注入 CVSS 5.9)串联成完整的 pre-auth 管理员创建链,默认配置、无需任何插件即可利用。披露数小时内开始大规模利用,Wordfence 防火墙累计拦截超过 1100 万次攻击。本文拆解利用链的每一环,以及它给所有 PHP 框架留下的教训。
漏洞速览
| 项目 | CVE-2026-63030 | CVE-2026-60137 |
|---|---|---|
| 类型 | REST API 批量请求路由混淆 | WP_Query SQL 注入 |
| CVSS(CNA) | 9.8 Critical | 5.9 Medium(CISA-ADP 评 9.1!) |
| 位置 | WP_REST_Server::serve_batch_request_v1() | author__not_in 参数处理 |
| 影响 | 子请求被分派给错误的 handler,绕过权限检查 | 标量字符串绕过过滤直达 SQL 拼接 |
| 影响版本 | 6.9.0–6.9.4 / 7.0.0–7.0.1 | 6.8.0–7.0.1 |
| 修复版本 | 7.0.2 / 6.9.5 | 7.0.2 / 6.9.5 / 6.8.6 |
利用链成立的版本交集是 6.9.x 和 7.0.x;6.8.x 只受 SQL 注入影响(链不成立),但同样必须升级到 6.8.6。
发现者是 Assetnote / Searchlight Cyber 的安全研究员 Adam Kues——他使用 OpenAI 的 GPT-5.6 Sol 辅助代码审计,整个过程耗时约 10 小时、计算成本约 25 美元。AI 辅助漏洞发现从概念验证进入生产力的标志性案例(本站此前拆解过 GPT-5.6 Sol 自身沙箱机制的相关事件,见文末延伸阅读)。CVE-2026-60137 由 TF1T、dtro 和 haongo 独立报告。
第一环:CVE-2026-63030 路由混淆——两个数组的下标错位
WordPress 的 /wp-json/batch/v1 端点允许客户端把多个 REST 子请求打包进一次 HTTP 请求。serve_batch_request_v1() 的内部实现用了两个并行数组:$validation(每个子请求的校验结果)和 $matches(每个子请求匹配到的路由与 handler)。
问题出在错误处理的不对称上:当某个子请求的 URL 解析失败(wp_parse_url() 出错)时,错误对象会写入 $validation 数组,但 $matches 数组里没有对应条目。校验循环和分派循环是分开跑的,两个数组从这一刻起发生下标错位——后面的子请求会拿到前面子请求的路由和 handler。
一句话总结这个原语:请求 A 的数据跑在了请求 B 的处理器上。
攻击者用它实现两个绕过:
- 绕过 batch schema 对内层 GET 的禁令:外层 batch 用一个"widget 形状"的请求去调用 batch 回调;
- 绕过路由级权限检查:内层用一个按 widget 路由校验通过的请求,去触发
posts集合的get_items()处理器。
第二环:CVE-2026-60137 SQL 注入——字符串绕过类型检查
WP_Query 的 author__not_in 参数设计上接受数组(作者 ID 列表),WordPress 只在参数是数组时才做整数化过滤:
// 简化示意(漏洞逻辑)
if ( is_array( $q['author__not_in'] ) ) {
// 数组路径:逐个 sanitize 整数化 —— 安全
$q['author__not_in'] = array_map( 'absint', $q['author__not_in'] );
}
// 字符串路径:没有 else 分支的过滤!
// 直接拼进 SQL:
// AND wp_posts.post_author NOT IN (<攻击者控制的字符串>)传入数组会被清洗,传入字符串则原样拼接——类型检查的漏网之鱼。攻击路径的巧妙之处在于怎么把字符串塞进去:
GET /wp/v2/categories?author_exclude=<SQL 载荷>categories 路由没有注册 author_exclude 参数,所以它原样通过该路由的参数校验;然后靠第一环的路由混淆,让这个请求实际由 posts 的 get_items() 处理器执行——而 posts 路由会把 author_exclude 映射到存在漏洞的 author__not_in 查询变量。SQL 注入的载荷就这样"借道"抵达了目标。
完整利用链:一个 HTTP 请求,五级跳
把两环串起来,攻击者用单个物理 HTTP 请求完成从匿名到管理员:
攻击者(无任何凭证)
│
│ ① POST /wp-json/batch/v1
│ 外层 batch:widget 形状请求 → 触发 batch 回调(绕过内层 GET 禁令)
│ 内层 batch:widget 校验请求 → 实际由 posts get_items() 执行
│ 参数:author_exclude = "' UNION SELECT <23 列伪造 wp_posts 行>-- -"
▼
WordPress 核心
│ ② 路由混淆生效,SQL 注入触发
│ ③ UNION 注入返回伪造的 wp_posts 行(按物理 23 列顺序构造)
│ ④ WordPress 把伪造行转成 WP_Post 对象并写入缓存
│ ⑤ Core 更新逻辑消费伪造对象:
│ 以真实管理员的 user_id 发布一个 Customizer changeset
│ → 短暂"借用"管理员身份
▼
⑥ 攻击者创建自己的管理员账号
│
▼
⑦ 上传恶意插件 / web shell → 服务器 RCE这里有个反直觉的设计点:SQL 注入本身是 SELECT-only 的——它不能直接写数据库。攻击者的解法是注入伪造的 post 行,让 WordPress 自己的对象缓存和更新逻辑把伪造数据当成真数据消费,再借 Customizer changeset 的发布流程冒用管理员身份。这把"只读注入"升级成了"身份冒用",也是整条链最值得安全研究者细品的一环。
时间线与在野利用
- 2026-07-17:漏洞披露,6.8.6 / 6.9.5 / 7.0.2 同步发布修复(7.1 Beta2 同样修复);
- 披露当天:攻击量较前一日暴涨 277 倍;
- 数小时内:公开 PoC 出现在 GitHub,德国联邦信息安全局(BSI)发布橙色级别警告;
- 累计:约 195 万次攻击记录,Wordfence 单防火墙拦截超 1100 万次;
- 2026-08-04:日本 Cyber Security Cloud 发布在野利用报告,WordPress 6.8.0–7.1 Beta 全版本范围受影响;
- 2026-09:PoC 仍在流传,vulhub 提供了 Docker 复现环境,未打补丁站点仍在被持续扫描。
评分分歧:同一个 CVE,5.9 与 9.1 之间差着什么
这次事件里有个值得所有做漏洞管理的人记住的插曲:CVE-2026-60137 的 CNA(WPScan)给的 CVSS 是 5.9 Medium,而 CISA-ADP 对同一个 CVE 评了 9.1 Critical——差 3.2 分、跨两个等级。
分歧的根源在于视角:单看"SQL 注入 + 只读"确实是 Medium 量级;但放在"可被路由混淆无认证触发 + 可组成 pre-auth RCE 链"的上下文里就是 Critical。更危险的是 6.8.x 系列只受 5.9 分的 SQL 注入影响、不受路由混淆影响——如果你的补丁优先级流程按 CVSS 分数排序,6.8.x 站点的这条注入可能被排到低优先级,而这正是分数驱动 triage 的经典盲区。CVSS 评的是"漏洞在真空中的最大危害",不是"它在你的攻击面里的实际位置"。
防御清单
立即动作(所有 WordPress 站点):
- 升级到 7.0.2 / 6.9.5 / 6.8.6 及以上(8.x 分支用户等后续正常版本,不受影响);
- 无法立即升级的站点:WAF 规则拦截含
author_exclude参数的/wp/v2/categories请求与异常的/batch/v1嵌套请求;限制/wp-json/batch/v1的访问来源; - 排查 IOC:核对管理员账号列表、检查异常 REST API 访问日志、留意陌生登录尝试与新增插件(BSI 警告的三项检查)。
给 PHP 框架与插件开发者的工程教训:
- 批量/嵌套请求端点是高危区:校验数组和分派数组必须同源同构——要么用单一结构体数组(每项自带 validation + match),要么在错误路径上同步两个数组。任何"两个并行数组靠下标对齐"的设计都是错位漏洞的温床;
- 参数过滤不要依赖类型假设:
is_array()才过滤的写法意味着"传字符串就能绕过"。对外部输入统一走入口清洗(WordPress 的sanitize_*/ Laravel 的 FormRequest),而不是按预期类型分派; - 未注册参数要显式拒绝:categories 路由不认识
author_exclude却放行了它——未知参数静默透传,加上参数映射的间接层(author_exclude → author__not_in),让污染数据流穿过了两道"看起来存在"的校验; - 对象反序列化/ hydrate 边界要验证数据来源:WP_Post 从查询结果构造时信任了行数据——任何"把数据库行变成领域对象再驱动业务逻辑"的路径,都要意识到注入者可以伪造行。
小结
wp2shell 的技术栈没有超出"路由混淆 + SQL 注入"这两个经典类别的组合,但它的每个环节都踩在工程实践的真实软肋上:并行数组的下标耦合、按类型分派的过滤逻辑、未注册参数的静默透传、以及"SELECT-only 注入危害有限"的误判。加上 AI 辅助审计把发现成本压到 25 美元量级——这类漏洞的发现速度只会越来越快。如果你的业务还挂在 6.x 旧版本上,今天就是升级日。
参考资料
- Searchlight Cyber / Assetnote 技术分析
- Cyber Security Cloud:wp2shell 在野利用报告(2026-08-04)
- vulhub:CVE-2026-63030 Docker 复现环境
- Undercode Testing:wp2shell 技术深潜
- 本站相关:OpenAI GPT-5.6 Sol 零日链深度拆解(AI 辅助安全研究镜像案例)、FrankenPHP CVE-2026-45062:Unicode 路径分割 RCE、PHP 错误与异常处理、phargo:Rust PHP 引擎与 WordPress、LLM Prompt Injection 防御

