PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招

简介: PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序

大家好,我是程序员天天困。

先做个三十秒的自查:打开终端,敲一句 php -v。

如果版本号停在 8.2.35、8.3.36、8.4.27、8.5.12 之前,你手上的站就在 PHP2Shell 的射程里。这不是「可能会被扫到」——补丁还没正式发布,已经有人在打了。

下面把两个 CVE 怎么坏的、为什么十几年没人修得动、nginx 为什么被误伤,以及今晚先动哪一步,一次讲清。

一、10 月 21 日:一场被泄漏逼着提前的发布

这次发布最反常的不是漏洞本身,而是整条时间线被人从中间拽断了。

10 月 20 日 15:00 UTC,两个 CVE 编号开始在论坛和社交平台上流传,还带了一个错误的评分:10.0。实际评分 9.2,发布还提前了一天。

PHP 团队的反应是先稳住节奏:19:30 UTC 发出预告,把发布日从 22 日 14:00 挪到 21 日 14:00。原因很实际——分销商还在打包,立刻发只会让一大批人没有简单的升级路径。

真正的转折在 20:00 UTC 左右。Calif 误发了一段 PoC 视频,删得很快,副本已经传开。

21 日,荷兰的 DigiD、MijnOverheid、议会网站和 Tweakers 直接下线,日本也有几家大服务做了紧急维护。补丁还没发,先把系统停了——这个动作本身比任何 CVSS 分数都说明问题。

07:34 UTC,多家主机商向 Patchstack 报告了正在发生的利用。不到十分钟后,Chainguard 单方面解除禁运,放出含补丁的镜像。又过两分钟,Cloudflare 的 CTO 说看到了早期攻击。

PHP Foundation 复盘:披露被打断,Chainguard 提前六小时发布含补丁镜像

时间(UTC) 发生了什么
10-20 15:00 CVE 编号外流,附带错误的 10.0 分
10-20 19:30 PHP 提前发预告,发布日从 22 日改到 21 日
10-20 20:00 Calif 误发 PoC 视频,副本扩散
10-21 07:34 主机商报告正在发生的利用
10-21 07:41 Chainguard 抢先发布含补丁镜像
10-21 13:30–14:00 四个版本发布
10-21 起 大规模扫描与在野利用

官方确认受影响的运行时范围与四个修复版本

13:30 到 14:00 之间,四个版本上线。Sansec 的预警更直接:正式发布后 15 分钟内,大规模扫描就会开始。当天 GitHub 上就出现了 PoC,其中一份还带了 nginx + php-fpm 8.2.34 的 Docker 靶场。

我印象最深的不是这个速度,而是它说明的事:PoC 视频一泄露,保密窗口就开始收窄;等补丁镜像上线,连 diff 都不用猜了。

二、病根不在 index.php,在引擎收尾的那几毫秒

PHP2Shell 最阴的地方,是把「请求失败」变成了攻击面。

先认识三个词,后面就好懂了。

PHP2Shell:Wiz 给这套组合利用起的名字,指的是攻击者无需认证就能在服务器上执行命令。你可以把它理解成大楼地基里的一条裂缝——上面盖的是 WordPress 还是自研后台,都不影响裂缝存在。

UAF(Use-After-Free,释放后使用):一块内存已经被释放,程序却还留着指针继续用它。类比是租客退房没交钥匙,下一位住进来,你照样能翻他的东西。

OPcache:PHP 把编译后的脚本结构缓存进共享内存的机制,多个 worker 共用。它像小区的公共仓库,谁都能往里放东西,也谁都能拿错。

具体怎么坏的?我尽量说人话。

PHP 处理 multipart 请求时,会在「请求启动」阶段就解析 RFC1867,这早于常规模块初始化。session 的 upload_progress 逻辑会在这个很早的窗口里初始化 session 状态。CVE-2026-109618 的链条是:解码器把请求级的记账数据存在 BG(unserialize),此时一次内存分配失败触发 fatal,请求堆被销毁。但反序列化器的全局状态并没有跟着失效,下一个由同一 worker 处理的请求,就可能把攻击者重新占用的那块内存,当成活的记账结构来用。

