版本 v1.0|信息截至 2026-10-22(北京时间)|适用对象:自己管服务器、或用虚拟主机/托管 WordPress 的独立开发者、小团队、接外包的个人站长。

这份文档只做“自查与修复”,不包含、也不链接任何漏洞利用代码。漏洞信息每天都在变,动手前请先看第 7 节列出的官方来源有没有新的更新。


0. 先看这一段:30 分钟行动清单

如果你只有半小时,按顺序做这五件事:

  1. 查清你所有站点实际在跑的 PHP 版本(不是服务器默认版本,是每个站点、每个容器各自的版本)。方法见第 3 节。
  2. 对照修复版本:8.5 系列要 ≥ 8.5.12,8.4 ≥ 8.4.27,8.3 ≥ 8.3.36,8.2 ≥ 8.2.35。8.1 及更老的版本没有官方发布版,等同“未修复”。
  3. 能升就立刻升,升完重启所有 PHP 进程(PHP-FPM / Apache / 容器),再复查一次版本。
  4. 升不了的站,先下线或挡到 WAF 后面(Cloudflare 免费版已有针对性规则),并联系主机商。
  5. 从 10 月 17 日开始倒查日志和文件,看有没有被打过。漏洞公开是 21 日,但已有人观测到 17 日就出现了攻击尝试。方法见第 5 节。

一句话原则:更新 WordPress、Laravel、插件都不会更新 PHP 本身。 这次的洞在 PHP 运行时里,必须升级 PHP。


1. 发生了什么

2026 年 10 月 20 日,两个 PHP 核心漏洞的编号 CVE-2026-109618 和 CVE-2026-109619 在 Hacker News 和社交媒体上提前泄露,被称为 PHP2Shell(php2shell)。PHP 安全团队原计划 22 日发布修复,因泄露提前到 21 日,发布了 8.5.12、8.4.27、8.3.36、8.2.35 四个修复版本。两个漏洞的官方评分都是 CVSS v4.0 9.2(严重),都可以让未登录的远程攻击者在服务器上执行任意代码citation。

两个洞的原理用大白话说:

编号 俗称 发生了什么
CVE-2026-109618 OOM 内存损坏 PHP 用 memory_limit 限制内存,超限时会强行中断请求。如果中断发生在请求数据只初始化了一半的时候(比如正在解析表单文件上传),清理阶段会把这些“半成品”当成正常数据去释放,而这块内存里可能还残留着攻击者之前塞进去的数据。
CVE-2026-109619 残留全局状态(释放后使用) 某个请求的内部状态在内存已释放后还“活着”(比如发生致命错误之后),同一个 worker 处理的下一个请求可能在重置之前就用上了这块脏数据。官方还确认它能造成内存信息泄露。

来源:citation

为什么这次格外危险:

  • 影响面极广。 从 PHP 5.4 到 8.5.11 都受影响;PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed 所有对外提供网页的部署方式都在范围内。PHP 官方认为 mod_php 风险最高,因为演示的攻击在任何应用代码运行之前就能触发citation。
  • 已经被实际利用。 Amazon 威胁情报称 10 月 17 日(公开前 4 天)就在蜜罐上看到了攻击尝试,现在已是“大范围利用”;Cloudflare 和 watchTowr 也观测到攻击citation。
  • 老版本没人管。 10 月 20 日的统计显示,在 HTTP 头里暴露 PHP 版本的约 150 万台主机中,约 88.4 万台(59%)还是 8.1 或更老的版本citation。研究者称已在实验室对 WordPress、Drupal、Joomla、Magento 的特定版本实现了免登录代码执行。

另外提醒:网上流传的“PoC”很多未经验证,有些可能夹带恶意程序。不要下载、不要在自己服务器上跑来路不明的利用代码“试试看”。


2. 我受不受影响?按你的部署方式对号入座

