夜雨聆风学习资料网

ARTICLE · 1233173

AI 让一个存了 20 年的 PHP 漏洞,变成了能打的漏洞

AI 让一个存了 20 年的 PHP 漏洞,变成了能打的漏洞
10 月 21 日,PHP 官方发了紧急安全版本,修两个 CVSS 评分 9.2 的漏洞,未登录的攻击者能远程执行代码。

第一个要命的地方是位置。洞在 PHP 的核心引擎里,不在你的业务代码里。官方信息站写得很直接,一个只有 Hello World 的应用,跑在受影响的 PHP 版本上,一样中招。

第二个要命的地方是默认配置。Apache 配 mod_php 的情况下,一个未登录请求就能在任何应用代码运行之前触发,官方确认默认配置下可以做到完整的远程代码执行。

第三个是范围。PHP 支撑全球约 70% 的网站,WordPress、Drupal、Joomla、维基百科背后的 MediaWiki 全在里面。第三方安全机构 TechTarget 的估算是有 2200 万台暴露服务器面临风险。这个洞的 PoC 在公开当天就有人放了出来,三个仓库累计三千多星,也就是说这事已经不是理论风险了。

研究方在真实容器镜像上验证过的应用清单很长。WordPress 7.1.2、Drupal 11.4.3、Joomla 6.1.4、MediaWiki 1.46.2、Roundcube 1.7.4、Magento 2.4.9、phpMyAdmin 5.2.3、Nextcloud 35.0.1、Laravel 13.11、Symfony 8.1、WHMCS。列这些名字就一个目的,说明洞在引擎里,你的框架再新也挡不住。

两个洞的机理说人话。第一个是内存耗尽的时候,PHP 可能留下一半没初始化完的内部数据,后面又把它当成有效数据用,等于自己把内存搞脏了。第二个是致命错误发生在错误的时点,留下的状态指向了上一个请求的内存,同一个 worker 处理下一个请求时就用到了那块已经释放的内存。两个都长在 PHP 几乎每个请求都会碰到的地方,内存管理和错误处理。

补丁的规模能说明问题,Zend 引擎改了 300 个文件。

下面五步,按顺序做。

第一步,查你在不在名单里

版本对照,低于修复版的就是受影响。

分支
受影响
修复版
8.5
低于 8.5.12
8.5.12
8.4
低于 8.4.27
8.4.27
8.3
低于 8.3.36
8.3.36
8.2
低于 8.2.35
8.2.35
8.1 及更早
受影响,官方已停止常规支持
仅最小化源码回溯补丁(7.4 / 8.0 / 8.1)

四条查法,按可靠性排。命令行跑 php -v 看版本号。用官方那个检查工具,openai/php2shell-checker,能查 URL 也能查本地二进制。用包管理器,apt list --installed | grep php 或者 brew list --versions php。看容器镜像的 tag。

官方那个检查工具有四条限制,不说清楚会给你假的安全感。它只读 HTTP 响应头里的 X-Powered-By 和 Server,或者本地 php -v 的输出。它不跟随重定向,不看响应体。头部被隐藏或改写的时候它返回 UNKNOWN,而 UNKNOWN 不等于安全。它给的 OK 也只是版本号比大小的结论,本身不验证补丁是不是真的生效了。

还有个时间点要提醒。PHP 8.2 的官方支持在 2026 年 12 月 31 日结束,也就是这个分支打完这最后一个安全补丁,两个月后就彻底不修了。还在 8.2 上的项目,这次升完得顺手排一下升级计划。

第二步,升级,然后重启

重启这一条很多人会漏。只换文件不重启,旧进程还在跑旧代码,等于没修。官方明确要求重启 Apache、PHP-FPM 以及任何长驻的 PHP worker 进程。

不同运行方式的风险不一样,对运维有用。

运行方式
未登录 RCE
关键条件
Apache mod_php
是
默认配置即可,请求启动阶段就触发
PHP-FPM
是
需要应用层存在可达的解码路径
php-cgi / FrankenPHP / LiteSpeed
官方列为在风险范围
取决于具体配置

