先看结论

  • 受影响:PHP 8.2 < 8.2.35、8.3 < 8.3.36、8.4 < 8.4.27、8.5 < 8.5.12;7.4 / 8.0 / 8.1 已 EOL,需要厂商 backport。
  • 检测:运行 check-php-security-fixes.sh,指定 PHP 的绝对路径。
  • 修复:宝塔面板把 PHP 7.4.33 升级到 7.4.34,重启 PHP-FPM 后复测。
  • 实测结果:7.4.33 返回 NOT PATCHED;7.4.34 返回 PATCHED。

2026 年 10 月 21 日,PHP 项目发布了两个高危安全修复,漏洞被合称为 php2shell。两个 CVE 的 CVSS v4.0 评分均为 9.2(Critical),攻击者可以在未认证的情况下触发远程代码执行(RCE)。公开 PoC 已经出现,并且有安全厂商观察到在正式披露前就有人尝试利用。

这篇文章记录一次真实的修复过程:在宝塔面板上把 PHP 从 7.4.33 升级到 7.4.34,用官方提供的行为检测脚本确认修复,并把检测、升级、复测的截图一并保存下来,方便同样使用宝塔的站点照着操作。

实测环境:宝塔面板(Linux)、PHP 7.4.33 → 7.4.34、检测脚本为 2026-10-22 从项目仓库获取的版本。

一、php2shell 是什么?

这次披露包含两个独立的漏洞,触发路径不同,但最终都可能走到远程代码执行:

CVE-2026-109618:内存耗尽后的状态破坏

当一次请求在内存耗尽(OOM)的过程中被中止时,PHP 可能只完成了部分请求数据的初始化,但清理阶段仍会去处理这些不完整的数据。攻击者可以通过构造消耗内存的多部分(multipart)请求来反复触发,例如没有可用的 Content-Length、包含大量空文件字段、或者每个 part 带大量 header。成功利用后可导致 RCE。

CVE-2026-109619:跨请求的脏全局状态

PHP 的全局状态可能保留了指向已释放请求内存的指针。同一个 PHP-FPM worker 处理下一个请求时,如果在新请求完成初始化之前复用了这些旧状态,就可能触发 use-after-free 一类的问题。公开分析提到的触发路径包括 Session Upload Progress、PHP 8.5 的 fatal error backtrace、解析/编译错误以及执行超时等。

需要特别注意的是,Apache mod_php 的风险最高,已有在默认配置下完成 RCE 的演示;PHP-FPM 也在特定条件下被验证可利用。除了传统的 PHP-FPM 和 mod_php,php-cgi、FrankenPHP、LiteSpeed / LSAPI、容器镜像以及各种面板内置的 PHP 都可能受影响。

哪些版本需要处理?

PHP 分支修复版本 / 状态
8.28.2.35
8.38.3.36
8.48.4.27
8.58.5.12
7.4 / 8.0 / 8.1已 EOL,无官方修复版本;需要操作系统或面板厂商提供 backport

PHP 7.4 官方最后一个版本是 7.4.33,所以宝塔提供的 7.4.34 并不是 PHP 官方新分支,而是宝塔为 7.4 分支做的安全回移构建。它能解决眼前的问题,但 7.4 已经停止维护,长期还是应该规划迁移到 8.2 或更高版本。

二、检测:先确认你的 PHP 是否修复

这次使用的检测脚本来自 SamJUK/php2shell-check-cve-2026-109618。它不是一个漏洞扫描器,也不会发送 exploit;它只是启动一个短生命周期的 PHP CLI 进程,把内存限制设为 8M,然后观察内存耗尽后 PHP 是否会继续执行 shutdown handler。

脚本的退出码含义很直接:

  • 0:检测到已修复的行为;
  • 1:仍是旧的、未修复的 OOM 行为;
  • 2:结果不确定,或者找不到 PHP 二进制。

1. 登录服务器,确认 PHP 路径

宝塔的 PHP 通常安装在 /www/server/php/ 下。先看有哪些版本:

ls /www/server/php/

本文要检查的是 7.4:

/www/server/php/74/bin/php -v

如果输出是 PHP 7.4.33,说明当前 CLI 还是旧版本,需要升级。注意不要只看 php -v,因为系统 PATH 里的 php 可能不是宝塔正在给站点使用的那个版本。

2. 下载检测脚本

cd /root
curl -fsSLO https://raw.githubusercontent.com/SamJUK/php2shell-check-cve-2026-109618/refs/heads/master/check-php-security-fixes.sh
chmod +x check-php-security-fixes.sh

如果服务器访问 GitHub raw 不稳定,也可以先在本地下载,再用 scp 上传到服务器。脚本本身只有几 KB。

3. 执行检测

bash /root/check-php-security-fixes.sh /www/server/php/74/bin/php

