WordPress wp2shell 漏洞深度解析:一个匿名请求拿下服务器,百度云防护已紧急更新防御规则
WordPress 7.0.2 紧急安全更新修复了被命名为 wp2shell 的高危漏洞。安全圈普遍将其称为近几年 WordPress 核心最严重的漏洞之一——它让匿名、完全未经身份验证的攻击者,在未安装任何第三方插件的默认 WordPress 环境中运行任意代码。换句话说,这是一个预身份验证的远程代码执行(RCE)漏洞:无需密码,一个 HTTP 请求就能控制你的服务器。
重点提醒:百度云防护(需完成 ICP 备案后使用)已针对 wp2shell 紧急更新了 WAF 防御规则,可拦截该漏洞的关键利用特征。本文在拆解攻击原理的同时,也提醒已备案站长及时确认规则已开启。
一、两把钥匙:wp2shell 其实是”组合拳”
wp2shell 不是一个漏洞,而是两个漏洞搭伙作案:
| CVE 编号 | 漏洞类型 | CVSS 评分 | 角色 |
|---|---|---|---|
| CVE-2026-60137 | WP_Query SQL 注入 | 9.1 | 武器 |
| CVE-2026-63030 | REST /batch/v1 路由混淆 |
7.5 | 通行证 |
把两个漏洞单独拿出来都掀不起浪:
- CVE-2026-60137 虽 9.1 分,但触发它的路由原本要登录才能访问,攻击者连门都摸不到——一把没开保险的枪。
- CVE-2026-63030 只是个权限绕过,绕过去后却没有可打的靶子。
二者一配合,路由混淆负责把恶意输入免认证地送到注入点门口,SQL 注入负责开火。枪和通行证都齐了,攻击者无需任何前提条件,一个匿名请求即可控制服务器。
二、枪:WP_Query 里的 SQL 注入(CVE-2026-60137)
问题出在 WP_Query 的 author__not_in 参数,这是一个典型的 PHP 类型转换(type juggling)问题。
该参数本应是一个 ID 数组,代码假定传入的就是数组,直接把值拼进 SQL 查询。如果传入的不是数组,而是一段精心构造的字符串,针对数组的清理逻辑会被绕过——你传什么,SQL 里就拼什么,注入就这么成了。
// 预期: author__not_in = [1, 2, 3] -> "AND wp_posts.post_author NOT IN (1,2,3)"
// 传入: author__not_in = "1) AND 1=0 UNION ALL SELECT ... -- -"
// 字符串被原样拼接进 SQL
$where .= " AND {$wpdb->posts}.post_author NOT IN ({$author__not_in})";
典型的 UNION 注入片段(通过注释禁用 per_page=-1 保证注入执行):
author__not_in=1) AND 1=0 UNION ALL SELECT <23 个字段> -- -&per_page=-1
该注入自 WordPress 6.8 就埋下,但暴露的 author__not_in 参数需要身份验证才能访问,所以一直没爆。要匿名触发它,需要一个”通行证”。
三、通行证:批处理接口的路由混淆(CVE-2026-63030)
WordPress 5.6 起新增批量操作 REST 接口 /wp-json/batch/v1,作用是将多个 REST 请求打包成一个 HTTP 调用,服务器按顺序处理。6.9 引入的缺陷是两个数组不同步:当某个子请求触发错误,两个数组会向右错位一格,导致第 N 个子请求跑在了第 N-1 个子请求的处理程序和权限上下文里。
攻击者可构造嵌套批处理请求(double batch),让本需权限的 GET /wp/v2/widgets 在公开的 posts::get_items() 处理程序下执行。白名单检查形同虚设,恶意输入在完全未登录状态下被送到 SQL 注入点。枪,上膛了。
四、完整攻击链:从一个请求到 WebShell
整个攻击只需一个匿名 HTTP 请求:
- 绕过认证:向
/wp-json/batch/v1发送嵌套的畸形批处理请求,靠路由混淆让公开路由为敏感路由打掩护。 - 触发注入:恶意输入到达
author__not_in(在 REST 批量请求中表现为author_exclude,传入WP_Query即author__not_in)。Payload 为经典 UNION 注入,末尾-- -为 SQL 注释符,用于注释后续字段保证注入完整执行。 - 伪造数据行,拿到写入能力:往
wp_posts/wp_options伪造带<site-url>标记的行,骗 WordPress 自己创建真实的 oEmbed 缓存条目和 transient。标识符可计算(post_name = md5(url + attributes)),攻击者对写入内容了如指掌。 - 提权到管理员上下文:伪造一条带真实管理员
user_id的customize_changeset记录,配合post_type=request的行,触发在管理员上下文执行的parse_request。 - 创建管理员,植入 WebShell:同一批处理请求内再塞一个
POST /wp/v2/users新建管理员账号,然后自毁插件执行系统命令。攻击者拿到 shell——这便是 “wp2shell” 之名的由来。
五、为什么极度严重
- 不用登录:匿名攻击,无需账号与前置条件。
- 默认安装即中招:不挑插件不挑主题,纯净 WordPress 也会被攻陷。
- 一个请求打完收工:脚本批量扫描成本约等于零。
- PoC 已满天飞:7 月 18 日 GitHub 上已有可用 PoC 与现成 Docker 复现环境(wp2shell-lab)。
- 盘子太大:全球超 40% 网站运行在 WordPress 上。
用了 Redis / Memcached 持久化对象缓存是否就安全?只能算”半个没事”:持久化缓存会改变 transient 写入路径,可能断掉 RCE 链,但对底层 SQL 注入毫无办法,攻击者照样可免认证读库。
六、百度云防护已紧急更新 wp2shell 防御规则(重点提醒)
针对这一组合漏洞,百度云防护已在 WAF 规则库紧急上线专项防御,覆盖以下利用特征:
| 防护点 | 拦截内容 |
|---|---|
| 批量接口滥用 | /wp-json/batch/v1 异常批量调用、嵌套批处理(double batch)特征 |
| SQL 注入特征 | author__not_in / author_exclude 参数中的 UNION、类型混淆注入 payload |
| 提权与写库行为 | 伪造 customize_changeset、wp_options 可疑写入等 WebShell 前置动作 |
| 管理员账号创建 | POST /wp/v2/users 异常建号行为 |
已接入百度云防护(需完成 ICP 备案)的站点,建议在控制台确认上述规则已更新并处于”拦截”状态。托管在速度网络的已备案用户,规则通常自动下发,但仍建议登录控制台核对,必要时手动开启”严格模式”。
必须敲黑板:WAF 封锁是临时措施,不是修复。编码一下、换换大小写、变个路由形式,WAF 规则就可能被绕过,且它管不了从 6.8 就存在的底层 SQL 注入。临时方案唯一的意义是争取升级时间,真正关门的是补丁。
七、临时缓解方案(争取升级时间)
在升级前,可用 rest_pre_dispatch 禁用 Batch API:
add_filter('rest_pre_dispatch', function( $result, $server, $request){
if('/batch/v1' !== strtolower(untrailingslashit($request->get_route()))){
return $result;
}
return new WP_Error('rest_batch_disabled', '批量 API 已禁用', ['status'=>401]);
}, -1000, 3 );
也可在 Nginx 或防火墙层面直接封禁 /wp-json/batch/v1 路由的访问。
八、升级后务必排查入侵痕迹
升级完别急着走,顺手查一遍:
- 用户列表里有没有不认识的管理员;
- 插件列表里有没有没装过的插件;
wp_posts/wp_options里有没有可疑数据行;- 访问日志里有没有打
/batch/v1的异常请求。
九、延伸:AI 写插件正在给站点”埋雷”
现在越来越多人用 AI 写 WordPress 插件,方便是真方便,但 AI 生成的代码追求”能跑”而非”安全”:输入不校验、SQL 直接拼、权限检查与 nonce 验证经常”忘记”写。攻击者也在用 AI,从前分析漏洞、写 exploit 要几天,现在几小时即可。涉及数据库查询、文件上传、权限判断的代码,上线前务必人工过一遍。
十、结语
wp2shell 最令人不安的是它的”完美配合”:一个 9.1 分的注入躺了一年多没人够得着,一个 7.5 分的路由混淆恰好递上免认证入口,最终拼成”无需登录、一个请求、直达 WebShell”的完整攻击链。
行动清单:
- 立即升级 WordPress 到 7.0.2 及以上;
- 已备案站点确认百度云防护 wp2shell 防御规则已开启;
- 升级后按第八节排查入侵痕迹;
- 不要用 WAF 代替补丁。
