AI 挖出的 PHP 漏洞,我花一小时在自己服务器上补好了

Web安全 · 10 分钟阅读· 137 次阅读

早上刷到一条新闻,心里咯噔一下:OpenAI 用 AI 在 PHP 引擎里挖出了一对潜伏十几年的远程代码执行漏洞,全球约七成网站中招,多国政府网站连夜关站避险。而我手头这台香港服务器,博客和游戏站全靠 PHP 撑着。

于是我花了一个多小时把它补掉了。过程里还翻了个大车,值得记下来。

PHP2Shell 应急封面

先说结论

  • 这次漏洞叫 PHP2Shell(CVE-2026-109618 / CVE-2026-109619,CVSS v4.0 双双 9.2),出问题的是 PHP 引擎底层的内存管理,和你的网站代码写得好不好没关系,默认配置就中
  • 修复只有一条路:升级 PHP。8.2 分支升到 8.2.35,10 月 21 日官方已发布
  • 我这台服务器(宝塔 13.1.0 + PHP 8.2.34)一刀全中;升级时宝塔脚本先删旧 PHP 再编译,第一次编译失败导致站点短暂全挂——这是本次最大的坑
  • 编译失败的原因是系统缺两个链接软链(libidn2、libiconv),补上软链重跑就过了;redis 扩展因为宝塔的扩展脚本机制失效,改成 pecl 源码手动编译
  • 最终全站恢复 200,PHP 8.2.35 + redis/zip/opcache 齐活

这次漏洞是什么:AI 挖出来的 PHP2Shell

先补一下新闻背景。10 月 21 日,OpenAI 发了篇博客:他们今年 6 月启动的「Patch the Planet」计划里,安全公司 Calif 用 GPT-5.6 Sol 在 PHP 核心代码里审计出了两个严重漏洞,谷歌旗下的 Wiz 也独立发现并验证了攻击路径——无需登录,直接远程执行代码,还能从内存里读密码和 API 密钥。

几个让这次事件不太一样的点:

角度 事实
漏洞位置 PHP 引擎底层的内存管理(内存耗尽时的哈希表计数错序 + 释放后使用),不在任何具体应用里
潜伏时间 出问题的代码可以追溯到 2012 年的 PHP 5.4.0,潜伏十几年
影响面 业界估计约 70% 的网站靠 PHP 跑,WordPress/Drupal/Joomla 全在船上
谁发现的 AI 深度参与:Calif 用 GPT-5.6 Sol,Wiz 用自家工具 + Gemini,双方独立发现
时效 补丁发布前一天漏洞信息泄露,黑客抢跑攻击,Cloudflare 在补丁上线前几小时就监测到了攻击流量

美国 NSA 的说法是这两个洞「通常提供一条直通服务器文件系统或数据库的路」。PHP 基金会的安全工程师对《华尔街日报》说,这是 PHP 十多年来遇到的最严重问题,堪比 2014 年的 Heartbleed。

攻击条件里有个细节和我直接相关:公开分析里,攻击者用不到 8MiB 的请求数据就能耗尽 PHP 默认 128MiB 的内存额度触发漏洞。而我两台…不对,我这台服务器的 memory_limit 正好就是默认的 128M。条件完全吻合。

我的服务器:一刀全中

登上去检查(杨杨.xyz 的博客和 www.yuibao.com 的游戏站其实都在同一台机器上,共用同一份 PHP):

检查项 结果 判定
PHP 版本 8.2.34(宝塔 13.1.0) ❌ 不在修复版内
memory_limit 128M(默认值) ❌ 正是攻击条件
Web 根 /www/wwwroot/ 两个站点 共用这份 PHP
第三方扩展 redis / zip / opcache 升级后要重点盯

结论很干脆:没有侥幸空间,升。直接上宝塔的官方升级脚本:

cd /www/server/panel/install
nohup bash install_soft.sh 0 install php 8.2 > /root/php82_upgrade.log 2>&1 &

