【漏洞预警】WordPress wp2shell 紧急漏洞预警:CVE-2026-63030 / CVE-2026-60137 未授权 RCE 链

· 2026-07-19 09:32 · 3 阅读

猎户攻防实验室 2026-07-19 09:32 北京

WordPress wp2shell 紧急漏洞预警:CVE-2026-63030 / CVE-2026-60137 未授权 RCE 链

2026 年 7 月 17 日,WordPress 发布 7.0.2 / 6.9.5 / 6.8.6 与 7.1 beta 2 安全版本,修复由两个核心漏洞串联而成的 wp2shell 攻击链:

  • • CVE-2026-63030 — REST API batch/v1 批处理路由混淆,CNA CVSS 9.8 Critical

  • • CVE-2026-60137 — WP_Query 的 author__not_in SQL 注入,CNA CVSS 5.9 Medium

两者串联后,在默认配置、无需认证、无需任何插件的情况下,可对开箱即用的 WordPress 实现远程代码执行。Patchstack 已将 CVE-2026-63030 标记为"Known to be exploited"(2026-07-17 晚 7 点 ET 前已有野外利用报告),公开 PoC 已在 GitHub 流通。

一、影响版本

必须按分支判断,不要用"版本号 ≥ 某值即安全"的跨分支比较(7.0.0 数值上大于 6.9.5,但仍受影响)。

WordPress 版本

CVE-2026-60137

CVE-2026-63030

风险

< 6.8

不受影响

6.8.0 – 6.8.5

仅 SQLi

6.9.0 – 6.9.4

完整 wp2shell RCE 链
7.0.0 – 7.0.1

完整 wp2shell RCE 链
7.1 beta 1

必须更新

6.8.6 / 6.9.5 / 7.0.2 / 7.1 beta 2

已修复

二、攻击链与实测证据

入口:WordPress REST API 支持两种等价访问形式,防护必须同时覆盖

POST /wp-json/batch/v1        # path 形式
POST /?rest_route=/batch/v1   # query 形式 ← PoC 实测默认走这个

攻击链

  1. 1. 向 batch/v1 发送构造的批处理请求,触发 REST API 子请求匹配与调度之间的路由混淆;

  2. 2. 被错误调度的子请求把请求参数 author_exclude 映射到 WP_Query 的 author__not_in,因未做完整整数化处理 → SQL 注入;

  3. 3. 通过 UNION 假文章原语拖取 wp_users 表(实测:3 个请求拿到管理员 bcrypt 哈希 $wp$2y$10$...);

  4. 4. 利用 SQLi 在 wp_posts 伪造 customizer changeset / nav menu item → 自动创建名为 wp2_<16hex> 的管理员账号;

  5. 5. 用该管理员身份上传插件 webshell → RCE。

实测(隔离 Docker 环境,WordPress 7.0.1 默认配置):

完整链路(check → read → shell)在受影响版本上稳定复现;在 7.0.2 上 UNION 原语即不可用,桥接在第一步失败。

三、紧急排查

第 1 步:版本确认

wp core version
# 或无 WP-CLI 时
grep '\$wp_version[[:space:]]*=' /var/www/html/wp-includes/version.php

判读:

实际版本

处置

6.9.0 – 6.9.4

 / 7.0.0 – 7.0.1 / 7.1 beta 1

完整链路暴露,按 P0 立即处置

6.8.0 – 6.8.5

仅 SQLi,检查插件/主题是否暴露该参数,升级到 6.8.6

6.8.6

 / 6.9.5 / 7.0.2 / 7.1 beta 2

已修复

⚠️ 不要用"版本 ≥ 6.9.5"判断安全。7.0.0 数值上大于 6.9.5 但仍受影响。

第 2 步:检查 batch 入口是否可达

空 batch 请求探测(不含任何 payload,只判断路由是否暴露):