你的情况 谁负责升级 PHP 你要做的事
虚拟主机 / cPanel / Plesk / 宝塔面板 主机商提供 PHP 包,但版本常由你在面板里选 每个站点单独检查(cPanel 看 MultiPHP Manager,Plesk 看每个订阅的 Hosting Settings),切到已修复的版本;没有修复版就发工单催
托管 WordPress(各类 Managed WP) 主机商 看主机商公告是否已推送修复;后台“站点健康”里能看到 PHP 版本
自己的 VPS / 云服务器(apt / yum 装的 PHP) 你自己 用发行版的包更新;注意发行版常“打补丁不改主版本号”,要看更新日志(见 3.3)
Docker / 容器部署 你自己(重建镜像) 拉取新基础镜像并重建、重新部署,只重启旧容器没用。官方 php 和 wordpress 镜像已带上修复版citation
PaaS / Serverless(各类托管运行时) 平台方 查平台安全公告,确认运行时已更新;必要时触发一次重新部署
用了 remi 源(rpms.remirepo.net) 你自己 该志愿者源 21 日当天没有更新,建议切回发行版官方包或自行编译citation
PHP 7.4 / 8.0 / 8.1 没有官方发布版 PHP 团队只提供了最小源码补丁给能自己编译的人;其余情况请直接升级到 8.2+,或找发行版 / LTS 供应商
PHP 5.4 – 7.3 没有任何修复 只能升级。这类站点今天就应该先下线

还有一类容易漏掉的:你没意识到自己在跑 PHP 的东西——NAS 管理界面、路由器/面板、某些商业软件后台、很多年前给客户搭的小站。荷兰 NCSC 在 GitHub 维护了一份“内嵌 PHP 的第三方软件”清单可以对照citation。

注意 8.2 的寿命:8.2 系列只在 2026 年 12 月 31 日前提供安全修复citation。这次既然要动,建议直接升到 8.3 或 8.4,省得两个月后再折腾一次。


3. 怎么查版本(三层,从外到内)

3.1 从外面看(不需要登录服务器)

curl -sI https://你的域名 | grep -i x-powered-by

如果返回 X-Powered-By: PHP/8.3.12 之类,就能看到对外暴露的版本。但这只是参考:很多站关掉了这个头,CDN 也可能把它去掉;而且发行版打过补丁的包,版本号可能看起来“旧”其实已修复。外部检测只能证明“有问题”,不能证明“没问题”。

3.2 在服务器上看(最准确)

php -v          # 命令行版本
php-fpm -v      # 网页实际使用的 FPM 版本(名字可能是 php-fpm8.3、php83-php-fpm 等)
php-cgi -v      # 如果用 CGI

命令行的 PHP 和网页用的 PHP 经常不是同一个。 务必查 FPM / Apache 模块那一个。实在找不到,可以临时放一个只有 <?php echo PHP_VERSION; 的文件到站点目录访问一下,看完马上删除(千万别留 phpinfo() 在线上)。

3.3 发行版“偷偷打补丁”的情况

Debian / Ubuntu / RHEL 系常把补丁打进旧版本号里,所以要看包的更新日志里有没有这次的 CVE 编号:

# Debian / Ubuntu(把包名换成你实际的,如 php8.2-fpm)
apt changelog php8.2-fpm 2>/dev/null | grep -m1 CVE-2026-109618

# RHEL / Rocky / Alma / CentOS Stream
rpm -q --changelog php-fpm | grep -m1 CVE-2026-109618

能搜到编号,说明这个包已包含修复;搜不到,说明还没更新或源里还没有。


4. 修复:怎么升、升完怎么验收

唯一完整的修复是升级 PHP。 PHP 核心开发者原话是“唯一的缓解措施就是打补丁 / 升级 PHP”citation。

常见环境的做法(示例,按你的实际包名调整):

# Debian / Ubuntu
sudo apt update && sudo apt install --only-upgrade 'php8.*'
sudo systemctl restart php8.3-fpm      # 以及 apache2 / nginx

# RHEL 系
sudo dnf update 'php*'
sudo systemctl restart php-fpm httpd

# Docker:改 Dockerfile 的基础镜像标签到修复版本,然后
docker compose build --pull && docker compose up -d

验收标准(全部满足才算修好):

  1. 网页实际使用的 PHP(FPM / 模块)版本 ≥ 对应修复版本,或包更新日志里能查到 CVE-2026-109618 / 109619;
  2. 所有 PHP worker 已重启(systemctl status 看启动时间晚于升级时间;容器看创建时间);
  3. 站点能正常打开、登录、上传文件(升级可能带来兼容问题,顺手测一下关键流程);
  4. 同一台机上每个站点都查过,不只是默认站点。

