PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相

本文转载自微信公众号「表哥带我」

原文标题:PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相

内容版权归原作者及原公众号所有,转载仅为传播资讯;若有侵权请联系管理员删除。

摘要

PHP2Shell(CVE‑2026‑109618、CVE‑2026‑109619)属于临界高危漏洞;当资产为公网暴露 Apache mod_php 运行环境时必须高度重视,优先补丁修复;对于 PHP‑FPM 环境,并非完全无风险,但原生环境下完整稳定 RCE 利用条件苛刻,不存在 “打过去就直接拿 shell” 的一键危害;无论哪种 SAPI,都不能直接判定绝对安全,需要结合版本、配置、SAPI 类型综合研判。

2026 年 10 月 21 日,PHP 发了四个安全版本:8.5.12、8.4.27、8.3.36、8.2.35。

图片[1]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 1:PHP 官方归档中的 2026 年 10 月 21 日公告。来源:PHP News Archive,截图时间 2026-10-22 10:28 CST。

修的是两个未认证远程代码执行漏洞。PHP 安全团队给它们的命名分别是:

  • CVE-2026-109618:RCE via OOM corruption

  • CVE-2026-109619:RCE via stale global state

社区后来把它们合称为 PHP2SHELL。两个漏洞的官方 CVSS v4.0 向量一样:

CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

基础评分 9.2,Critical,可远程,无需授权,造成RCE,但利用复杂度高,还需要额外的攻击前置条件。对下游关联系统,官方没有确认直接影响。

一时间舆论甚嚣尘上,但它既非真正的“核弹”,也非无用的“玩具”——这两个洞的实际判断从两个角度来说:①一旦打中,后果有多重;②打中的难度有多大。




版本和影响面



漏洞与修复版本

图片[2]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

PHP 8.1 及更早分支已经 EOL,所以官方 advisory 的修复版本表只覆盖上面四个受支持分支。

PS: Debian、Ubuntu、RHEL、AlmaLinux、Rocky Linux 以及各类 LTS 厂商经常保留上游版本号,只回填安全补丁。现场得看包 changelog、厂商公告或构建时间。PHP 8.3.x 不能直接判安全,旧版本号也不能直接判有问题。

哪些 PHP 运行时暴露

PHP 官方和 CERT-EU 的口径都很直接:所有对外提供 Web 服务的 PHP 运行时,都该纳入排查。mod_php、PHP-FPM、php-cgi、FrankenPHP、LiteSpeed 这些 SAPI 都在范围内。

风险最高的是 Apache mod_php。php_request_startup() 失败以后,mod_php 仍可能进入 request shutdown。

FPM 得分开看。请求启动失败时,FPM 通常会终止异常 child,所以官方没有给出和 mod_php 完全相同的、应用无关的启动阶段利用链。源码分析仍把 FPM 算在受影响面里:应用执行阶段触发的 OOM,仍可能走到错误清理路径。

109619 的 session upload progress 链已经在 FPM 上验证过。它通常要求前一个请求在正常启动后执行到某个合适的应用操作,再触发 fatal error。这个条件比 mod_php 的启动阶段链多一步,不能拿一句“FPM 已验证”省略。

受影响的也不只是网站。网关、NAS、防火墙管理口、虚拟化管理面板、打印设备、工控 appliance,以及把 PHP 打进镜像的商用产品,只要运行时旧,都该按“可能未认证 RCE”处理。

9.2 和 9.8 怎么来的

PHP 安全团队给的是 CVSS v4.0 9.2。NVD 同时给了自己的 CVSS 3.1:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

NVD 3.1 评分是 9.8。两个分数的评价模型不同,复杂度判断也不同。它们描述的还是同一组漏洞。内部工单可以优先采用 PHP 官方 v4.0 向量,风险说明里保留 NVD 3.1 分数,免得业务方看到 9.2 就把它当成普通高危。

图片[3]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 2:CVE-2026-109618 的 NVD API 返回。CVSS v4.0 为 9.2,CVSS v3.1 为 9.8。来源:NVD REST API。NVD 详情页在当前网络下无法稳定渲染,图中数据按官方 API 原响应本地格式化。

图片[4]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 3:CVE-2026-109619 的 NVD API 返回。CVSS v4.0 为 9.2,CVSS v3.1 为 9.8。来源:NVD REST API。

把 v4.0 向量拆开看,扣分项其实就那几个:

图片[5]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

AC:H 和 AT:P 不能略过。网上出现完整 RCE 利用,不等于每个请求都能成功。worker 调度、堆布局、PHP 构建、扩展、SAPI、配置和前端限制,都会改变成功率。




两个漏洞到底坏在哪



CVE-2026-109618:OOM 会直接跳出去

PHP 旧路径处理内存耗尽时,会调用 longjmp() 一类 bailout 机制,从 allocator 的错误现场直接跳回请求边界。

正常异常退栈会逐层执行清理逻辑。longjmp() 不会。

图片[6]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 4:CVE-2026-109618 官方 advisory 页面,标题为 RCE via OOM corruption。来源:GHSA-w8hw-w9fx-j79w。

请求启动阶段很敏感。PHP 正在解析 multipart/form-data,$_POST、$_FILES、数组键、文件元数据和 session upload progress 等结构正在逐步建立。此时某块 request memory 可能已经挂到内部结构中,但还没有完成初始化。

如果这时发生一次 OOM,即使失败的那次分配和这块半初始化内存没有直接关系,执行流也会被提前拽走。状态链大概是这样:

multipart 解析
-> request-owned 结构已发布但未完全初始化
-> 任意一次分配触发 memory_limit
-> longjmp / bailout
-> 匹配的初始化或清理被跳过
-> request shutdown 继续运行
-> shutdown 把无效状态当有效数据读取或分支

在 mod_php 上,request startup 失败以后,request shutdown 仍可能继续跑。攻击者可以用 HTTP 请求控制 OOM 前的堆布局,让 shutdown 去消费伪造的 zval、HashTable 或其他内部结构。最后落到函数指针,命令也就执行了。

补丁改了什么

核心提交是:

3584a98bda0f2c0d367255b046798b0d3a221a19
Avoid state corruption via bailout

图片[7]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 5:官方补丁提交 3584a98,提交说明同时列出两个 GHSA。来源:php-src commit 3584a98。

提交说明直接写了它同时修复两个 advisory。改动集中在 OOM 退出语义,没法靠某一个 multipart 分支里的 NULL 判断解释:

  • 软 OOM 改为 delayed OOM。allocator 不再立刻 longjmp(),而是记录 pending 状态,在下一个 VM safe point 正常处理。

  • 如果内存继续增长到更高的 hard limit,则使用 _exit() 终止进程,避免再回到不确定的 shutdown 状态。

  • 请求解析中的关键分配改为 fallible 路径,允许上层停止解析并正常清理。

  • 只保留 multipart 真正需要的 Content-Disposition 和 Content-Type,重复 header 不再全部长期保留。

  • request shutdown 清理 PG(http_globals)、last error、module activation 等可能在 startup 失败后仍被访问的状态。

  • 新增加 php_basic_early_rinit(),在请求早期重新初始化此前可能被前置流程使用的序列化、反序列化和 URL scanner 状态。

改动铺得这么大,是因为它同时碰到 Zend Memory Manager、VM 中断点、request lifecycle、multipart parser 和 session 全局状态。每个位置都牵着请求生命周期的另一端。

CVE-2026-109619:上一条请求留下的指针

109619 的入口在 worker 的全局位置。那里缓存了一个 request-owned memory 的地址。请求结束后,这块内存会释放,worker 进程却还在,地址也留在原处。下一条请求只要在正常初始化覆盖它之前碰到这段状态,就可能出现 use-after-free。

图片[8]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 6:CVE-2026-109619 官方 advisory 页面,标题为 RCE via stale global state。来源:GHSA-6wmf-4rv5-p3rp。

PHP-FPM 和 Apache prefork 都会让长期存活的 worker 连续处理多个请求。一个请求结束后,request heap 会被释放,worker 进程不会退出。正常情况下,RINIT、RSHUTDOWN 和模块 lifecycle 会把全局引用逐个清空。异常路径可能断掉这条清理链。

官方把它拆成 producer 和 consumer:

请求 A(producer)
-> 把 request-owned session / error / extension state 发布到 worker 全局位置
-> fatal error、OOM、超时或 shutdown 后诊断打断匹配的 reset
-> request memory 被释放
-> worker 继续存活

请求 B(consumer)
-> 被同一个 worker 接收
-> 在正常 request 初始化覆盖全局状态之前
-> 某段早期逻辑读取、释放、分支或调用悬空指针

成功利用至少要同时满足五件事:

  1. 攻击者可达的 producer。

  2. reset 被跳过,或者 reset 之后又被 shutdown 诊断重新填回。

  3. worker 不退出,request heap 被释放。

  4. 后续请求命中同一个 worker。

  5. 正常初始化前存在 consumer。




真打起来,卡在哪



首先,这两个漏洞都是未授权的RCE。

Wiz 的披露材料把影响演示到了 WordPress、Drupal 和 Joomla 的容器镜像上。

图片[9]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 7:Wiz Research 展示的已确认影响,包括未认证内存泄露和预认证 RCE。来源:PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet。

但他的原文中提到:①攻击者可以远程利用,某些情况下“fairly easily”。②完整利用要串联多个缺陷,还得以目标为样本反复调整堆布局。

所以入口可以很容易,终点不一定容易——未认证和无需交互降低了尝试成本,堆布局、二进制适配和 worker 调度又把成功门槛抬了上去。

CVE-2026-109618

109618 的确认利用已经通过 HTTP 打到 Apache mod_php。官方写的是默认 PHP 输入限制、默认内存限制,以及正常的多 worker prefork 配置。

但,“能打到那条路径”和“每个站都能稳定拿到命令执行”是两码事。官方明确说,可移植性和可靠性取决于 PHP 构建、扩展、前端行为和 allocator 布局。所以 109618 在互联网可达的旧版 mod_php 上建议按照真实 RCE 风险处理。

而对于FPM,官方没有给出 109618 的完整利用。请求启动失败时,FPM 通常会终止 child,刚好把 mod_php 那条“startup 失败后继续 shutdown”的路截短。源码分析仍认为,应用执行阶段触发的 OOM 可能进入错误清理路径,但这和前面的 startup 链不是同一个实战入口。

FrankenPHP 在源码行为上更像 mod_php,被官方列为可能受影响;LSAPI 更像 FPM,但官方没有验证完整利用。看到 FPM 就写“安全”不对,看到 PHP 就写“都能一把打”也不对。

CVE-2026-109619

109619 则更看运气。

完整条件至少包括:

  1. 请求 A 能触发 producer,并制造 fatal error、OOM、超时或清理阶段的诊断。

  2. 该请求结束时,匹配的 reset 没有执行,或者 reset 之后又被诊断信息填回。

  3. request heap 被释放,worker 继续存活。

  4. 请求 B 命中同一个 worker。

  5. 正常初始化之前,有 consumer 读取、释放、分支或调用悬空状态。

在现实场景中,显然攻击者不能直接指定 worker,此时,请求 A 和请求 B 可能被不同 child 接走。worker 越多,尝试次数越多,但 A/B 落回同一个进程也需要调度配合。

官方确认了两条比较实用的 consumer。PHP 8.5 的 fatal_error_backtraces 开启时,可以把 fatal backtrace 留在 worker 全局状态里,下一条请求在早期产生诊断时碰到它;另一条是 session.upload_progress,它默认开启,不需要业务调用 session_start(),可以在普通 session 初始化之前进入 session 和 decoder 状态。

mod_php 可以在 multipart startup 阶段同时完成 producer 和 interruption,所以业务代码可以完全不参与。FPM 在 startup 失败时会杀掉 child;已确认的 FPM session 链要在应用正常启动后执行到合适操作,再触发 fatal error,最后由后续请求的 upload progress 消费。

图片[10]-PHP2SHELL:真正的核弹还是虚假的“核弹”——关于网传核弹级 PHP 漏洞,剥开噱头看真相-阻击者联盟

图 8:Wiz Research 对利用构造、堆布局适配和重复调试的描述。来源同上。




排查和修补方式



确认资产和补丁状态

优先级从高到低:

  1. 互联网可达的 Apache mod_php。

  2. 多租户 PHP-FPM pool、共享 session 存储、统一登录入口。

  3. 文件上传、工单附件、头像、导入功能明显的应用。

  4. 容器镜像、Helm chart、基础镜像和 CI/CD 中的 PHP runtime。

  5. 网关、NAS、管理面板、虚拟化平台和厂商 appliance。

检查命令至少包括:

php -v
php -i | egrep ‘Server API|memory_limit|post_max_size|

upload_max_filesize|max_input_vars|max_input_nesting

_level|max_file_uploads|max_multipart_body_parts|

session.upload_progress|fatal_error_backtraces’

apache2ctl -M | grep php
php-fpm -t

php -v 只能当一个入口。容器还要看镜像 digest、构建时间、包 changelog,以及实际运行进程加载的 libphp.so/php-fpm 路径。

打补丁

目标版本:

PHP 8.5.12
PHP 8.4.27
PHP 8.3.36
PHP 8.2.35

发行版提供的等价安全包也可以。

落地时,需要注意:

  1. 更新包以后必须滚动重启或重建所有 worker、Apache child 和容器。

  2. 多阶段构建镜像要确认最终镜像里的 runtime 被更新,builder 更新不算。

  3. FPM master 不会因为替换磁盘上的 .so 自动加载新代码,必须 reload/restart。

缓解措施

以下措施都替代不了补丁,但可以应急。

1、所有 SAPI

  • 在反向代理、API Gateway、Ingress 和 WAF 上限制解码后的请求体大小。

  • 对 multipart 限制 part 数、单 part header 数、单 header 长度、字段嵌套深度和文件控件数量。

  • 明确处理 chunked request、缺失 Content-Length、伪造或冲突的 Content-Length。

  • 如果业务不需要 multipart,直接在 PHP 之前拒绝 multipart/form-data。

  • 把前端限制和 PHP 自身 post_max_size、max_input_vars、max_input_nesting_level、max_file_uploads、max_multipart_body_parts 同时收紧。

  • 让每个 worker 或小型 worker 组有独立 cgroup 内存边界,避免一个容器杀 master 和所有 child。

  • 不要把 RLIMIT_AS、RLIMIT_DATA 当成修复手段。底层 mmap() 失败同样可能触发旧版 longjmp() 路径。

2、针对 109618

  • 无法升级时,官方给出的首要配置缓解是启动级设置并强制执行 memory_limit=-1。

  • 检查 FPM pool、Apache php_admin_value、.user.ini、控制面板和框架启动脚本,确认没有重新恢复有限值。

  • memory_limit=-1 仍不是完整修复。物理内存、cgroup、mmap() 或其他底层分配失败,仍可能触发漏洞路径。

  • 如果不使用上传,优先在前端阻断 multipart。必须上传时,把 post_max_size 保持在业务真正需要的范围内,并让 upload_max_filesize 更低。

  • 对无文件上传业务,可先把 post_max_size 压到接近普通表单大小。有上传业务时,避免继续沿用 100M、512M 这类省事配置。

3、针对 109619

  • 在启动配置中设置 session.upload_progress.enabled=0,然后重启全部 worker。

  • 不要依赖 ini_set()、.user.ini、运行时路由或管理后台开关。这些设置可能晚于漏洞触发点。

  • PHP 8.5 额外设置 fatal_error_backtraces=0,阻断已确认的 fatal backtrace producer。

  • 清理会在请求早期产生 warning、notice、deprecation 的无效 INI、过时配置和扩展参数。

  • 修复攻击者可达的 fatal error、解析错误、未捕获异常和 Maximum execution time exceeded 路径。

  • 性能允许时,把 pm.max_requests=1 作为缩小跨请求利用窗口的减害措施。它会显著增加进程创建成本,也可能掩盖其他问题。

关闭 session.upload_progress 只阻断已确认的 session consumer。它不会修复 109618,也覆盖不了 109619 的其他 extension 或 error state。

4、添加日志告警规则

建议把下面这些 PHP 日志模式加入临时告警:

Allowed memory size of .* exhausted
Maximum execution time of .* exceeded
Unknown on line 0
PHP Request Startup
Session ID is too long or contains illegal characters
Failed to read session data
Allowed memory size .* in Unknown on line 0

除此之外,还可以关注:

  • Apache/FPM child 异常退出、SIGABRT、segfault、频繁 respawn。

  • 同一客户端在短时间内发送超大 multipart、超多 part、超长 header 或大量 upload progress 字段。

  • max_input_vars warning 与后续 OOM 连续出现。

  • Set-Cookie: PHPSESSID 异常变长、包含不可打印字符或二进制样式内容。

  • FPM pool 在低业务量时快速耗尽。

  • 新出现的 www-data、apache、nginx、php-fpm 子进程、cron、systemd timer 或文件写入。

WAF 只能做补偿控制。实际利用可能只使用结构合法、内容看起来像普通表单的 HTTP 请求。单靠一个 multipart 特征签名,不能说明请求安全。

5、对于应急响应

  • 隔离对应实例和共享存储,保留内存、进程树、网络连接、日志和容器层。

  • 轮换 worker 可读的所有数据库、对象存储、API、CI/CD 和第三方服务凭据。

  • 检查 /tmp、上传目录、session 目录、webroot、cron、systemd、SSH authorized_keys、容器挂载和 Kubernetes service account。

  • 检查 worker 到内网管理口、Redis、数据库和云元数据服务的异常连接。

  • 不要只删除一个可疑文件就恢复服务。完整 RCE 通常伴随凭据获取和持久化。




参考来源



  • PHP 官方发布归档,用于核对 2026 年 10 月 21 日、四个修复版本和风险口径:https://www.php.net/archive/2026.php

  • CVE-2026-109618 官方 advisory,用于核对 RCE via OOM corruption、影响版本和补丁版本:https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w

  • CVE-2026-109619 官方 advisory,用于核对 RCE via stale global state、producer/consumer 条件和补丁版本:https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp

  • 官方补丁提交 3584a98bda0f2c0d367255b046798b0d3a221a19,提交说明同时引用两个 GHSA:https://github.com/php/php-src/commit/3584a98bda0f2c0d367255b046798b0d3a221a19

  • NVD CVE-2026-109618 详情页:https://nvd.nist.gov/vuln/detail/CVE-2026-109618

  • NVD CVE-2026-109619 详情页:https://nvd.nist.gov/vuln/detail/CVE-2026-109619

  • NVD REST API,CVE-2026-109618 原始响应:https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-109618

  • NVD REST API,CVE-2026-109619 原始响应:https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-109619

  • CERT-EU / NCSC-NL 运营信息和缓解建议:https://github.com/NCSC-NL/php2shell

  • Wiz Research 披露材料,用于核对已演示影响、利用构造过程和目标适配成本:https://www.wiz.io/blog/php2shell-php-rce

  • oss-security CVE-2026-109618 公告:http://www.openwall.com/lists/oss-security/2026/10/21/6

  • oss-security CVE-2026-109619 公告:http://www.openwall.com/lists/oss-security/2026/10/21/7


你对这个事件怎么看?对于漏洞修复,你有什么建议?欢迎在评论区分享。

— 阅读原文请访问微信公众号「表哥带我」 · 原文链接 —

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像-阻击者联盟
欢迎您留下宝贵的见解!
提交
头像-阻击者联盟

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容