for url in \
'https://YOUR_SITE/wp-json/batch/v1' \
'https://YOUR_SITE/?rest_route=/batch/v1'
do
  curl -ksS --connect-timeout 10 -o /dev/null \
    -w "$url -> HTTP %{http_code}\n" \
    -H 'Content-Type: application/json' --data '{"requests":[]}'"$url"
done

判读:

path 形式

query 形式

含义

200207受影响版本的典型表现

(实测:受影响的 WP 7.0.1 上 path 形式 200、query 形式 207)

200

 / 207 / 明确 REST 参数错误

同左

入口可达,需结合版本判断风险

401

 / 403

同左

已被认证策略或安全设备阻断(缓解已生效)

404

同左

路由不可达,或被 WAF/重写规则隐藏 —— 不能单独据此判定安全

⚠️ 不要用 curl -I(HEAD 请求)测 batch/v1。实测 HEAD 会被 pretty-permalink 重写规则返回 301/404/405,即使 POST 实际可达。

第 3 步:访问日志找入口请求

⚠️ 必须同时匹配 path 形式和 query 形式,只匹配 path 形式会漏报(PoC 默认走 query 形式):

# 覆盖两种形式的入口请求
zgrep -hEi '"POST [^"]*(/wp-json/batch/v1/?|[?&]rest_route=(%2f|/)?batch(%2f|/)v1)' \
  /var/log/nginx/access.log* /var/log/apache2/access.log* 2>/dev/null
# PoC 默认 User-Agent 是字面值 "wp2shell",可作为附加筛选
zgrep -hE '"wp2shell"$' /var/log/nginx/access.log* /var/log/apache2/access.log* 2>/dev/null

命中后进一步提取:源 IP、User-Agent、状态码、请求大小、同源频率、CDN/源站日志是否能对应。

单次 batch POST 也可能是正常功能调用,不能仅凭路径判定入侵。但受影响版本上出现来源不明、频率异常或体积超大的 batch POST,应立即升级为高优先级调查。

第 4 步:数据库 IOC 检查(实测高可靠)

⚠️ 关于 PoC 清理行为(实测结论)

  • • PoC 预认证桥接建的临时管理员和 webshell 插件正常退出时会清理(实测从干净基线跑一次后,wp_users 和 plugins/ 都恢复原状)

  • • 但 PoC 客户端(urllib)在网络不稳时会出现 IncompleteRead 异常中断,中断时不会执行清理逻辑,会留下 wp2_<16hex> 管理员账号和 wp2shell_<8hex> webshell 目录

  • • PoC 完全不清理 wp_posts 伪造记录,每次跑都新增 2 条 —— 这是最可靠的入侵 IOC,比 wp_users 检查更稳

下面两条 SQL 都建议执行,互为补充:

先拿实际表前缀:

wp db prefix    # 通常返回 wp_,但很多站点做过硬化
-- 实测 IOC 1:PoC 桥接创建的临时管理员(残留)
SELECT ID, user_login, user_email, user_registered
FROM<prefix>users
WHERE user_login LIKE'wp2\_%'
OR user_registered >'2026-07-10'
ORDERBY user_registered DESC;
-- 实测 IOC 2:PoC 伪造的 wp_posts
-- 特征:post_type IN ('customize_changeset','nav_menu_item','request')
--      且 post_date 硬编码为 '2020-01-01 00:00:00'
SELECT ID, post_type, post_status, post_date, LEFT(post_title, 50)
FROM<prefix>posts
WHERE (post_type IN ('customize_changeset','nav_menu_item','request')
AND post_date ='2020-01-01 00:00:00')
OR post_title LIKE'wp2\_%';

判读:任一查询有结果 → 基本确认已被 PoC 利用

补充检查管理员角色(用于发现已存在账号被提权):

SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM<prefix>users u
JOIN<prefix>usermeta um ON um.user_id = u.ID
WHERE um.meta_key ='<prefix>capabilities'
AND um.meta_value LIKE'%administrator%'
ORDERBY u.user_registered DESC;

