PHP2Shell 漏洞(CVE-2026-109618、CVE-2026-109619)临时缓解记录

PHP2Shell 漏洞(CVE-2026-109618、CVE-2026-109619)临时缓解记录

记录时间:2026-11-03
环境:CentOS 7.9(el7)/ Zabbix 4.4.6 / PHP 7.2.34(自编译,/data/app/php72)/ Tengine / 站点根目录 /data/app/tengine/html

一、问题现象

安全那边同步了两个 PHP 漏洞:CVE-2026-109618 和 CVE-2026-109619,等级 Important,都能导致远程代码执行。阿里云安全中心的修复文档把这组漏洞叫 PHP2Shell。

这个漏洞我早就知道,只是公司业务基本都是 Java 服务,我没往 PHP 上想。安全提醒之后才反应过来,这台 Zabbix 4.4 的前端是 PHP 跑的。

机器上查到的是系统 RPM 的 PHP:

php-5.4.16-48.el7.x86_64
php-cli-5.4.16-48.el7.x86_64
php-common-5.4.16-48.el7.x86_64

which php 指向 /usr/bin/php,php --ini 读 /etc/php.ini。但系统 RPM 的 5.4 只是 CLI,Web 实际跑的是 /data/app/php72 这套 2020 年编译的 PHP 7.2.34,FPM 由 /etc/init.d/php-fpm 以 -c /data/app/php72/etc/php.ini 启动。要处理的是这个 7.2。

阿里 ALinux 2 的修复版 php-5.4.16-48.2.al7 在公网和内网源都取不到,PHP 官方也只修到 8.2.35、8.3.36、8.4.27、8.5.12,7.2 没有官方补丁。业务不能停机,也没有 PHP 升级窗口,我先按官方缓解措施做配置收敛,升级后续再排期。

二、根因分析

CVE-2026-109618 出在 PHP 处理 OOM 的路径上:内存耗尽时用 longjmp 退出当前上下文,部分场景下请求内存还没初始化就被后续清理逻辑读取或分支,最终可以走到 RCE。触发点是 memory_limit 耗尽。

CVE-2026-109619 是跨请求的全局状态问题:全局变量临时指向请求内存,某些场景下没被正确重置,或者重置后又被重新填回。请求内存释放后,全局状态仍然指向那块内存,后续请求读到脏状态,同样可以 RCE。

这台机器的风险点有三个:

  • Web PHP 是 7.2.34,2020 年编译,上游早已 EOL,没有任何官方补丁
  • Zabbix 4.4.6 也是 2021 年 EOL 的版本
  • FPM worker 原本 pm.max_requests = 5000,低流量下能活好几天,跨请求状态复用窗口很长

官方文档明确写了没有能完整消除这两个漏洞的通用配置,配置只能降低特定触发路径的风险。

三、解决方案

3.1 修改前备份

cp -a /data/app/php72/etc/php.ini \
      /data/app/php72/etc/php.ini.bak.$(date +%Y%m%d%H%M%S)
cp -a /data/app/php72/etc/php-fpm.d/www.conf \
      /data/app/php72/etc/php-fpm.d/www.conf.bak.$(date +%Y%m%d%H%M%S)

本次生成的备份:

/data/app/php72/etc/php.ini.bak.20261103214011
/data/app/php72/etc/php.ini.bak2.<时间戳>
/data/app/php72/etc/php-fpm.d/www.conf.bak.<时间戳>

3.2 关闭 session 上传进度

编辑 /data/app/php72/etc/php.ini,保留原来的注释行,新增生效行:

;session.upload_progress.enabled = On
session.upload_progress.enabled = Off

也可以用命令改:

sed -i -E 's/^[[:space:]]*;?[[:space:]]*session\.upload_progress\.enabled[[:space:]]*=.*/session.upload_progress.enabled = 0/' \
      /data/app/php72/etc/php.ini

3.3 取消内存上限

先看内存余量,确认这台机器扛得住:

free -m
              total        used        free      shared  buff/cache   available
Mem:          31985        5196         835         372       25953       26023
Swap:         10239        1942        8297

32GB 内存,available 26GB,dmesg 里没有 OOM 记录。这个余量下按官方建议设 -1:

sed -i -E 's/^[[:space:]]*;?[[:space:]]*memory_limit[[:space:]]*=.*/memory_limit = -1/' \
      /data/app/php72/etc/php.ini
grep -n '^memory_limit' /data/app/php72/etc/php.ini
# 401:memory_limit = -1

3.4 限制 FPM worker 请求数

原来的值是 5000:

grep -n 'pm.max_requests' /data/app/php72/etc/php-fpm.d/www.conf
# 139:pm.max_requests = 5000

把旧值用 ; 注释掉,新增一行。php-fpm.conf 的注释符是 ;,不是 #:

;pm.max_requests = 5000
pm.max_requests = 1
sed -i -E 's/^[[:space:]]*[#;]?[[:space:]]*pm\.max_requests[[:space:]]*=.*/pm.max_requests = 1/' \
      /data/app/php72/etc/php-fpm.d/www.conf
/data/app/php72/sbin/php-fpm -t -y /data/app/php72/etc/php-fpm.conf

3.5 重启

/etc/init.d/php-fpm restart

配置汇总:

参数 值 说明
session.upload_progress.enabled Off 关闭上传进度,切断 109619 的一条触发路径
memory_limit -1 取消 PHP 内存上限,避开 109618 的 OOM 触发点
pm.max_requests 1 每个 worker 只服务一个请求,杜绝跨请求状态复用

四、验证

4.1 FPM SAPI 层

/data/app/php72/sbin/php-fpm -i -c /data/app/php72/etc/php.ini \
  | grep -E '^memory_limit|session.upload_progress.enabled'
memory_limit => -1 => -1
session.upload_progress.enabled => Off => Off

4.2 Web SAPI 层

在站点根目录放临时文件,用真实请求验证:

printf '%s\n' '<?php echo ini_get("session.upload_progress.enabled") ? "ON" : "OFF";' \
  > /data/app/tengine/html/_chk.php
curl -s http://127.0.0.1/_chk.php; echo
rm -f /data/app/tengine/html/_chk.php
OFF
printf '%s\n' '<?php echo ini_get("memory_limit"), PHP_EOL;' \
  > /data/app/tengine/html/_chk2.php
curl -s http://127.0.0.1/_chk2.php; echo
rm -f /data/app/tengine/html/_chk2.php
-1

4.3 服务状态

curl -sI http://127.0.0.1/ | head -1
# HTTP/1.1 200 OK

worker 回收可以用 PID 对比确认:

pgrep -f 'php-fpm: pool www' | sort > /tmp/fpm_before
for i in $(seq 1 20); do curl -s -o /dev/null http://127.0.0.1/index.php; done
pgrep -f 'php-fpm: pool www' | sort > /tmp/fpm_after
comm -13 /tmp/fpm_before /tmp/fpm_after | wc -l

五、注意事项

这三个配置只是缓解,不是修复。109618 的 OOM 路径和 109619 的其他状态路径仍然存在,官方也明确说没有通用缓解方案。

  • memory_limit = -1 的代价是资源耗尽风险。极端请求可能把内存吃满,触发内核 OOM killer。建议给关键进程加保护:
echo -1000 > /proc/$(pidof mysqld)/oom_score_adj
for p in $(pgrep -f '/data/app/zabbix-4.4.6/sbin/zabbix_server'); do
  echo -1000 > /proc/$p/oom_score_adj 2>/dev/null
done
  • php-fpm.conf 和 www.conf 的注释符是 ;,不是 #。改完先跑 php-fpm -t 再重启。
  • CLI 的 php --ini、php -i 不代表 FPM 的加载结果。这台机器的 FPM 通过 init 脚本的 -c 参数读 /data/app/php72/etc/php.ini,验证要用 php-fpm -i -c 或 Web 临时文件。
  • 回滚时把备份覆盖回去,再重启 FPM:
cp -a /data/app/php72/etc/php.ini.bak.20261103214011 /data/app/php72/etc/php.ini
cp -a /data/app/php72/etc/php-fpm.d/www.conf.bak.<时间戳> /data/app/php72/etc/php-fpm.d/www.conf
/etc/init.d/php-fpm restart
  • 前端目前监听 80,站点根目录直接就是 Zabbix 前端。安全组和防火墙只对运维网段开放,这是成本最低的一道防线。
  • 长期方案还是升级。PHP 7.2.34 和 Zabbix 4.4.6 都过了 EOL,最终要迁到 Zabbix 7.x + PHP 8.2.35+ 或更受支持的组合。

六、后续可选:参考第三方补丁重编译

想彻底修掉这两个 CVE,还有一个路:参考 Qsnh/php2shell-fixed,把 patches/php-7.2.34-CVE-2026-109618-109619.patch 打到官方 php-7.2.34 源码上重新编译。仓库里的一键脚本是给宝塔写的,路径写死在 /www/server/php/<版本>,我们这套 /data/app/php72 用不上,只能自己编译。

我试过这个 patch,能干净应用到官方 7.2.34 源码(16 个文件,dry-run 通过)。但风险不低:补丁是第三方从官方 7.4 补丁移植的,不是 PHP 官方产物;要原样复用 2020 年的编译参数,依赖库、扩展、链接库对不上就可能起不来;补丁还改了 OOM 和 multipart 的行为,Zabbix 前端得重新回归。

所以我这次没动,继续用配置缓解顶着,等有维护窗口再评估。

七、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