升不了的时候:临时措施(不等于修复)

PHP 官方列了几个能降低特定攻击路径风险的配置,但明确说做了也不代表安全citation:

  • 针对 109618:把 memory_limit 设为 -1,并且让应用无法覆盖(在 php.ini / FPM 池配置里用 php_admin_value);同时用容器或 cgroup 内存上限兜底,防止服务器被拖垮。
  • 针对 109619:session.upload_progress.enabled=0;PHP 8.5 还要 fatal_error_backtraces=0。
  • 改完必须重启所有 PHP worker。
  • 把站点挡到 Cloudflare 后面:Cloudflare 已为包括免费版在内的所有用户部署了针对已知攻击方式的 WAF 规则,但它自己也说这只是权宜之计,而且只保护经过 Cloudflare 的流量(源站 IP 暴露的话等于没挡)。
  • 实在不行,先下线。CERT-EU 的建议就是把无法修复的公网应用先隔离citation。

关于 session.upload_progress 有争议:早期很多文章把它当“根源”,但这一说法在补丁发布前并未得到 PHP 官方确认citation。按官方公告来,把它当“减少一条路径”,不要当“盾牌”。


5. 我是不是已经被打了?(从 10 月 17 日开始查)

Amazon 建议调查起点至少放在 10 月 17 日,而不是公开日citation。重要提醒:如果怀疑已被入侵,先保留证据(日志、可疑文件拷贝)再升级,否则重启后很多线索就没了。

5.1 看日志里的异常信号

这两个洞都和内存耗尽、worker 崩溃有关,所以重点找这几类记录:

grep -hE "Allowed memory size|exited on signal (11|6)|SIGSEGV|SIGABRT" \
  /var/log/php*fpm*.log /var/log/apache2/error.log /var/log/httpd/error_log \
  /var/log/nginx/error.log 2>/dev/null | tail -n 100

journalctl --since 2026-10-17 2>/dev/null | grep -E "php.*(SIGSEGV|signal 11|Allowed memory size)" | tail -n 50

10 月 17 日之后,如果突然出现一批平时没有的内存耗尽或段错误,尤其集中在对上传接口、首页的 POST 请求上,就要警惕。

5.2 看有没有多出来的 PHP 文件(网页后门)

# 10 月 17 日之后新增或被改过的 PHP 文件
find /var/www -type f -name '*.php' -newermt 2026-10-17 2>/dev/null | head -n 200

# 上传目录里本不该有 PHP 文件
find /var/www -type f -path '*upload*' -name '*.php*' 2>/dev/null

# 常见后门特征(会有误报,逐个人工看)
grep -rlE "eval\(base64_decode|gzinflate\(base64_decode|assert\(\\\$_(POST|GET|REQUEST)" \
  /var/www --include='*.php' 2>/dev/null | head -n 50

5.3 看持久化手段

  • 定时任务:crontab -l、sudo crontab -u www-data -l(或 apache / nginx 用户)、ls -la /etc/cron.d /etc/cron.hourly
  • CMS 后台有没有多出管理员账号(WordPress 看“用户”列表,按注册时间排序)
  • 服务器上有没有陌生进程、陌生的 SSH 公钥(~/.ssh/authorized_keys)

5.4 发现可疑怎么办

一旦确认被入侵,“删掉后门文件”远远不够。按这个顺序:保留证据 → 站点下线 → 从干净的备份或镜像重建 → 升级 PHP → 轮换所有密钥(数据库密码、.env 里的 API Key、云服务 AccessKey、支付密钥、WordPress 的 salts)→ 重新上线后持续观察日志。同一台机器上的其他站点也要视为可能被波及。


6. 借这次机会做一遍的基础加固

这次的洞再严重,也只是 PHP 安全问题里的一个。下面这些平时就该做,顺手一起做掉:

运行时层面

  • 关掉版本暴露:php.ini 里 expose_php = Off,让扫描器没那么容易盯上你。
  • 不再用任何 EOL 版本(目前 8.1 及以下);记下 8.2 会在 2026 年底停止安全支持。
  • 上传目录禁止执行 PHP(Nginx 在 uploads 路径 location 里拒绝 .php;Apache 用 .htaccess 或配置禁用)。
  • 生产环境 display_errors = Off,错误只写日志。
  • 一台机器跑多个客户站时,每个站用独立的 FPM 池和系统用户,一个站被打穿不至于全军覆没。

应用层面

  • 有 composer.lock 的项目跑一下 composer audit(Composer 2.4+ 自带),它会对照公开漏洞库列出有问题的依赖。
  • WordPress:删掉不用的插件和主题(停用≠安全,文件还在就可能被利用),剩下的保持更新;注意 2026 年 7 月 WordPress 自身也出过一对“wp2shell”漏洞(CVE-2026-63030、CVE-2026-60137),和这次 PHP2Shell 是两回事,各修各的citation。
  • .env、.git、备份压缩包、phpinfo.php 不要放在网站可访问的目录里。可以用 curl -sI https://你的域名/.env 检查,应返回 403/404。

习惯层面

  • 给自己建一张“我在跑的东西”清单:域名、服务器、PHP 版本、部署方式、谁负责升级。这次最耽误时间的往往不是升级本身,而是想不起来自己到底有几个站。
  • 订阅下面第 7 节的一两个来源,严重漏洞出来时能第一时间知道。

7. 去哪里看实时更新

漏洞信息变化很快,这份文档只是 10 月 22 日的快照。以下来源按可信度排序:

来源 看什么
php.net 首页新闻 / PHP 安全公告(GitHub) 一手信息:修复版本、官方认可的缓解措施
你的发行版安全公告(Debian DSA、Ubuntu USN、Red Hat RHSA) 你的包什么时候修好、修在哪个包版本
CERT-EU、加拿大网络安全中心、CISA、英国 NCSC 政府级预警,信息克制、准确
CISA KEV(已被利用漏洞目录) 某个漏洞是否已确认被在野利用。PHP 历史上进过 KEV 的有 CVE-2024-4577(PHP-CGI)、CVE-2019-11043(PHP-FPM)等,两者都被勒索软件用过
Tenable FAQ、Unit 42、watchTowr、Wiz 等安全厂商博客 利用情况、攻击时间线,更新最勤
NCSC-NL php2shell 仓库 哪些第三方软件内嵌了受影响的 PHP

附录 A:一键只读自查脚本

下面这个脚本只读不改:查版本、查补丁、查关键配置、扫日志和可疑文件。把输出整段贴给配套的「PHP 安全自查助手」Skill,它会帮你逐项判断并给出修复步骤。

说明:脚本基于常见的 Debian/Ubuntu/RHEL 路径编写,尚未在所有环境中实测;个别命令在你的系统上不存在时会静默跳过。请用有 sudo 权限的账号运行,以便读取日志。网站目录不是 /var/www 的,改第一行的 WEBROOT。

#!/usr/bin/env bash
# php-selfcheck.sh — PHP 安全只读自查(PHP2Shell 版,2026-10)
WEBROOT="${WEBROOT:-/var/www}"
SINCE="2026-10-17"

echo "===== 0. 系统 ====="
uname -a; cat /etc/os-release 2>/dev/null | head -n 3

echo; echo "===== 1. 本机所有 PHP 可执行文件及版本 ====="
find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /opt -maxdepth 6 \
  \( -name 'php' -o -name 'php[0-9]*' -o -name 'php-fpm*' -o -name 'php-cgi*' \) 2>/dev/null \
  | sort -u | while read -r b; do
    [ -x "$b" ] && [ ! -d "$b" ] && echo "$b -> $("$b" -v 2>/dev/null | head -n1)"
  done

echo; echo "===== 2. 正在运行的 PHP 进程 ====="
ps -eo pid,lstart,cmd | grep -E "php|httpd|apache2|frankenphp|lsphp" | grep -v grep | head -n 20

echo; echo "===== 3. 发行版包与补丁日志 ====="
if command -v dpkg >/dev/null; then
  dpkg -l | awk '/^ii/ && ($2 ~ /^php[0-9.]*-(fpm|cli|cgi)$/ || $2 ~ /libapache2-mod-php/) {print $2, $3}' | while read -r p v; do
    hit=$(apt changelog "$p" 2>/dev/null | grep -m1 -oE "CVE-2026-10961[89]")
    echo "$p $v  补丁记录: ${hit:-未找到}"
  done