PHP/Apache 错误日志取证:UNION 注入触发时,WordPress 的 wpdb::print_error() 会把完整 SQL 写入 PHP error log。具体位置取决于部署方式:

  • • Apache + mod_php/var/log/apache2/error.log 或 /var/log/httpd/error_log

  • • Nginx + PHP-FPM/var/log/php-fpm.log 或 /var/log/php/error.log

  • • Docker 容器:PHP error_log 通常指向 stderr,通过 docker logs <container> 查看

  • • wp-content/debug.log仅当 wp-config.php 同时设置 define('WP_DEBUG', true) 和 define('WP_DEBUG_LOG', true) 时才会生成(默认 WordPress 镜像不会自动开启,所以这个文件大概率不存在,不要直接 grep 它)

# Apache 部署
grep -hE 'UNION ALL SELECT|post_author NOT IN' /var/log/apache2/error.log* 2>/dev/null
# Nginx + PHP-FPM 部署
grep -hE 'UNION ALL SELECT|post_author NOT IN' /var/log/php*/error*.log 2>/dev/null
# Docker 部署
docker logs <wordpress-container> 2>&1 | grep -E 'UNION ALL SELECT|post_author NOT IN'
# 仅当显式开启 WP_DEBUG_LOG=true 时才存在
grep -hE 'UNION ALL SELECT|post_author NOT IN' /var/www/html/wp-content/debug.log* 2>/dev/null

命中即强烈表明已遭 PoC 利用 —— payload 中可见伪造的 customize_changeset / nav_menu_item / request 等 post_type 字符串。

第 5 步:文件 IOC 检查

实测 PoC webshell 落在 wp-content/plugins/ 目录(通过管理员插件上传功能写入,另外会自动清理,不作为唯一排查证据)。下面第 1 条是 wp2shell 专属 IOC,第 2-4 条是通用 webshell 排查:

# 1. wp2shell 专属 IOC:实测 webshell 路径模式
#    PoC 通过插件上传能力写到 plugins/wp2shell_<8hex>/wp2shell_<8hex>.php 
find /var/www/html/wp-content/plugins -type d -name 'wp2shell_*' 2>/dev/null
find /var/www/html/wp-content/plugins -type f -path '*/wp2shell_*/*.php' 2>/dev/null
# 2. 通用排查:事件窗口内新增/修改的可疑 PHP 文件(含危险函数)
#    排除 wp-config.php / wp-config-docker.php(容器构建产物,含 base64 是 wp_salt 用,属误报)
find /var/www/html -type f -name '*.php' -newermt '2026-07-10' \
  -not -name 'wp-config.php' -not -name 'wp-config-docker.php' \
  -print0 2>/dev/null \
  | xargs -0 -r grep -lE 'eval[[:space:]]*\(|base64_decode[[:space:]]*\(|shell_exec[[:space:]]*\(' 2>/dev/null
# 3. 通用排查:uploads 目录原则上不应有可执行 PHP
#    (wp2shell 实测不会写这里,但其它攻击向量常滥用 uploads)
find /var/www/html/wp-content/uploads -type f \
  \( -iname '*.php' -o -iname '*.phtml' -o -iname '*.phar' \) 2>/dev/null
# 4. 核心 checksum 校验
wp core verify-checksums
wp plugin verify-checksums --all

判读

  • • 命中第 1 条 find wp2shell_* → 几乎确定是 PoC webshell(PoC 正常退出会清理,异常退出时残留)

  • • 命中第 2 条 → 通用 webshell 嫌疑,需结合文件来源、修改时间、checksum 综合判断

  • • 命中第 3 条 → 非本次 PoC 特征,但应立即调查(其它攻击向量)

  • • wp plugin verify-checksums 报 Couldn't fetch response ... 404 → 该插件不在 WordPress.org checksum 库,这本身是 IOC(实测:PoC 上传的 wp2shell_<8hex> 会触发此警告)

  • • wp core verify-checksums 报 File should not exist: wp-config-docker.php → Docker 镜像特例,非入侵痕迹