一句话:收工收到一半被叫停,工具没归位,第二天新班组进来顺手就拿错了。

光能破坏还不够,还得知道往哪儿写。Wiz 另找了一个内存泄露原语:同样的早窗口里,PS(id) 可能指向已释放的内存;下一个 multipart 请求在上传进度清理时,会把那块内存当 zend_string,URL 编码后塞进响应的 PHPSESSID Cookie。应用从没调用过 session_start() 也照样泄。这一步把 ASLR 带来的地址不确定性抹掉了。

CVE-2026-109619 换了两条路。

第一条是 fatal error backtrace。EG(last_fatal_error_backtrace) 保存着最近一次 fatal 的调用栈,OOM 销毁请求堆时却没清掉它。同一 worker 的下一个请求只要在早期输出一条诊断,就会踩到这个悬空指针。

第二条是 OPcache 类型混淆。zend_accel_script_persist() 先推进进程内的 CG(map_ptr_last),持久化完成后才发布共享 frontier。中间一旦分配失败,本地推进了、共享状态还以为槽位空着;别的 worker 复用时拿到错误类型的指针,共享 OPcache 就被污染了。

这两条都跨请求、跨 worker,重启单个进程都不一定干净。

两个 CVE 背后是四个具体的内存安全 bug,且演示目标覆盖 WordPress 与 Drupal

三、为什么这个洞能躺十几年

漏洞不是没人看见,是看见了也修不动。

PHP Foundation 在发布当天的复盘里写得很坦白:2024 年 PHP 做了多年来第一次外部安全审计,由 OSTIF 组织、Quarkslab 执行、Sovereign Tech Agency 出钱。就在修审计报告里另一个问题时,一位受基金会资助的核心开发者第一次看到了这个 OOM 问题的苗头。

当年就有人推测这个 OOM 问题可能被利用,但没人拿出具体路径,加上手头有更明确的漏洞要修,这件事就压下去了。

后来想修,发现每条路都疼:改内存管理可能破坏向后兼容;换一种处理方式会引入新的 DoS 面;还有的方案要求大量第三方扩展跟着改,根本协调不动。安全团队是共识制,几个方案各有代价,谁也说服不了谁,修一阵停一阵,反复了几个月。

基金会自己给这个过程的定性是:这是一次结构性失败,既有决策流程的问题,也有人手不够的问题。

真正的推动来自 2026 年 8 月 11 日,Calif 的研究员 Ryan A 报了一个同根因的无条件 RCE。同一天 OpenAI 独立报了一遍,Wiz 在 8 月 24 日也报了,9 月 18 日 OpenAI 又补了新向量。从 8 月起,这件事才变成整个团队的最高优先级,有人连着周末和通宵在协调、验证、写补丁。

我看完这段的感受挺复杂。我们平时骂厂商「有洞不修」,但真实情况往往是一群人在兼容性、性能和安全性之间反复拉扯,最后被一个外部报告逼到必须选一条路。开源基础设施的安全,很多时候不是技术问题,是排期和人力问题。

四、为什么改 WordPress、加 WAF 规则都救不了

所有「在应用层堵」的方案,天生慢了一步。

这是我最想强调的一点:漏洞代码在 PHP 引擎里,不需要你的路由、控制器、中间件点头。mod_php 下,一个无认证请求在应用代码运行前就能触发,默认配置已经演示了完整 RCE;PHP-FPM 下,确认的链条要走应用层代码路径——比如 session_start() 这种默认就会调用的地方。门槛不同,但都不需要账号。

