10 月 22 日上午 9 点 25 分,Cloudflare CEO Matthew Prince 在 X 上发了一组数字,看得我头皮发麻:
关于 #php2shell 的一些统计数据。在它被公开宣布前的 10 小时内,在 @Cloudflare 网络上我们看到了数千次利用它的尝试。宣布后 45 分钟内,这一数字激增至数万次,2 小时内达到数十万次,现在已达数百万次。
数小时前还是”理论上的严重漏洞”,数小时后就是数百万人次的实弹射击。这不是一次普通的安全更新,这是 PHP 诞生三十多年来最接近”全球性事故”的一次。
如果你手上管着任何一台跑 PHP 的服务器——WordPress、Typecho、Discuz、Laravel、宝塔面板、群晖里的 Web Station——请先停下手上的事,看完第一节,然后去升级。
先把结论放在前面
- 漏洞名:PHP2Shell,两个 CVE:
CVE-2026-109618、CVE-2026-109619 - 评级:CVSS 9.2(满分 10),无需账号、无需登录,一个 HTTP 请求就能触发
- 影响:PHP 5.4 到 8.5 的所有版本,覆盖全球约 70% 的网站
- 修复版本:PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35(2026 年 10 月 21 日 14:00 UTC 发布)
- 现状:PoC 已公开,全网扫描器在跑,Cloudflare 侧的攻击量已到百万级;NSA、FBI、CISA 同时发警告,德国、法国、荷兰、比利时、芬兰、捷克、日本都有政府站点临时下线
一句话:这不是”有空再修”的漏洞,是”现在不修就可能已经晚了”的漏洞。
一个请求就能拿到 shell,问题出在 PHP 自己身上
PHP2Shell 这个名字很直白:PHP to shell。
两个漏洞都出在 PHP 内核处理 HTTP 请求的内存管理上,跟你的代码写得好不好、插件是不是最新版,没有关系。PHP 官方说得很清楚:
- CVE-2026-109618(内存耗尽型破坏):服务器内存吃紧时,PHP 会中断当前请求,但内部数据结构只初始化了一半。请求清理阶段会把这堆”半成品”当成合法数据来用,攻击者可以顺着这条路径劫持执行流。PHP 官方特别点名:mod_php 模式下风险最高,因为未认证的请求可以在任何应用代码执行之前就触发攻击。
- CVE-2026-109619(跨请求的过期全局状态):一个致命错误打断请求后,PHP 仍然持有已经被释放的内存的引用。下一个落到同一个 worker 上的请求,会在正常初始化重置它之前用到这份”僵尸状态”,形成 use-after-free——可以读内存,也可以执行代码。
第二个尤其阴险。它意味着同一个 PHP 进程处理的上一个用户的请求,可能把数据漏给下一个用户。泄露的东西包括数据库密码、Session Token、API Key、TLS 证书私钥。Wiz 的演示里,攻击者甚至不需要拿到 RCE,光靠读内存就能把敏感信息捞走——这种攻击比 RCE 更难被”检测恶意进程”的工具发现。
Wiz 说这两个漏洞”是 PHP 历史上最严重的漏洞之一,也堪称软件史上最严重的漏洞之一”。它们的根子可以追溯到几十年前,但一直被认为”理论上存在、实际上不可利用”,直到 AI 把这条利用链拼了出来。
时间线:从”预告”到”破禁”,只用了一天
这件事最值得记录的不是漏洞本身,而是它崩坏的披露过程。
- 8 月 11 日:Calif(参与 OpenAI “Patch the Planet” 项目)报告第一个漏洞
- 8 月 24 日:Wiz 报告第二个漏洞。三家机构都承认,AI 模型在找洞过程中做了大量工作
- 10 月 20 日 15:00 UTC:两个 CVE 编号开始在论坛和社交媒体上流传,还带着错误的发布时间和评分
- 10 月 20 日:PHP 官方被迫提前一天发预警,把原定周四的发布改到周三;同一天,Calif “误发”了一段 PoC 演示视频
- 10 月 21 日 03:02 UTC:Cloudflare 的 WAF 规则上线,免费版和付费版默认开启。这比官方披露早了大约 9 小时
- 10 月 21 日 07:41 UTC:Chainguard 提前发布含修复的镜像,比协调披露时间早 6 个多小时
- 10 月 21 日 14:00 UTC:PHP 正式发布 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
- 10 月 22 日:多国政府站点下线,NSA/FBI/CISA 联合警告
PHP 基金会在事后博客里直接点名 Chainguard”单方面撕毁禁运”——所有分发商都提前拿到了补丁,Chainguard 抢先构建了含修复的镜像,等于让攻击者可以对着二进制做 diff,在其他人还在等官方发布的时候就把洞看明白了。Chainguard CEO Dan Lorenc 的回应是”我们没泄露”,理由是禁运规则没写清楚,而且他们自己也用 AI 独立发现了同样的漏洞。
这场口水战谁对谁错,留给律师。对运维来说只有一句话:你的修复窗口,被别人的抢跑压缩到了几个小时。
攻击量为什么涨得这么疯
Prince 给的那条曲线,本质上是一条”武器化速度”曲线:
| 时间点 | Cloudflare 观测到的利用次数 |
|---|---|
| 公开宣布前 10 小时 | 数千次 |
| 宣布后 45 分钟 | 数万次 |
| 宣布后 2 小时 | 数十万次 |
| 现在 | 数百万次 |
三个推手:
- PoC 是现成的。Calif 误发的视频、随后流传的利用代码,让脚本小子的门槛降到”复制粘贴”。
- 目标太好找。Shodan、FOFA 上一筛就是几百万个暴露的 PHP 服务,Unit 42 统计有 2260 万个 互联网暴露实例可能受影响。
- AI 在攻防两端同时加速。找洞用 AI,写变种 payload 也用 AI。Cloudflare 说他们观察到攻击者在根据 WAF 规则反复变形 payload,试图绕过检测。
所以这次不是”某个插件爆雷,等作者更新”的节奏,而是”全网的扫描器已经把你的域名放进队列”的节奏。
现在该做什么:按顺序来
第一步:查你到底是什么版本
别信面板首页那行字,要查真正跑在 FPM 里的二进制:
# CLI 版本(不一定等于网站用的版本)
php -v
php-fpm -v
# 找到站点实际使用的 socket / 进程
ps aux | grep php-fpm
ls -l /proc/$(pgrep -f 'php-fpm: master' | head -1)/exe
WordPress 用户可以在站点根目录临时放一个 info.php:
<?php phpinfo(); ?>
访问后看 PHP Version,看完立刻删掉。更稳妥的方式是看面板里的 PHP 版本,或者用 wp --info(WP-CLI)。
对比一下:
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| 8.5 | < 8.5.12 | 8.5.12 |
| 8.4 | < 8.4.27 | 8.4.27 |
| 8.3 | < 8.3.36 | 8.3.36 |
| 8.2 | < 8.2.35 | 8.2.35 |
| 8.1 及更早 | 全部 | 官方已停止支持,仅有源码级 backport |
第二步:升级,然后重启
# Debian / Ubuntu(Ondrej PPA 或发行版源)
sudo apt update
sudo apt install --only-upgrade 'php8.4-*'
sudo systemctl restart php8.4-fpm
# RHEL / Alma / Rocky
sudo dnf update 'php*'
# Docker:拉新镜像重建,别只 restart
docker pull php:8.4-fpm
docker compose up -d --force-recreate php
几个坑:
- 只
apt upgrade不重启 FPM,等于没修。老进程还在内存里跑着旧代码。 - CLI 升级了不代表 FPM 升级了。很多机器 CLI 是 8.4、网站跑的是 8.2,两个都要看。
- remi 源这次没有当天更新。PHP 官方专门提醒用
rpms.remirepo.net的人切换到发行版包或自己编译。宝塔编译安装的 PHP 要等面板推新版本,如果面板还没跟上,临时方案是切到系统源里的新版 PHP,或者先做下一节的缓解。 - WordPress 后台的自动更新修不了这个洞。它更新的是 WP 核心和插件,不是 PHP 运行时。托管主机用户请直接开工单,把这条 CVE 编号甩给客服。
第三步:补不上?先按官方清单收紧
PHP 官方说得很直白:”没有任何完整的缓解措施,升级是唯一安全的选择。”下面这些只能减少特定攻击路径的成功率,不能让你变安全:
; php.ini 或 FPM pool 配置(php_admin_value)
memory_limit = -1
post_max_size = 32M
session.upload_progress.enabled = 0
display_errors = 0
fatal_error_backtraces = 0
注意两点:
memory_limit = -1是把内存耗尽的风险转交给操作系统,必须配合 cgroup v2 的memory.max做兜底,否则一个请求就能把整台机器拖垮。不要用RLIMIT_AS/RLIMIT_DATA代替,那会触发同一条脆弱路径。session.upload_progress.enabled = 0必须写在 FPM pool 或 php.ini 里,ini_set()和.user.ini不生效——它们在请求解析之后才执行。
改完一样要重启 worker。不重启,配置只是躺在文件里。
第四步:当作”可能已经被打过”来排查
这一点很多人会跳过。因为这个漏洞能读进程内存,即使没有 RCE,凭据也可能已经泄露。
# 1. 一周内被改过的 PHP 文件
find /var/www -name '*.php' -mtime -7 -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort -r | head -50
# 2. 可疑的大 POST / multipart 请求
grep -E 'POST .*(multipart|upload_progress)' /var/log/nginx/access.log | tail -50
# 3. WordPress 完整性
wp core verify-checksums
wp plugin list --status=active
wp user list --role=administrator
然后换掉所有能换的密钥:数据库密码、wp-config.php 里的 salts、对象存储 AK/SK、支付和短信接口的 Key、SSH 密钥。如果服务器上还跑着别的站点,一并按”已失陷”处理。
最后检查 /tmp、/dev/shm、wp-content/uploads 里有没有陌生的可执行文件,以及 crontab -l 和 /etc/cron.d 里有没有陌生任务。
几个必须纠正的误解
“我装了 Wordfence / 云 WAF,没事。” WAF 是创可贴,不是手术。Cloudflare 自己都说,请客户”在打补丁的同时保持 WAF 开启”。而且这次攻击在披露前就已经开始,WAF 规则是抢在披露前 9 小时上线的——你能被保护,是运气好,不是理所当然。
“我用的是最新版 WordPress。” WordPress 版本和 PHP 版本是两回事。WP 再新,底下的 PHP 是 8.1,照样中招。
“PHP 7.4 早就停止支持了,应该没人打。” 恰恰相反,老版本没有官方补丁,是这次最危险的一批。PHP 官方给了 7.4/8.0/8.1 的最小源码 backport,但需要你自己编译;发行版和 LTS 厂商也可能有回移补丁,值得先查一下。
“我的网站小,没人会盯上。” 这次的攻击是全网无差别扫描,不是定向打击。攻击者要的是”能用的服务器”,不是”有价值的服务器”——你的机器可以拿去发垃圾邮件、当代理、挖矿。
写在最后
过去我们习惯了”漏洞公布→等厂商→下周打补丁”的节奏。PHP2Shell 把这个节奏压到了小时级:漏洞细节提前泄露,补丁镜像提前发布,PoC 视频误发,AI 让写利用代码的成本趋近于零。
这次 AI 帮防守方抢到了几个小时的先手——Wiz 和 OpenAI 用它找洞,Cloudflare 用它写规则,PHP 基金会用它验证修复。但同一个工具在攻击者手里,效率是一样的。
对个人站长来说,能做的其实很朴素:把 PHP 版本管理当成日常,而不是事故。 订阅 php.net 的安全公告,给服务器开自动安全更新,别把 PHP 8.1 用到 2028 年。
毕竟下一次,未必还有 9 小时的提前量。
参考来源:PHP 官方安全公告(2026-10-21)、Cloudflare 博客《Cloudflare deploys protections against php2shell vulnerability》、Wiz Research、The Hacker News、BleepingComputer、The Register、Al Jazeera、Unit 42、CISA 公告。











发表回复