四、修复

4.1 首选方案:升级到对应分支安全版本

升级前先备份(数据库 + wp-content + wp-config.php):

# 默认升级到最新稳定版(当前 7.0.2)
wp core update
wp core verify-checksums
wp core version    # 确认精确版本,不是"大于某值"
# 需要保留当前分支时显式指定(按实际分支选其一,不要全跑)
wp core update --version=7.0.2    # 7.x 分支
wp core update --version=6.9.5    # 6.9 分支
wp core update --version=6.8.6    # 6.8 分支

4.2 Docker 部署升级陷阱

只跑 docker pull 不够 —— Compose 不会自动用新镜像,必须 --force-recreate

# 1. 先改 compose 文件里的 image: 标签为安全版本
#    image: wordpress:7.0.2-apache
# 2. 重新拉取 + 强制重建
docker compose pull
docker compose up -d --force-recreate
# 3. 验证容器内实际 WP 版本(不能只看镜像 tag)
docker compose exec wordpress php -r \
'include "/var/www/html/wp-includes/version.php"; echo $wp_version, PHP_EOL;'

⚠️ 升级前确认数据库、wp-contentuploads 都在持久化卷中,且有可恢复备份。

⚠️ 截至 2026-07-19,Docker Hub 上 wordpress:7.0.2 尚未发布wordpress:7.0.1-php8.2-apache 有 arm64 变体)。Docker 部署的站点建议改用容器内 wp core update 升级,等官方镜像或自行构建。

4.3 发行版包管理器部署(apt)

如果 WordPress 由 Debian/Ubuntu 包管理,应优先遵循发行版安全公告:

apt update && apt install --only-upgrade wordpress

发行版可能用回补补丁而非升级版本号,不能只看版本字符串判断是否修复,需对照发行版 changelog。

4.4 升级后验证

升级完成不等于安全,必须复测

# 1. 版本精确落在安全分支
wp core version    # 应输出 6.8.6 / 6.9.5 / 7.0.2
# 2. checksum 通过
wp core verify-checksums    # 应输出 "Success: WordPress installation verifies against official checksums."
# 3. 用第 2 步的探测确认 batch 入口行为符合预期(已升级版本上 POST 仍可达但 SQLi 失败)

五、无法立即升级时的临时缓解

临时措施必须同时覆盖两种入口:

/wp-json/batch/v1
/?rest_route=/batch/v1

5.1 MU 插件兜底(最稳,所有部署方式通用)

wp-content/mu-plugins/block-wp2shell.php

<?php/**
 * wp2shell emergency mitigation.
 * Remove after upgrading to 6.8.6 / 6.9.5 / 7.0.2.
 *
 * 实测:rest_pre_dispatch 在 REST 路由规范化之后触发,
 *       因此同时覆盖 path 和 query 两种入口。
 */
add_filter('rest_pre_dispatch'static function ($result$server$request) {
if ('/batch/v1' === untrailingslashit($request->get_route())) {
returnnewWP_Error('wp2shell_blocked',
'REST batch endpoint temporarily disabled.'array('status' => 403));
    }
return$result;
}, 103);

部署后从站点外部复测两个入口都应返回 403

for url in \
  'http://localhost:8083/wp-json/batch/v1' \
  'http://localhost:8083/?rest_route=/batch/v1'
do
  curl -ksS -o /dev/null -w "$url -> HTTP %{http_code}\n" \
    -H 'Content-Type: application/json' --data '{"requests":[]}' "$url"
done

5.2 Nginx 边缘层加固(与 MU 插件叠加,推荐)