Drupal 用户别觉得离自己远。Wiz 的演示目标里就有 drupal:latest,而且 Drupal 官方在 20 日就挂出了 PSA-2026-10-20,标题写得很直白:Critical PHP update。这是 PHP 的更新,不是 Drupal 的安全版本。Drupal 的核心更新也修不了它,因为漏洞不在 Drupal 代码里。

WordPress 同理。Automattic 参与了协调,但能做的只是发指引;主题、插件、核心怎么升都碰不到 PHP 运行时。Joomla 也在演示名单里。这三个只是靶子,换成 Laravel、ThinkPHP 还是祖传单文件后台,结论一样。

比 RCE 更隐蔽的是那个内存泄露。Wiz 演示了不执行代码、只读内存:别的请求留下的密码、登录 token、API key、证书,都可能被 URL 编码后塞进你自己的 Cookie 里。在共享主机上,同一个 worker 池里的其他站点也可能被波及——这是这类漏洞最难受的地方,你甚至不知道自己已经被读过一遍。

可能有人会问:我用的是 WordPress,等官方推送更新不就行了?

不行。PHP2Shell 在 PHP 运行时里,不在 WordPress 代码里,核心、主题、插件怎么升都碰不到它。你要么自己升服务器 PHP,要么让主机商升;用共享主机的,今晚就该去问一句「你们的 PHP 到 8.2.35 了吗」。

还有一类「缓解措施」值得单独说:网上传得很广的 session.upload_progress.enabled=0。它只封 109619 的已确认路径,对 109618 没有作用。Datadog 的说法很直接:社区流传的这类缓解「保护不了你」。把它当护身符,比不设还危险,因为你以为自己安全了。

WAF 也一样。Cloudflare 那批规则挡的是已经见过的利用形状,换个编码、改改分块方式就可能绕过去。它能帮你争取时间,但挡不住变体,更不能替代升级。

网上已经出现号称能「一键修复 PHP2Shell」的插件和脚本,别装。这种来路不明的东西没有验证,轻则白装,重则给你留个后门。

五、nginx 到底冤不冤

nginx 在这件事里既冤枉,又不冤枉。

先说冤枉的部分:PHP2Shell 没有碰 nginx 一行代码。你把 nginx 换成 Apache、Caddy,PHP 该中还是中。nginx 在这里只是门卫,它把 multipart 请求原样转给 php-fpm,真正出事的是门后面的运行时。那些「PHP 漏洞影响 nginx」的标题,因果关系是反的。

但「不冤枉」的部分更值得看:同一周,nginx 自己也有三个 CVSS 9.2。这不是让它替 PHP2Shell 背锅,而是这周它自己也有一批 9.2 要修。

10 月 14 日,nginx 1.30.6 和 1.31.7 发布,修了三个内存安全类的高危问题:alias 配合 rewrite 时的堆溢出(CVE-2026-15176)、map 指令求值字符串表达式时的堆溢出(CVE-2026-93479)、SSI 模块配合 AIO 的 UAF(CVE-2026-97205)。

新加坡 CSA 在 10 月 21 日专门发了告警,写明这三个都是 9.2,影响 nginx 开源版 1.30.5 及以下、1.31.6 及以下,还有 NGINX Plus R33–R37、Ingress Controller 5.6.0–5.6.3 等一整串产品。远程代码执行的条件是 ASLR 关闭或可绕过,但打崩 worker 是稳定的。

nginx 的三个 9.2 是独立于 PHP2Shell 的另一批漏洞,且修复早了一周

所以真实场景是这样的:你的入口是 nginx,后面挂 php-fpm。门有门的洞,屋里有屋的洞,两件事挤在同一周。只补 PHP 不补 nginx,等于换了门锁没修窗;只补 nginx 不补 PHP,门修好了,屋里的洞还在。

顺便说一句,PHP2Shell 的公开 PoC 靶场就是 nginx + php-fpm 8.2.34。这个组合太常见了,常见到很多人以为「nginx 在前面就安全一点」。并没有。