跑之前我还特意确认了宝塔官方最新脚本里的目标版本:curl -s https://download.bt.cn/install/0/php.sh | grep '^php_82=',输出 8.2.35,和 PHP 官方补丁版一致。看起来万无一失。

翻车:宝塔先把旧 PHP 删了,然后编译失败了

二十多分钟后我去看日志,红了:

编译失败的终端输出

链接阶段挂了:/bin/ld: cannot find -lidn2、cannot find -liconv。库本身都在系统里,只是缺开发链接用的 .so 软链。

真正要命的是另一件事:宝塔这套脚本的流程是先清掉旧版本再编译。也就是说编译失败的这一刻,/www/server/php/82/bin/php 已经不存在了,php-fpm 也停了——两个站当场 502。

这里没有退路可选,只能往前修。缺什么补什么:

# libidn2 有运行库没开发软链
ln -sf /usr/lib64/libidn2.so.0 /usr/lib64/libidn2.so

# libiconv 是宝塔以前自己编译的,没进链接路径
echo '/usr/local/libiconv/lib' > /etc/ld.so.conf.d/bt-libiconv.conf
ldconfig

重跑安装脚本,这次 configure、make 一路绿到底,PHP 8.2.35 编译落位,php-fpm 拉起。站点从挂掉到恢复,前后四十来分钟——对一个小站来说能接受,但对流量敏感的站这个流程得想清楚再动手。

第二个坑:扩展没了,宝塔的扩展脚本也帮不上忙

版本升完不代表完事。检查 php -m,发现只剩 zip,redis 和 opcache 都没了——新编译的 PHP 是干净的,扩展得重装。

按老习惯走宝塔面板的「安装扩展」,结果发现面板的 phplib 扩展脚本机制对 8.2 已经失效(脚本 URL 拉下来是 0 字节)。不纠结,直接用服务器上现成的 8.2.35 源码树手动编 redis:

curl -sSL -o /tmp/redis-6.1.0.tgz https://pecl.php.net/get/redis-6.1.0.tgz
cd /tmp/redis-6.1.0
/www/server/php/82/bin/phpize
./configure --with-php-config=/www/server/php/82/bin/php-config
make -j$(nproc) && make install
echo 'extension = redis.so' >> /www/server/php/82/etc/php.ini

opcache 和 zip 其实已经随主编译生成好了(opcache.so、zip.so 就躺在扩展目录里),只是新的 php.ini 里没写加载行,补上:

zend_extension = opcache.so
opcache.enable = 1
opcache.jit = tracing
opcache.jit_buffer_size = 64M
extension = zip.so
extension = redis.so

重启 php-fpm,三个扩展齐活。顺带说一句,这次停机期间游戏站的玩家数据一点没慌——它的 redis 全是 function_exists 包着的降级设计,redis 不在就自动回落 MySQL,缓存断了功能不受影响。当初写代码时留的这个后手,今天值回票价。

验证

最后把该看的都看了一遍:

验证通过的终端输出

再用浏览器访问线上域名,各截一张真实页面:

修复后的博客首页

修复后的游戏站首页

版本 8.2.35、扩展齐、fpm 在跑、redis 监听 127.0.0.1、两个站 200——收工。

给同样在跑 PHP 站的人三句话

  1. 别赌「我的站小没人扫」。漏洞信息已经泄露,全网的扫描器都开着,AI 把「找到漏洞」和「写出利用」之间的成本打到了几乎为零。你唯一能做的就是尽快升版本。
  2. 升级前先想清楚失败路径。宝塔这套「先删旧再编译」的流程意味着编译失败 = 站点下线。动手前确认系统有编译所需的开发库软链,或者干脆低峰期做。
  3. 面板是双刃剑。它帮你省了编译配置的麻烦,但扩展脚本这种边角机制可能早就悄悄失效了。真出事的时候,phpize + php-config 手动编译这套基本功,比等面板修好更靠得住。

相关阅读

评论 (0)

还没有评论,来说两句吧~

发表评论

验证码

提交后需站长审核,通过后才会显示。