# Pretty permalink 形式
location~* ^/(?:index\.php/)?wp-json/batch/v1/?$ {
return403;
}
# Query 形式(PoC 实测真实流量,必须保留这条)
if ($args~* "(^|&)rest_route=(%2f|/)batch(%2f|/)v1/?(&|$)") {
return403;
}

⚠️ 不要把阻断 /wp-json/wp/v2/users 当作修复。漏洞利用的子请求由 WordPress 内部 REST 服务器调度,不会作为新的外部 HTTP 请求再次经过 Nginx,所以这条规则对 wp2shell 无效。

5.3 Apache .htaccess 加固(适用 Apache + mod_rewrite)

⚠️ 实测注意事项

  • • .htaccess 仅在 Apache 主配置对 /var/www/html 设了 AllowOverride All(或 FileInfo)时才生效。WordPress 官方 Docker 镜像在 docker-php.conf 里启用了 AllowOverride All,但部分定制镜像可能没启用 —— 不确定时优先用 MU 插件方案。

  • • path 形式的 RewriteRule 在 .htaccess 里容易被 WordPress 标准 RewriteRule . /index.php [L] 抢先匹配而失效(实测命中此问题)。建议把 wp2shell 拦截规则放在 # BEGIN WordPress 块之前,或干脆只依赖 query 形式拦截 + MU 插件。

# 放在 # BEGIN WordPress 之前,确保优先匹配
RewriteEngineOn
# Query 形式(PoC 实测真实流量,实测可拦)
RewriteCond%{QUERY_STRING} (^|&)rest_route=(%2F|/)batch(%2F|/)v1/?(&|$) [NC]
RewriteRule ^ - [F,L]
# Path 形式(注意:必须放在 WP 标准 index.php 重写之前才生效)
RewriteCond%{REQUEST_URI} ^/(?:index\.php/)?wp-json/batch/v1/?$ [NC]
RewriteRule ^ - [F,L]

5.4 三层防御实测对比

测试场景

Nginx

MU 插件

.htaccess

PoC check 结果

三层全启用

HTTP 403,被 Nginx 拦

仅禁用 Nginx(保留反代)

HTTP 403,被 MU 插件拦

禁用 Nginx + MU 插件

✅(query 形式)

HTTP 403,被 .htaccess 拦 query 形式

实测细节

  • • Nginx 和 MU 插件两层对 path 和 query 两种形式都完全有效

  • • .htaccess 层实测对 query 形式有效,对 path 形式(/wp-json/batch/v1容易被 WP 标准重写规则抢先而失效,所以建议把 .htaccess 作为第三道兜底,不要单独依赖

  • • 生产建议:Nginx(边缘)+ MU 插件(应用)两层叠加即可,无需依赖 .htaccess

结论:Nginx 和 MU 插件任一层独立即可阻断整条攻击链;.htaccess 不可靠,仅作兜底。

六、发现失陷后的处置

如果第 4、5 步发现 IOC,不能只升级 WordPress

  1. 1. 隔离站点 + 取证:切换维护页或断公网,保留磁盘、容器、数据库、日志快照;

  2. 2. 从可信镜像/干净安装包重建核心、插件、主题,不要只删可疑文件(无法保证后门清理干净);

  3. 3. 重置所有管理员密码,撤销可疑的 Application Password;

  4. 4. 更新 wp-config.php 的 Authentication Keys and Salts(使所有现有登录会话失效):

    wp config shuffle-salts          # 注意是 shuffle-salts(带 s),不是 shuffle-salt
    # Docker 部署:因 wp-config.php 用 getenv_docker() 包装,还需改容器的 SALT 环境变量

  5. 5. 轮换关联凭据:数据库、SMTP、CDN、对象存储、第三方 API key;

  6. 6. 检查持久化:计划任务、MU 插件、额外主题/插件、系统 cron、Web 服务账号的 SSH 密钥;

  7. 7. 评估通报义务:根据日志确认最早攻击时间,评估数据库/用户数据是否泄露,按本地合规要求履行通报。

参考

跳转微信打开