一句话,跑 mod_php 的最危险,跑 FPM 的相对好一些但不是免疫。

第三步,升不了级就只能买时间

前提先说死。官方原话是不存在完整的缓解措施,升级是唯一安全的选择。下面这些手段只能拖时间,不能替代补丁。

针对内存耗尽那一条,可以用 php_admin_value 把 memory_limit 设成 -1,让它永远不触顶。但代价是失去内存保护,恶意请求可能直接把你的进程打到内存耗尽。同理还要压低 post_max_size,不上传文件的环境设 8M,要上传的设 32M,并且保证 upload_max_filesize 小于它。在 Web 服务器或反向代理那一层限制解码后的请求大小、字段数量、multipart part 数量、header 数量和嵌套深度。用容器或 cgroup 限制内存。不要用 RLIMIT_AS 或 RLIMIT_DATA 去限。

针对跨请求残留状态那一条,把 session.upload_progress.enabled 设成 0,并且重启所有 worker,因为运行时用 ini_set 或 .user.ini 改是不生效的。8.5 上再设 fatal_error_backtraces=0。顺手清掉那些无效或已废弃的 per-request 设置,它们会额外制造致命错误点。最后一条最容易漏,把攻击者能触发的致命错误和执行超时都修掉。

第四步,加检测,顺手挡一个字段

社区已经把检测规则开源了,四套覆盖主流工具链。Suricata 和 Snort 有 13 条,包含请求、响应、侦察三类。Sigma 有 5 条,覆盖 Web 日志、进程创建、文件事件。ModSecurity v2 和 v3 有 7 条,Apache 和 Nginx 可以直接挂。还有一个 OpenResty 的 Lua 模块,适合放在反向代理层。

最省事的一条临时拦截,是在 multipart POST body 里挡掉 PHP_SESSION_UPLOAD_PROGRESS。正常业务几乎不用这个字段,副作用很小,但它盖住了两个漏洞的主要入口。

这不是局部事件。日本 IPA 在 10 月 22 日发了公告,斯洛文尼亚 SI-CERT、阿尔巴尼亚 AKSK 都发了预警,国内也有安全厂商在发通告。华尔街日报和纽约时报都写了。

第五步,按凭据可能已泄露来处置

这一段最容易被忽略。这个洞除了远程执行代码,还有内存泄露。官方说法是可能读到其他请求或者服务器自身的密码、登录令牌、API key 还有证书。

也就是说,一台暴露在公网、跑过受影响版本的机器,即使你没发现异常,也该假设凭据可能已经被读走了。补完洞之后应该按这个假设做轮换,数据库密码、API key、证书、会话密钥,一个都别省。

到这里是「该做什么」。下面说「为什么这件事值得你多想五分钟」。

这个洞在代码里躺了十几年

它不是新出现的。2020 年就有人报过相关线索,编号是 PHP 的 bug #78876,描述是某些长变量会导致内存耗尽、临时文件没有被清理。当时没多少人当回事。

2024 年,事情曾经走到过一个很接近的节点。PHP 基金会用 Sovereign Tech Agency 的资金,通过 OSTIF 组织了 PHP 多年来第一次外部安全审计,执行方是 Quarkslab。一次正经的、有资金支持的、由外部机构执行的审计,在 PHP 这个量级的项目上并不多见。那次审计重新发现了同一个内存耗尽的问题。

结果是,因为无法证明它可利用,这个 bug 没有被修复。

这次的研究方是在看一个旧补丁时撞见这条线索的。那个 2020 年的补丁在 git 里还带着 security-audit-2024 的标签,等于把两年前的判断过程留在了代码历史里。

这一段值得停一下。一个可以在正式审计里被「证明不了能打」而搁置的问题,两年后变成了现实威胁。「无法证明可利用」曾经是一个可以接受的风险处置理由。这个理由现在失效了。

AI 到底做了什么,别被流行说法骗了