六、同一天还有两个「爆点」,但性质完全不同

10 月 21 日是个补丁日,但不是每一条都叫 RCE。

Node.js 这边,CVE-2026-111497 是 Brotli 解码器的越界写,官方定性为可导致远程代码执行,评级 high。修复版本是 22.23.4、24.22.1、26.12.1。同一个安全版本里还带了 c-ares、OpenSSL、Undici 等依赖更新,以及几个 TLS 主机名校验问题。跑 Node 服务的,node -v 看一眼,22、24、26 三条线都在射程内。

Node.js 安全发布:Brotli 解码器越界写 CVE-2026-111497,评级 high

Go 这边,CVE-2026-111943 被不少标题写成了「Go 也爆 RCE」。并不是。它是 crypto/x509 在 Certificate.Verify 时没有正确拦住已知被攻陷的中间证书,CWE-299,Red Hat 给的 CVSS 3.1 是 6.8。后果是中间人攻击:攻击者拿这些中间 CA 签的证书冒充正常服务器。影响版本是 1.26.10 之前和 1.27.3 之前。

性质差多远?用个比喻:PHP 那个是墙被凿穿了,Go 这个是门锁该换没换。都危险,但防守动作完全不同。前者要连夜升级运行时,后者要检查信任链和证书固定策略,顺带把 Verify 的错误处理翻一遍。

我把这三条放一张表里,方便你判断优先级:

编号 是什么 最坏后果 严重度
CVE-2026-109618 / 109619 PHP 引擎内存破坏 无认证 RCE 9.2(v4.0)
CVE-2026-111497 Node Brotli 解码越界写 RCE high
CVE-2026-111943 Go x509 证书链校验缺失 中间人假冒 6.8(v3.1)

七、今晚就能做的四件事

缓解措施只能减伤,不能免疫,这句话 PHP 官方自己写在了公告里。

第一,先确认版本,再谈别的。 目标是这四个之一:

分支 修复版本 备注
8.5 8.5.12 当前主线
8.4 8.4.27
8.3 8.3.36
8.2 8.2.35 2026-12-31 EOL,升完就该规划下一站

PHP 8.1 及更早官方不再发版,但这次给了 7.4、8.0、8.1 的最小源码 backport 分支,发行版和 LTS 厂商也可能有回填包,去问 vendor,别自己猜。另外,rpms.remirepo.net 这个志愿者仓库当天明确说不会有更新,用它的人得换源或自己编。Docker 用户升完记得重建镜像,别只改 tag 不重拉,本地缓存可能还停在旧 digest。

第二,memory_limit 和 post_max_size 要会设。 官方的临时缓解是针对 109618 的:把 memory_limit 设成 -1,最好走 FPM pool 或 Apache 的 php_admin_value,防止应用自己改回去。注意这只是拆掉可控的内存上限,底层的分配失败仍然可能走到那个路径。

同时把 post_max_size 压小:不需要上传的站给 8M,需要上传的给 32M,upload_max_filesize 保持在它下面。有条件的把容器或 cgroup 当可用性边界,别用 RLIMIT_AS、RLIMIT_DATA 去「限内存」——那恰好会触发那个致命的分配失败。

第三,别把 session.upload_progress.enabled=0 当万能药。 它必须在 php.ini、FPM pool 或服务器配置里生效、然后重启所有 worker;ini_set() 和 .user.ini 不算。而且它只盖住 109619 的一部分路径,109618 该中还是中。

第四,查日志,然后想清楚要不要换密钥。 成功利用不一定崩进程,别等 crash。值得看的信号:异常大的表单提交、请求初始化阶段的内存耗尽报错、反复出现的 500、PHPSESSID 响应 Cookie 异常变大或格式不对、PHP worker 莫名重启、以及 Web 进程突然拉起 shell。

