目标:在不升级 PHP 大版本(留在 7.4)的前提下,给运行中的 PHP-FPM 打上官方 EOL backport,修复两个未授权 RCE。 本文分两部分:第一部分直接用编译好的包部署到同配置机器(免编译,最快);第二部分从头源码编译并切换。
背景与适用环境
文件php74-patched-712e60-arm64.tar.gz
- 漏洞:2026-10-21 PHP 官方紧急修复两个未授权 RCE,代号 php2shell,CVSS v4.0 9.2。
- CVE-2026-109618:OOM 时
longjmp提前退出,未初始化请求内存被当有效数据,内存破坏。 - CVE-2026-109619:worker 跨请求残留陈旧全局状态,释放后使用(UAF)。
- CVE-2026-109618:OOM 时
- 官方安全版本为 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35;8.1 及更老官方停更,但 PHP 安全团队为 7.4/8.0/8.1 发布了最小源码 backport。
- 官方 backport:仓库分支
PHP-7.4-CVE-2026-109618-109619,commit712e60f9edfd03f0960fc4fc3c2c375f42b90aa2(”Avoid state corruption via bailout, Minimal mitigation for EOL version”)。 - 本文实战环境:Ubuntu 20.04 focal / arm64(aarch64) / 原 PHP 7.4.3-4ubuntu2.29 FPM / nginx
fastcgi_pass unix:/run/php/php7.4-fpm.sock/ systemd 管理。 - 重要边界:该 backport 只修这两个 CVE,并强制关闭 session upload progress;7.4 仍处 EOL,不补其他历史漏洞,长期建议迁 8.3/8.4。
第一部分:编译好的包 → 部署到同配置机器(免编译)
适用:目标机与编译机同架构(arm64)、同系统(Ubuntu 20.04 focal)、同 PHP-FPM 部署方式。直接拷贝二进制,几分钟完成,无需编译。
1.1 适用前提(先核对)
在目标机逐项确认,全部满足再继续:
1
2
3
4
5
6
7
8
9
10
11
# 架构必须是 aarch64(x86 不能用本包二进制)
uname -m # 期望 aarch64
# 系统
cat /etc/os-release | grep PRETTY # 期望 Ubuntu 20.04
# 原 fpm 走 systemd、socket 路径一致
systemctl list-unit-files | grep php7.4-fpm
ls -l /run/php/php7.4-fpm.sock
# pool 配置目录存在
ls /etc/php/7.4/fpm/pool.d/
# 关键系统库存在(focal 默认自带)
ldd /usr/sbin/php-fpm7.4 | grep -E 'libssl|libsodium|libsqlite3|libxml2|libicu'
架构不同(如 x86_64)请走第二部分源码编译,不要强行拷贝本包。
1.2 获取安装包
包名:php74-patched-712e60-arm64.tar.gz(约 5.8M),md5 2b2543fc97c2cc6f1e801e4ead16d4fe。
从编译机(示例 IP 192.168.41.102)拉到本地或直接传到目标机:
1
2
3
4
5
# 在一台能同时访问两台机器的主机上执行
scp [email protected]:/root/php74-patched-712e60-arm64.tar.gz /tmp/
# 再传到目标机(替换成目标机 IP)
scp /tmp/php74-patched-712e60-arm64.tar.gz root@<目标机IP>:/tmp/
在目标机校验完整性:
1
2
3
cd /tmp
md5sum php74-patched-712e60-arm64.tar.gz
# 应输出:2b2543fc97c2cc6f1e801e4ead16d4fe
1.3 解包
包内目录结构(解到 / 后自动落到 /opt/php74-patched 和 /deploy):
1
2
3
4
5
6
7
8
./opt/php74-patched/sbin/php-fpm
./opt/php74-patched/bin/php
./opt/php74-patched/lib/php/extensions/no-debug-non-zts-20190902/opcache.so
./opt/php74-patched/etc/php.ini
./opt/php74-patched/etc/fpm-prod.conf
./deploy/deploy.sh
./deploy/rollback.sh
./deploy/php74-patched-fpm.service
目标机 root 解包:
1
2
tar xzf /tmp/php74-patched-712e60-arm64.tar.gz -C /
ls -l /opt/php74-patched/sbin/php-fpm
1.4 执行部署(停旧启新,约几秒 PHP 中断)
1
bash /deploy/deploy.sh
脚本做的事(等价手动步骤见 1.7):
- 拷贝
php74-patched-fpm.service到/etc/systemd/system/; systemctl daemon-reload;- 停止并禁用旧
php7.4-fpm; - 启用并启动
php74-patched-fpm,复用现有 pool 和同一个 unix socket; - 打印活动状态和新版本号。
1.5 验证
1
2
3
4
5
6
7
8
9
10
11
12
13
# 单元状态
systemctl is-active php74-patched-fpm # 期望 active
systemctl is-enabled php74-patched-fpm # 期望 enabled
# 运行版本(应为 7.4.34-dev)
/opt/php74-patched/sbin/php-fpm -v
# socket 已重建、属主 www-data
ls -l /run/php/php7.4-fpm.sock
# 站点回归(状态码应与切换前一致)
curl -I http://127.0.0.1/
curl -I http://127.0.0.1:6008/
可选:放一个临时探针经 nginx→fpm 确认真实运行版本,验完即删:
1
2
3
printf '<?php header("Content-Type: text/plain"); echo "VER=".PHP_VERSION." opcache=".(function_exists("opcache_get_status")?"on":"off")."\\n";' > /var/www/html/__pc.php
curl -s http://127.0.0.1/__pc.php # 期望 VER=7.4.34-dev opcache=on
rm -f /var/www/html/__pc.php
1.6 回滚(旧服务保留,随时可退)
1
2
3
4
5
6
bash /deploy/rollback.sh
# 等价于:
# systemctl stop php74-patched-fpm
# systemctl disable php74-patched-fpm
# systemctl enable --now php7.4-fpm
systemctl is-active php7.4-fpm
1.7 手动等价步骤(不使用 deploy.sh 时)
1
2
3
4
5
cp /deploy/php74-patched-fpm.service /etc/systemd/system/
systemctl daemon-reload
systemctl stop php7.4-fpm
systemctl disable php7.4-fpm
systemctl enable --now php74-patched-fpm
第二部分:从头源码编译 + 切换(不同架构 / 标准化)
适用于:没有现成包、架构不同、或想在本机复现整套流程。全程独立目录 /opt/php74-patched,不覆盖系统原文件。
2.1 安装编译工具链与依赖
1
2
3
4
5
6
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
build-essential bison re2c pkg-config autoconf \
libxml2-dev libsqlite3-dev libssl-dev libsodium-dev \
zlib1g-dev libffi-dev libreadline-dev libargon2-dev ca-certificates
说明:源码快照(commit tarball)不含预生成
configure,需要autoconf跑buildconf。
2.2 经代理拉取官方 backport 源码
若直连 GitHub 不通,在 URL 前加代理前缀 https://jasu.521156.xyz/。
1
2
3
4
5
mkdir -p /root/phpsrc && cd /root/phpsrc
curl -sL --max-time 150 -o bp.tar.gz \
"https://jasu.521156.xyz/https://codeload.github.com/php/php-src/tar.gz/712e60f9edfd03f0960fc4fc3c2c375f42b90aa2"
tar xzf bp.tar.gz
cd php-src-712e60f9edfd03f0960fc4fc3c2c375f42b90aa2
校验补丁已在源码树内(两条 backport 特征):
1
2
3
4
# 1) session 强制关闭 upload_progress 的最小缓解标记
grep -n 'rfc1867_enabled) || true' ext/session/session.c
# 2) 版本仍属 7.4
grep PHP_VERSION main/php_version.h
2.3 生成 configure 并配置
1
2
3
4
5
6
7
8
9
10
11
12
13
./buildconf --force
./configure \
--prefix=/opt/php74-patched \
--enable-fpm \
--enable-opcache \
--enable-pcntl \
--enable-pdo --with-pdo-sqlite --with-sqlite3 \
--with-openssl --with-sodium --with-zlib \
--enable-calendar --enable-exif --enable-ftp \
--enable-phar --enable-posix --enable-shmop \
--enable-sockets --enable-sysvmsg --enable-sysvsem --enable-sysvshm \
--disable-mbstring
configure 会提示
unrecognized options: --enable-ffi, --enable-gettext, --enable-readline,属正常(7.4 这些开关名不同或默认内置),不影响核心;本场景站点未使用 ffi/gettext,故未编入。若需要 ffi,改用--with-ffi。
2.4 编译(内存小务必串行 + 加 swap)
关键教训:小内存机器(本例仅 646M)用 make -j4 会在编译 fileinfo/libmagic 时 virtual memory exhausted: Cannot allocate memory。 先加临时 swap,再用 -j1 串行续编(已编译的对象会保留,中断后重跑 make -j1 即可续上)。
1
2
3
4
5
6
# 临时加 1G swap(编译兜底,稳定后可删)
fallocate -l 1G /swapfile2 || dd if=/dev/zero of=/swapfile2 bs=1M count=1024
chmod 600 /swapfile2 && mkswap /swapfile2 && swapon /swapfile2
# 串行编译
make -j1
看到 Don't forget to run 'make test'. 且无 error: 即成功。
2.5 安装到独立目录
1
2
3
make install
/opt/php74-patched/sbin/php-fpm -v # 期望 PHP 7.4.34-dev (fpm-fcgi)
/opt/php74-patched/bin/php -m | tr '\n' ' '
确认 opcache、pdo_sqlite、sqlite3、openssl、sodium、zlib、pcntl 等模块在列。
2.6 准备配置与 systemd 单元
/opt/php74-patched/etc/php.ini:
1
2
3
4
5
6
7
extension_dir=/opt/php74-patched/lib/php/extensions/no-debug-non-zts-20190902
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=0
memory_limit=-1
display_errors=Off
expose_php=Off
/opt/php74-patched/etc/fpm-prod.conf(复用系统现有 pool,不复制一份 pool,便于跟随原配置):
1
2
3
4
[global]
pid=/run/php/php7.4-fpm.pid
error_log=/var/log/php7.4-fpm.log
include=/etc/php/7.4/fpm/pool.d/*.conf
/etc/systemd/system/php74-patched-fpm.service:
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=PHP 7.4 patched FPM (php2shell backport 712e60)
After=network.target
[Service]
Type=simple
ExecStart=/opt/php74-patched/sbin/php-fpm --nodaemonize -y /opt/php74-patched/etc/fpm-prod.conf -c /opt/php74-patched/etc/php.ini
Restart=on-failure
[Install]
WantedBy=multi-user.target
先做配置语法检查(不启动):
1
2
3
4
/opt/php74-patched/sbin/php-fpm -t \
-y /opt/php74-patched/etc/fpm-prod.conf \
-c /opt/php74-patched/etc/php.ini
# 期望: configuration file ... test is successful
2.7 切换(停旧启新)
1
2
3
4
systemctl daemon-reload
systemctl stop php7.4-fpm
systemctl disable php7.4-fpm
systemctl enable --now php74-patched-fpm
nginx 的 fastcgi_pass unix:/run/php/php7.4-fpm.sock 无需改动,新 fpm 复用同一 socket。
2.8 验证
1
2
3
4
5
6
7
8
9
10
11
12
systemctl is-active php74-patched-fpm # active
ls -l /run/php/php7.4-fpm.sock # www-data www-data
/opt/php74-patched/sbin/php-fpm -v # 7.4.34-dev
# 站点回归(与切换前状态码一致即可)
curl -I http://127.0.0.1/
curl -I http://127.0.0.1:6008/
# 临时探针确认 fpm 实际版本,验完即删
printf '<?php header("Content-Type: text/plain"); echo "VER=".PHP_VERSION." opcache=".(function_exists("opcache_get_status")?"on":"off")."\\n";' > /var/www/html/__pc.php
curl -s http://127.0.0.1/__pc.php
rm -f /var/www/html/__pc.php
2.9 回滚
1
2
3
4
systemctl stop php74-patched-fpm
systemctl disable php74-patched-fpm
systemctl enable --now php7.4-fpm
systemctl is-active php7.4-fpm
2.10 收尾清理(可选)
1
2
3
# 删除编译源码与兜底 swap(确认稳定后)
rm -rf /root/phpsrc
swapoff /swapfile2 && rm -f /swapfile2
附:常见问题
- Q:为什么版本显示 7.4.34-dev 而不是 7.4.3?
A:backport 分支基于 7.4 开发线,仍是 7.4 大版本,不算升级;
-dev是分支快照标记。 - Q:
session.upload_progress.enabled在 php.ini 里仍显示 On,算修复了吗? A:算。backport 在代码层关闭了该攻击路径,ini 显示值不变;核心 RCE 修复在引擎(zend_alloc/zend_vm 状态重置)。 - Q:CLI 的
php变了吗? A:本方案只替换 web FPM,系统/usr/bin/php7.4命令行未动,不影响计划任务脚本。 - Q:这就一劳永逸了吗?
A:不是。仅修这两个 CVE,7.4 已 EOL,应规划迁移到受支持的 8.3/8.4;同时建议做常规加固(收敛
post_max_size、删除phpinfo.php/安装脚本、加防火墙白名单)。