近日,PHP 官方安全团队与多家安全研究机构(Calif、Wiz Research 等)联合披露了两个存在于 PHP 核心运行时的严重远程代码执行(RCE)漏洞,漏洞代号被社区命名为 PHP2Shell(对应编号 CVE-2026-109618 与 CVE-2026-109619,CVSS v4.0 评分高达 9.2)。
该漏洞波及从 PHP 5.4 到 PHP 8.5 跨度十余年的引擎代码。最为致命的是,该漏洞在 Web 应用代码执行之前即可被触发,即便你的业务代码只写了一句 <?php echo "OK"; ?>,只要底层 PHP 引擎未修复,攻击者便有可能通过精心构造的 HTTP 请求获取服务器控制权限。
一、 漏洞简述
- 漏洞名称:PHP2Shell
- CVE 编号:CVE-2026-109618(OOM 内存破坏致 RCE)、CVE-2026-109619(残留全局状态悬挂致 RCE)
- 严重程度:Critical(CVSS 9.2)
- 攻击前置:无需任何身份凭证(Unauthenticated)、远程可达
- 发现机制:由安全研究员借助新一代 AI 大模型与代码审计工具联合挖掘,属于潜藏在底层 runtime 历史代码中的内存安全问题。
二、 漏洞成因
通常我们处理 Web 漏洞(如 SQL 注入、反序列化、文件包含)时,注意力都集中在业务应用层。但 PHP2Shell 的缺陷位于 PHP 解释器底层(C 语言实现的核心逻辑)。
1. 请求初始化与 OOM 异常退出机制缺陷(CVE-2026-109618)
PHP 在接收并解析 HTTP POST 请求(特别是 multipart/form-data 数据流)时,底层会按块(chunk)分配堆内存。
- 当请求体触发内存上限(
memory_limit)导致 OOM(Out of Memory)时,PHP 引擎会通过 C 语言底层的longjmp()机制强制打断当前调用栈,直接跳转至顶层的错误处理例程。 - 缺陷在于:部分上下文状态在内存分配完成前就被标记为“已就绪”,异常跳出后内部状态处于未完全初始化的“半吊子”状态。随后的请求清理函数(cleanup)错误地读取或分支跳转至未初始化指针,造成堆内存破坏(Memory Corruption / UAF)。
2. 残留脏状态跨请求污染(CVE-2026-109619)
在 PHP-FPM 等多进程/长驻进程模型中,Worker 进程在处理完一次请求后会重置资源并等待下一个请求。
- 当
longjmp()中断发生时,某些全局管理结构指向的请求堆内存在释放后,指针并未被彻底清零。 - 下一个由该 Worker 处理的请求在初始化重置完成前,可能直接使用这些悬挂指针(Dangling Pointer)或陈旧状态,攻击者借此实现内存信息泄漏(如泄露内存基址绕过 ASLR)或在受控地址写入数据,最终达成远程代码执行(RCE)。
三、 影响范围与版本矩阵
几乎所有对外提供 HTTP 服务的 PHP 架构(包括 PHP-FPM、Apache mod_php、FrankenPHP、php-cgi 等)均受影响:
| 主分支系列 | 受影响版本 | 官方修复版本 | 状态说明 |
|---|---|---|---|
| PHP 8.5 | 8.5.0 ~ 8.5.11 | 8.5.12 | 当前主线稳定版 |
| PHP 8.4 | 8.4.0 ~ 8.4.26 | 8.4.27 | 官方维护分支 |
| PHP 8.3 | 8.3.0 ~ 8.3.35 | 8.3.36 | 官方维护分支 |
| PHP 8.2 | 8.2.0 ~ 8.2.34 | 8.2.35 | 官方维护分支 |
| PHP 7.4 / 8.0 / 8.1 | 所有版本 | 需打 Backport 补丁 | 已停止官方维护(EOL),仅提供源码补丁 |
| PHP 5.4 ~ 7.3 | 所有版本 | 无官方补丁 | 彻底 EOL,强烈建议迁移重构 |
四、 修复与应急排查方案
针对 PHP2Shell 漏洞,请务必注意:不存在配置层面的“完整缓解方案”,升级是唯一的根本出路!
1. 唯一彻底的方案:升级至安全版本
步骤 1:对照版本矩阵,升级至安全版本
根据当前所运行的主版本,通过操作系统包管理器(APT/YUM)、源码编译或官方更新通道,将 PHP 升级到对应的修复版本(8.2.35、8.3.36、8.4.27 或 8.5.12):
步骤 2:升级后重启 PHP-FPM 或 Apache,确保新二进制生效
仅仅更新系统包并不能保护正在运行的业务!未重启的服务依然加载驻留在内存中的旧版漏洞二进制,依然会遭到利用。
步骤 3:验证方式
在生产环境必须确认实际加载的二进制已经更新:
- CLI 命令行检测:
php -v - Web 运行时检测:
在测试目录放置一个包含
<?php phpinfo(); ?>的临时文件,在浏览器或使用curl访问,查看输出的 PHP Version 是否为修复后的版本号(如8.3.36)。确认后立即删除该探针脚本。
2. 如果业务受限暂时无法升级
如果由于旧业务依赖、变更窗口限制,短时间内确实无法升级 PHP,必须采取以下多重纵深手段实施临时缓解(官方与安全团队明确指出,以下措施仅降低被利用概率,不能代替升级补丁):
-
禁用
session.upload_progress.enabled: 已公开的概念验证(PoC)多依赖 PHP 内置的 session 上传进度追踪机制来构造内存状态。- 修改
php.ini(所有运行中的 FPM pool 及全局配置):session.upload_progress.enabled = Off - 修改后必须重启 PHP-FPM/Apache 服务使之生效。
- 注意:官方明确表示,该项仅能阻断依赖该路径的已知 PoC 链,无法根除底层的 OOM/悬挂指针缺陷,绝非完整防御手段。
- 修改
-
在反向代理 / WAF 层限制 multipart 请求:
- 在 Nginx、Traefik、Cloudflare 或各类企业级 WAF 上,对全站 URI 默认拦截
Content-Type: multipart/form-data。 - 仅在业务明确需要文件上传功能的特定 API / URL 端点进行放行,并在入口网关限制上传请求体的大小(
client_max_body_size)。
- 在 Nginx、Traefik、Cloudflare 或各类企业级 WAF 上,对全站 URI 默认拦截
-
网络层隔离对外暴露面:
- 对于后台管理系统、内网工具、尚未打补丁的核心服务,临时收敛端口,配置防火墙或网关白名单,仅允许内网 VPN / 堡垒机受信任 IP 访问。
3. 针对宝塔(BT-Panel)等运维面板用户的实操指南
国内大量中小站长与企业使用宝塔等集成面板,针对面板环境可按以下步骤操作:
方案 A:通过软件商店升级(首选)
- 登录宝塔面板后台,进入 【软件商店】 -> 【已安装】。
- 找到当前站点绑定的 PHP 版本(如 PHP 8.2 / 8.3),点击其对应的 【版本更新】/【检查更新】。
- 若面板官方已推送最新编译补丁,直接点击更新,并在更新完成后点击 【重启】 服务。
方案 B:面板暂未推送补丁时的应对
- 版本迁移:在软件商店中安装已被官方修复的更高版本 PHP(例如将 PHP 8.1 切换为已经打上补丁的 PHP 8.3/8.4),然后在 【网站设置】 中将站点的运行 PHP 版本一键切换。
- 手动编译(高级):下载官方打入补丁的源码,在面板的编译扩展目录手动替换并编译安装。
方案 C:面板环境临时缓解设置
在等待更新期间,立即通过面板关闭 upload_progress:
- 打开 【软件商店】 -> 找到正在运行的 PHP 版本 -> 点击 【设置】。
- 切换到 【配置文件】(即
php.ini编辑器)。 - 使用
Ctrl + F搜索session.upload_progress.enabled。 - 将该行修改为:
session.upload_progress.enabled = Off - 点击保存后,切换到 【服务管理】 菜单,点击“重启” PHP 服务。
五、 总结与安全反思
PHP2Shell(CVE-2026-109618/109619)展现了经典脚本语言底层代码在当下安全对抗中的脆弱性:即便业务代码写得无可挑剔,底层的 C 语言内存管理失误也会被利用为通向 Webshell 的捷径。
对于运维与安全团队而言,面对此种级别的核心组件 0-day / N-day 威胁,唯有快速建立起“资产版本秒级盘点 -> 网关层针对性拦截 -> 核心运行时无缝滚动打补丁”的防御闭环,才能在自动化扫描器与恶意利用工具到来前化险为夷。各团队应立即行动,排查资产并执行 PHP 版本升级!