如果这些信号出现过,或者你的机器在 21 日到打补丁之间一直暴露着,那光打补丁不够——假设数据库密码、API key、session 密钥已经被读过,该轮换的轮换。英国 NCSC 的建议更狠:修不了的系统,考虑先下线。

Cloudflare 已经给所有套餐(含免费)上了针对已知利用向量的 WAF 规则,有 CDN 的可以先开着。但它有个前提:流量得全部经过 Cloudflare。源站 IP 一旦裸奔,规则就是摆设。真要保命,还是得把版本升上去。

八、我的判断

说实话,这次最让我后背发凉的不是 9.2,而是 Wiz 那句话:这些缺陷在代码里躺了十几年,把「不可利用」变成「可利用」的,是前沿模型。

过去评估风险有个隐含前提:利用复杂度本身就是一道防线。一个需要精确控制内存布局、要反复调试、要对每个目标单独适配的漏洞,只有顶级团队玩得起,所以「暂缓修复」是可以接受的。PHP2Shell 把这条前提拆了。Wiz 说一个研究员一周干完了过去一个团队几个月的事,演示的还是 WordPress、Drupal、Joomla 三个不同目标。

对普通开发者的含义很具体:「这个洞太难利用了」不再是一个能写进风险评估的理由。

还有一个数字也该记住。CybelAngel 扫到 224 万台暴露在公网的 PHP 主机,其中 59% 跑在「不会再有补丁」的版本上。

这些人不是不知道,是动不了——老系统、老 CMS、主机商不升级、没人敢碰。这个长尾会拖很久,久到下一次类似的事爆发时,它们还在。

所以我的建议很土:今晚先看版本号,能升就升;升不了的,按官方那几条减伤措施做,然后把迁移排进日程。PHP2Shell 不会是最后一次,别拖到下一个 10 月 21 日。


我是程序员天天困,持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊:你手上的 PHP 项目现在卡在哪个版本,是升不上去还是不敢升?

相关文章
|
8天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1320 6
|
6天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
1128 21
|
2天前
|
存储 人工智能 自然语言处理
阿里云千问办公官网登录入口:qwenwork.cn网页版免下载,注册送2000积分、登录每日再送100积分
千问办公(QwenWork)是阿里巴巴推出的一站式AI办公平台,定位"不止于对话,更注重交付"。它基于千问大模型,具备任务拆解与多步骤自动执行能力,用户一句话即可完成PPT生成、数据分析、网页搭建、视频剪辑、信息调研等复杂任务,覆盖网页端、桌面端及钉钉生态。网页版免下载,访问qwenwork.cn注册即得2000积分,每日登录再送100积分。个人版免费起用,另有付费版与企业版可选。
|
11天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
6天前
|
人工智能 开发者
Qoder CN 大使计划正式开启|无限创造,等你一起
全年免费积分、优先内测、活动支持与更多共创机会,邀请用 Qoder 把想法变成现实的你。
567 6
|
1月前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
10037 33
|
7天前
|
安全 Linux 芯片
【万字长文】Ollama下载、安装、配置、使用保姆级教程(2026最新图解)
Ollama 是一款免费开源的本地大模型运行工具,支持 DeepSeek、Qwen 等主流模型,一条命令即可下载运行。2026 最新版 v0.35.1 官方安装包(1.47GB)已提供国内直连下载,纯本地、离线可用、无广告、无隐私泄露风险。
685 1
|
17天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
8天前
|
Web App开发 人工智能 JavaScript
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
REA(Reverse Engineer Anything)是近期爆火的开源AI逆向工具,4月发布,1个月内斩获15万+ Star。它让AI Agent无需源码即可分析二进制、APK、JS/Electron、HAR包等17类目标,自动反编译、生成可运行代码,并强制返回带证据与局限的结论。全本地执行,MIT协议,支持Hopper/Ghidra/IDA等引擎,一行命令即可上手。
794 2
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA