通知:两个致命紧急PHP漏洞需要修改

2026-10-22 01:16:23    行业资讯   

两个 CVSS 4.0 9.2 的未授权 RCE:一个在请求解析收尾时就能触发,一个靠上一个请求留下的脏状态喂给下一个请求。

10 月 21 日,PHP 安全团队一次发了四个修复版本:8.5.12、8.4.27、8.3.36、8.2.35。修的是 CVE-2026-109618 和 CVE-2026-109619,两个都能未授权远程执行代码。公告原话:没有完整的缓解措施,升级是唯一安全的选择。配置只能砍掉部分触发路径,完整修复只有换版本。

PHP 两个 9.2 分未授权 RCE 漏洞,需升级到 8.5.12 等版本

图 1 · 封面:两个 9.2 分,四个修复版本

洞一:内存被打爆,也可能变成 RCE

CVE-2026-109618,破坏点在 OOM 处理上。

PHP 处理内存耗尽用的是 longjmp():就地跳出当前上下文、中止这个请求。有些时机请求内存已经能被访问,却还没初始化完。这时只要有分配失败——失败的那次分配甚至不需要和这块内存有关系——PHP 就带着半初始化的状态退出去,请求收尾阶段再把这份脏数据当成有效数据读。

公告列了三条「在限额之内打爆内存」的路子:

  • multipart 请求不带可用的 Content-Length,能绕过 post_max_size 的初检继续灌数据
  • 单个 part 的 header 数量没有上限,它在内存里的表示(数组加字符串头)比线上字节大得多,post_max_size 之内的请求就能顶爆 memory_limit
  • 空文件控件不计入 max_file_uploads,照样生成完整的上传元数据数组,字段名再嵌套几层,数组开销继续翻倍

洞一触发链:OOM 提前退出,收尾阶段读到无效状态

图 2 · 洞一:从内存耗尽到远程代码执行

补丁把 longjmp() 换成两段式:在下一个 VM 安全点触发延迟的软 OOM,内存再冲到第二个、更高的阈值就直接 _exit() 硬退出。shutdown 阶段不会再看到无效状态,memory_limit 该有的限制一点没少。

洞二:上一个请求的垃圾,喂给下一个请求

CVE-2026-109619 是另一条路:跨请求的陈旧全局状态。这类问题要凑齐两半才成立——写脏的一侧(生产者)和读脏的一侧(消费者)。

worker 长期持有 error、session、object 和扩展状态,这些字段指向的内存属于某一次请求。正常流程里,请求内存释放前会把它们重置掉。但只要中途撞上致命错误,解析失败、未捕获异常、require 失败、执行超时、内存耗尽都算,操作就被丢在半路,配套的重置没执行。一条普通 warning 也可能在清理之后,把新的请求内存重新写回全局字段。这就是生产者。

堆释放了,worker 里的指针还指着它。同一个 worker 接下个请求时,正常初始化之前就有操作去读、去释放、拿它做分支判断。这是消费者。堆布局可控时,use-after-free 直接变成 RCE。

公告点名了两个已确认的消费者:请求初始化之前触发的无效/废弃 INI 告警,以及 session upload progress 在常规 session 初始化之前碰到 session 状态。生产者侧已确认的案例同样是两个:PHP 8.5 的 fatal_error_backtraces,和 upload progress 的生命周期。把 error_reporting 调低、加个 @、关掉 display_errors 都拦不住——全局状态在 PHP 决定要不要报告之前,就已经被读过了。

攻击成立需要生产者和消费者落在同一个 worker 上。多开 worker、勤回收能降低命中率,条件本身一直都在。

洞二触发链:请求 A 留下脏状态,请求 B 在初始化前读到它

图 3 · 洞二:写脏的和读脏的,必须落在同一个 worker 上

谁在射程内

所有对外处理 web 请求的 PHP:PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed。公告没提在野利用,但 mod_php 上的完整利用链已经跑通,并做了 HTTP 验证。

mod_php 风险靠前:它不管请求启动成功还是失败,都会跑一遍 shutdown 流程,所以 startup 阶段打爆内存,就能在应用代码起来之前走完整个链条。FPM 稍好一点,startup 失败直接退出子进程,脏状态没机会被消费;但应用执行阶段撞上 fatal、再配合 upload progress 的那条路,已经在 FPM 上验证通过。FrankenPHP 与 mod_php 同类,LiteSpeed 与 FPM 同类,这两个只做了源码分析。

常驻进程框架(Swoole、Workerman、RoadRunner 之类)不在公告评估的 SAPI 名单里,官方没给结论。按最坏情况处理,一样升级。

升到哪个版本:

  • PHP 8.5 → 8.5.12
  • PHP 8.4 → 8.4.27
  • PHP 8.3 → 8.3.36
  • PHP 8.2 → 8.2.35。这个分支 2026 年 12 月 31 日 EOL,之后不再有安全修复,能顺手排到 8.3 或 8.4 就一起排
  • PHP 8.1 及更早:项目本身不再发版,先问发行版和 LTS 厂商有没有 backport;官方也为 7.4、8.0、8.1 准备了最小源码 backport 分支

公告还提了一句社区维护的 rpms.remirepo.net:那个仓库当天不会有更新,官方建议换成发行版包,或者自己从源码构建。

升级之前,先把能关的门关上

下面这些都不等于安全,只是缩小具体路径的暴露面。

先确认自己站在哪个版本上。别只查 CLI,对外服务的是 FPM:

php -v
php-fpm -v
php -i | grep -E 'memory_limit|session.upload_progress.enabled|fatal_error_backtraces'

配置长这样(fatal_error_backtraces 只有 8.5 需要):

; /etc/php/8.4/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[session.upload_progress.enabled] = 0
php_admin_flag[fatal_error_backtraces] = 0
pm.max_requests = 1          ; 可选,见下文代价

挡 OOM 那条:

  • memory_limit 设成 -1,写在 FPM pool 的 php_admin_value 或 Apache 配置里,让应用改不回来。改完重启全部 worker,再逐个确认没有 pool、目录或应用把它设回有限值。底层分配真失败了,仍然可能走到那条路径,所以这只是缩小面。裸机上用这招要配外部边界(systemd 的 MemoryHigh/MemoryMax,或容器 cgroup),否则一个死循环就能把整台机器连同数据库一起拖死。
  • post_max_size 收紧:不用上传就 8M,要上传就 32M,upload_max_filesize 记得比它小。代理层能限的是字节数和 header 长度(client_max_body_size、large_client_header_buffers),part 数、字段数、嵌套深度得在 PHP 侧(max_file_uploads、max_input_vars、max_input_nesting_level)或 WAF 上做。没有 Content-Length 的请求也要算进去。
  • 拿容器或 cgroup 的内存上限当可用性边界,最好只杀受影响的 worker。别用 RLIMIT_AS、RLIMIT_DATA 顶替,它们会直接造成那次致命的分配失败。

挡陈旧状态那条:

  • session.upload_progress.enabled=0,重启全部 worker。这条必须在请求解析之前生效,写进 php.ini、FPM pool 或服务器配置;ini_set() 和 .user.ini 都太晚。
  • PHP 8.5 再加一条 fatal_error_backtraces=0,同样要重启。
  • 把请求级的无效、废弃 INI 清干净。用 error_reporting、display_errors 或 @ 把诊断藏起来没有用。
  • 修掉攻击者能摸到的致命错误和执行超时,包括 callback、上传、导入、清理阶段的失败。

两个可选项:FPM 的 pm.max_requests=1 让 worker 干完一单就换人(mod_php 对应 MaxConnectionsPerChild),代价是每次请求冷启动、opcache 失效;以及开 log_errors=On 加 E_ALL 做一轮配置和日志审计,注意日志里没报错不等于安全。

还有一件事容易被忽略:打完补丁,OOM 的表现会变。软 OOM 延迟到安全点才生效,硬 OOM 直接 _exit(),worker 会当场消失,PHP 错误日志里可能什么都没留下。监控上看得到的可能只是一次莫名单个 worker 掉线,或者 502 抖一下。这不是新故障,是补丁的正常行为。

升级做完,把临时措施撤回去:memory_limit 调回合理值,业务真需要 upload progress 就开回来。

升级对照:8.5、8.4、8.3、8.2 四个分支的修复版本

图 4 · 对照表:四个分支、四个版本号

出处

以上信息核对于 2026-10-22。

php -v 跑一下,版本号自己心里有数就行。要是还停在 8.1 或更早,说说卡在哪一步——发行版没跟上、框架版本锁死,还是共享主机动不了。公告会过期,留在旧版本上的机器不会自己变安全。

QQ
微信
公众号
客服