fi
if command -v rpm >/dev/null; then
  rpm -qa | grep -E '^php' | sort | while read -r p; do
    hit=$(rpm -q --changelog "$p" 2>/dev/null | grep -m1 -oE "CVE-2026-10961[89]")
    echo "$p  补丁记录: ${hit:-未找到}"
  done
fi

echo; echo "===== 4. 关键配置(命令行 PHP 视角,FPM 可能不同)====="
php -r 'foreach(["memory_limit","post_max_size","session.upload_progress.enabled","fatal_error_backtraces","expose_php","display_errors"] as $k){echo $k,"=",var_export(ini_get($k),true),PHP_EOL;}' 2>/dev/null
grep -rhE "^\s*php_(admin_)?(value|flag)\[(memory_limit|session.upload_progress.enabled)\]" /etc/php* /etc/opt 2>/dev/null | head

echo; echo "===== 5. Docker 容器内 PHP 版本 ====="
if command -v docker >/dev/null; then
  docker ps --format '{{.Names}} {{.Image}} {{.CreatedAt}}' 2>/dev/null | while read -r n rest; do
    echo "$n ($rest) -> $(docker exec "$n" php -v 2>/dev/null | head -n1)"
  done
fi

echo; echo "===== 6. 日志异常($SINCE 起)====="
grep -hE "Allowed memory size|exited on signal (11|6)|SIGSEGV|SIGABRT" \
  /var/log/php*fpm*.log /var/log/php*/*.log /var/log/apache2/error.log* /var/log/httpd/error_log* \
  /var/log/nginx/error.log* 2>/dev/null | tail -n 60
journalctl --since "$SINCE" 2>/dev/null | grep -E "php.*(SIGSEGV|signal 11|signal 6|Allowed memory size)" | tail -n 30

echo; echo "===== 7. 可疑文件 ====="
echo "-- $SINCE 后新增/修改的 PHP 文件(前 100 个)"
find "$WEBROOT" -type f -name '*.php' -newermt "$SINCE" 2>/dev/null | head -n 100
echo "-- 上传目录中的 PHP 文件"
find "$WEBROOT" -type f -path '*upload*' -name '*.php*' 2>/dev/null | head -n 50
echo "-- 后门特征命中(需人工复核)"
grep -rlE 'eval\(base64_decode|gzinflate\(base64_decode|assert\(\$_(POST|GET|REQUEST)' \
  "$WEBROOT" --include='*.php' 2>/dev/null | head -n 50

echo; echo "===== 8. 定时任务 ====="
crontab -l 2>/dev/null
for u in www-data apache nginx nobody; do echo "-- $u"; crontab -u "$u" -l 2>/dev/null; done
ls -la /etc/cron.d 2>/dev/null

echo; echo "===== 9. 敏感文件暴露(本地检查是否在网站目录)====="
find "$WEBROOT" -maxdepth 4 \( -name '.env' -o -name '.git' -o -name 'phpinfo.php' -o -name '*.sql' -o -name '*.zip' -o -name '*.tar.gz' \) 2>/dev/null | head -n 50

echo; echo "===== 完成。请把以上全部输出贴给「PHP 安全自查助手」。注意先删掉里面的密码等敏感信息。====="

附录 B:打印版检查清单

  • 列出所有站点 / 服务器 / 容器,以及各自的 PHP 版本和负责人
  • 每个对外站点的 PHP 版本 ≥ 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35,或包日志可查到 CVE 编号
  • 8.1 及以下的站点已升级或已下线
  • 升级后所有 PHP worker 和容器已重启 / 重建
  • 升不了的站已挡在 WAF 后,且源站 IP 不对外
  • 已从 10 月 17 日起检查日志中的内存耗尽 / 段错误
  • 已检查新增 PHP 文件、上传目录 PHP 文件、可疑定时任务、陌生管理员账号
  • 如有入侵迹象:已保留证据、重建、轮换全部密钥
  • expose_php = Off、display_errors = Off、上传目录禁止执行 PHP
  • 跑过 composer audit;WordPress 已清理无用插件主题
  • 已订阅至少一个官方安全公告来源