升级前的输出如下:

PHP 7.4.33 检测结果为 NOT PATCHED

图 1:升级前,PHP 7.4.33 返回 NOT PATCHED(主机名已打码)

关键信息有两行:

  • PHP 7.4.33 (/www/server/php/74/bin/php):脚本确认检查的是哪个二进制;
  • NOT PATCHED: PHP ran the shutdown handler after running out of memory.:内存耗尽后仍然执行了 shutdown handler,说明还是旧行为,需要升级。

4. 如果服务器装了多个 PHP 版本

宝塔经常会同时安装 7.4、8.0、8.1 等多个版本,不同站点可能绑定不同的 PHP。建议逐个检查:

for p in /www/server/php/*/bin/php; do
  echo "=== $p"
  bash /root/check-php-security-fixes.sh "$p"
done

容器环境则要在容器内部检查,不能只检查宿主机:

docker exec -i 容器名 bash -s < check-php-security-fixes.sh

脚本会在容器里用默认的 php 命令执行;如果容器里的 PHP 不在 PATH,需要进入容器后手动指定绝对路径。

三、升级宝塔 PHP 7.4.33 到 7.4.34

操作前请注意:宝塔的 PHP 更新会重新编译并替换文件,过程中可能导致服务短暂中断。建议在低峰期操作,先做服务器快照或备份,不要在业务高峰期直接点更新。

1. 打开 PHP 7.4 的设置

进入宝塔面板 → 软件商店 → 已安装 → 找到 PHP 7.4 → 点击 设置。

2. 点击“立即更新”

弹窗左上角会显示当前版本 PHP 7.4.33,下面一行显示“最新版本:7.4.34”。右上角会出现绿色的 立即更新 按钮,点它即可。

宝塔面板 PHP 7.4.33 升级到 7.4.34 的入口

图 2:宝塔 PHP 7.4 管理弹窗,右上角“立即更新”就是升级入口

更新过程需要下载源码并编译,通常要几分钟。期间不要关闭弹窗,也不要重复点击。更新日志里也写得很清楚:已经存在的插件文件会被替换,更新过程可能导致服务中断。

3. 确认版本

更新完成后,分别检查 CLI 和 FPM 的版本:

/www/server/php/74/bin/php -v
/www/server/php/74/bin/php-fpm -v

两个命令都应该输出 PHP 7.4.34。如果 CLI 已经是 7.4.34,但 FPM 还是旧版本,说明 FPM 进程没有重新加载,继续看下一步。

4. 重启 PHP-FPM

仅仅替换二进制文件还不够,正在运行的 PHP-FPM worker 仍然持有旧进程。必须在宝塔弹窗的 服务 页点 重启,或者用命令行重启:

/etc/init.d/php-fpm-74 restart

部分系统也可以使用:

systemctl restart php-fpm-74

重启同时会清掉 opcache,确保站点加载的是新二进制。如果服务器上有多个 PHP 版本,记得把每个正在使用的版本都升级并重启;同时到 网站 → 设置 → PHP 版本 确认站点绑定的是哪个版本。

四、复测:确认已经修复

升级并重启后,再跑一次同一个命令:

bash /root/check-php-security-fixes.sh /www/server/php/74/bin/php

这次输出应该变成:

PHP 7.4.34 检测结果为 PATCHED

图 3:升级后,PHP 7.4.34 返回 PATCHED(主机名已打码)

输出里的 PATCHED: PHP used the safe OOM exit from the 7.4-8.1 backport. 表示 7.4 分支已经用上了安全回移:内存耗尽时会在执行 shutdown handler 之前安全退出。脚本此时返回 0。

不同分支的通过输出会略有差别:8.2 及以上较新的修复版本会使用新的硬内存限制,输出类似 PATCHED: PHP used the new hard memory limit and stopped safely.,进程状态码是 137;7.4 / 8.0 / 8.1 的 backport 则输出 safe OOM exit from the 7.4-8.1 backport,状态码是 1,但脚本仍判定为已修复并返回 0。

结果怎么读

输出含义建议
NOT PATCHED: PHP ran the shutdown handler...旧行为,OOM 后仍执行 shutdown handler升级到含修复的构建,并重启 FPM
PATCHED: ... safe OOM exit from the 7.4-8.1 backport.7.4 / 8.0 / 8.1 的安全回移已生效保持该版本,规划迁移到受支持分支
PATCHED: ... new hard memory limit...8.2+ 新版本的硬内存限制已生效保持更新
INCONCLUSIVE结果不确定,或 PHP 无法执行测试代码检查路径、权限、php.ini 和容器环境

重要说明:这个脚本验证的是 OOM 路径的行为,主要对应 CVE-2026-109618 的触发条件;CVE-2026-109619 涉及跨请求的脏状态,无法靠这个本地行为测试单独判定。脚本通过不代表可以对 109619 掉以轻心,仍然应该升级到厂商明确包含两项修复的构建,并关注宝塔的更新日志。

如果复测仍然是 NOT PATCHED

按下面顺序排查:

  1. 路径不对:站点实际使用的可能不是 /www/server/php/74/bin/php。先看宝塔里站点绑定的 PHP 版本,再用对应路径检测。
  2. FPM 没重启:CLI 已经是新版本,但 FPM worker 还是旧进程。重启 PHP-FPM 后再测。
  3. 还有旧 worker:用 ps -eo pid,cmd | grep '[p]hp-fpm' 看是否还有旧进程残留;必要时在面板里停止再启动。
  4. 容器镜像没更新:宿主机升级了,但容器里还是旧镜像。需要重新构建或拉取新镜像。
  5. 更新失败:回看宝塔的更新日志和 PHP 编译日志,确认 7.4.34 真的安装成功,而不是只改了版本号显示。
  6. 多个版本混用:对每个正在使用的 PHP 版本分别执行检测,不要只测一个。

五、加固建议:升级只是第一步

php2shell 的利用门槛不高,互联网暴露的 PHP 服务应该按“已经被扫描”的心态处理。升级之外,建议同时做这几件事:

1. 所有 PHP 分支都处理,不只 7.4

8.2、8.3、8.4、8.5 都有对应修复版本;如果服务器上还有 8.0、8.1 或更老的版本,也要确认厂商是否提供了 backport。升级后统一重启 PHP-FPM 和 Apache,容器环境要重建镜像,不能只重启宿主机。

2. 7.4 只作为临时方案

7.4 已经 EOL,宝塔的 7.4.34 是厂商回移,不是长期维护版本。建议尽快把站点迁移到 8.2 或更高版本,并在测试环境验证扩展、框架和业务代码的兼容性。

3. 暂时无法升级时的缓解措施

下面这些措施只能降低风险,不能替代补丁:

  • 在 php.ini 或 FPM pool 中关闭 session.upload_progress.enabled,然后重启 FPM。注意这个配置不能用 ini_set() 在运行时修改。
  • PHP 8.5 可以设置 fatal_error_backtraces=0。
  • 在 Nginx 限制请求体大小(如 client_max_body_size),在 Apache 设置 LimitRequestBody;如果业务不需要 multipart,直接在反向代理层拒绝。
  • 在 WAF 上启用相关规则作为纵深防御。Cloudflare 等厂商已经发布了针对 php2shell 的托管规则。

4. 关注异常,但不要只依赖日志

可以在 PHP 错误日志里关注 Allowed memory size、Out of memory 的异常峰值,在访问日志里关注异常大的 multipart 请求、大量空文件字段、畸形 Content-Length。需要提醒的是,部分攻击不会执行应用代码,应用层日志可能几乎没有痕迹;没有告警不等于安全。

如果怀疑已经被利用,优先隔离主机、轮换 PHP 进程可访问的密钥和数据库口令,再检查 webshell、计划任务、异常进程和最近修改的文件。

六、常见问题

Q1:php -v 已经是 7.4.34,为什么脚本还是 NOT PATCHED?

最可能是路径不对,或者 FPM 没有重启。脚本检查的是你传给它的那个二进制;站点实际使用的可能是另一个 PHP 版本。用绝对路径分别检查 CLI 和 FPM,并确认面板里站点绑定的版本。

Q2:7.4.34 是 PHP 官方版本吗?

不是。PHP 7.4 官方最后一个版本是 7.4.33,7.4.34 是宝塔的厂商构建,包含 7.4-8.1 分支的安全回移。它可以作为过渡,但不应作为长期方案。

Q3:脚本显示 PATCHED,就能证明两个 CVE 都修好了吗?

不能完全证明。脚本验证的是 OOM 后的 shutdown 行为,主要覆盖 CVE-2026-109618 的路径;CVE-2026-109619 是跨请求状态问题,本地行为测试无法单独区分。结论应该以厂商构建包含的修复为准,必要时升级到官方受支持分支的最新版本。

Q4:只关闭 session.upload_progress 可以吗?

不行。它只是打断公开分析中提到的一条触发链,属于临时缓解,不能替代升级。攻击面还包括其他 OOM 和状态复用路径。

Q5:升级后网站 502 怎么办?

先看 PHP-FPM 是否正常启动、端口和 socket 是否与 Nginx 配置一致,再看 PHP 错误日志和扩展兼容性。如果必须回退,先把服务恢复,再尽快重新升级或迁移到受支持版本,不要长期停留在未修复状态。

七、参考链接


本文的记录时间是 2026-10-22。漏洞信息和修复版本可能继续更新,实际处置时请以 PHP 官方公告、操作系统发行版和宝塔面板的最新说明为准。