现在的流行说法是「AI 发现了 PHP 的严重漏洞」。这个说法既高估了 AI,也低估了人。

真实的分工是这样的。发现方是 Wiz Research 的四个人,加上 OpenAI Codex Security 的 Amy Burnett,加上 Calif 的 Ryan Austin。催化这件事的是 OpenAI 的 Patch the Planet 计划,7 月 30 日把 Calif 拉了进来,PHP 基金会的 Volker Dusch 负责牵线。

Wiz 官方博客有一句话,是整件事最准确的表述。翻译过来是,这些底层缺陷已经存在了几十年,但要证明它们可利用,需要一条极其复杂的攻击链,接近人类研究者实际能达到的上限。前沿 AI 模型大幅降低了这个门槛,把一个长期存在的缺陷变成了可用的漏洞利用。

注意这句话的重心。Wiz 说的不是「AI 找到了漏洞」,说的是「AI 降低了证明可利用的门槛」。

而 Austin 在自己的技术报告里还专门泼了一盆冷水,这一节叫「我们是怎么找到这个的」。他说初始发现没有用任何特殊工具,也没有用普通人拿不到的模型。他选了一个自己有背景、熟悉漏洞历史、并且带着明确假设的方向。他原话的意思很直接,没有哪个模型能靠一句提示词挖出所有 bug,至少现在还没有。

他还提到 LLM 的一个行为特征,特别值得记。模型的默认变体分析会倾向于从最新、最显眼的 bug 往邻近的旧代码走,不会自觉地回到二十年前的冷门利用原语。所以研究者自己脑子里那张「目标的漏洞历史图」依然关键,它决定了你能把模型往哪个方向推。

还有一个技术细节,我觉得是这件事里最值钱的部分。Austin 在发现第一个洞之后,让模型把失败模式泛化成了一个技能文件,写的是这样一条链,发布或销毁状态,执行可能失败的操作,然后初始化或提交或恢复;如果失败的操作里发生了非局部致命错误,提交就被跳过,下游的消费者于是相信了那个错误状态。这个技能文件后来带来了第二个更严重的发现。

所以准确的说法分三段。漏洞的存在,2024 年的审计就看见了。漏洞的可利用性,人类团队当时证明不了,所以没修。2026 年,人给方向,AI 给耐心,把「证明不了」推进成了「有可用的 PoC」。AI 在这里真正做的事,是把一个洞变成了一类洞。

这件事对你的长期影响

你依赖的开源基础设施里,可能躺着一批「已知但证明不了」的洞。过去它们被归到低优先级,理由是没有证据表明能打。现在这个分类标准不稳了。

PHP 基金会在公告里还说了一件事,他们正在加强 PHP 的安全建设,包括把生态安全团队做实、把漏洞报告流程理顺。一个开源基础设施被打疼一次之后怎么调整自己,这部分通常不会上新闻,但它决定了下一个洞还会不会在代码里躺十几年。

攻方的成本在降,守方的审计成本也在降。这是一场不对称的军备竞赛,但至少这一轮,白帽跑在了前面。从 8 月 11 日上报到 10 月 21 日补丁上线,只用了两个多月。社区同步出了四套检测规则,OpenAI 还写了检查工具,12 家厂商在同一天发公告。

收尾

这一轮防守方赢了。发现方是白帽,走的是协调披露,PHP 团队响应极快。Wiz 的公告里还专门感谢了 PHP 团队的反应速度和专业程度。

但同一套能力,攻守两边都拿到了。下一次,先动手的未必是白帽。

现在你能做的只有两件事。查版本,打补丁,重启。以及从今天起,别再拿「证明不了它能被打」当作不修的理由。

💬 互动话题

你手上还有跑着旧版本 PHP 的机器吗?这次的补丁你敢直接上生产,还是先灰度?评论区聊聊你们的应急流程。

👍 如果觉得有用

欢迎点赞、收藏、转发三连。

我是Hank, 我们下次再见。

相关学习资料