讲解一下这次PHP核弹级别漏洞的原理

讲解一下这次PHP核弹级别漏洞的原理

💡 原文中文,约6300字,阅读约需15分钟。
📝

内容提要

文章解析PHP高危漏洞CVE-2026-109619(RCE)原理:利用session.upload_progress在RINIT前初始化session;请求A触发OOM,经zend_bailout跳过清理,留下悬空指针;请求B用可控字段回收该内存,形成UAF;再通过信息泄露绕过ASLR,伪造HashTable并将pDestructor指向zif_system实现命令执行。攻击全程纯HTTP、早于应用代码执行、默认开启且无日志。建议升级PHP或关闭session.upload_progress.enabled。

🔎

延伸解读

漏洞利用为何能绕过常规防护

该漏洞的利用发生在PHP解析HTTP请求体阶段,早于应用代码执行。因此,路由、中间件、WAF规则乃至ini_set()都来不及生效。攻击请求就是普通的HTTP POST,无需认证,且session.upload_progress默认开启,session.use_only_cookies=1也无法阻止攻击者将PHPSESSID放在表单字段中。整个过程HTTP返回200,worker不崩溃,应用日志无异常,极具隐蔽性。

从UAF到RCE的关键步骤

攻击者先通过请求A触发OOM,利用zend_bailout跳过清理,留下悬空指针;请求B用可控字段回收内存,形成UAF。随后通过信息泄露获取堆基址和libphp加载基址,绕过ASLR;再伪造HashTable,将pDestructor指向zif_system,最终在析构时执行命令。全程纯HTTP,无需内存破坏即可完成。

另一种更稳定的攻击变体

除了内存破坏路径,攻击者还可将恶意序列化对象写入session文件。upload_progress在RINIT前读取session时会反序列化该对象,触发__destruct()。若目标应用存在可接力到system()的魔术方法链(如WordPress、Laravel等生态常见),即可实现RCE。此变体不涉及内存布局,稳定性极高。

缓解措施与局限性

根本解决需升级PHP。临时缓解可关闭session.upload_progress.enabled(需在php.ini设置,不能通过ini_set),但这不能解决OOM可能带来的DDOS攻击。极端方法如设置pm.max_requests=1或MaxRequestsPerChild 1可让每个Worker只处理一个请求,但代价过高,需谨慎权衡。

❓

Q&A

CVE-2026-109619 漏洞的完整攻击链是怎样的?

攻击分四步:1) 请求A利用 session.upload_progress 在 RINIT 前初始化 session,然后触发 OOM 导致 zend_bailout 跳过清理,留下悬空指针;2) 请求B用可控字段回收该内存,形成 UAF;3) 通过信息泄露获取堆基址和 libphp 基址,绕过 ASLR;4) 伪造 HashTable 并将 pDestructor 指向 zif_system,在请求B的清理阶段触发命令执行。

为什么这个 PHP 漏洞被称为“核弹级别”?

因为它无需认证、纯 HTTP 请求即可触发,且攻击发生在应用代码执行之前,绕过路由、中间件、WAF 和 ini_set;入口 session.upload_progress 默认开启,无需应用调用 session_start();全程无崩溃、无异常日志,HTTP 返回 200,隐蔽性极强。

session.upload_progress 功能是如何导致漏洞的?

该功能为了在上传过程中实时更新进度,会在 PHP 解析 multipart 数据时提前初始化 session,这个时机早于扩展的 RINIT。如果此时发生 OOM 导致 zend_bailout 跳过清理,session 全局变量中的指针(如 PS(id))就会悬空,后续请求可回收该内存进行利用。

如何修复或缓解 CVE-2026-109619 漏洞?

首选升级 PHP 到已修复版本。临时缓解可关闭 session.upload_progress.enabled(需在 php.ini 中设置,不能通过 ini_set),但这不能防止 OOM 导致的 DoS。极端方法可设置 php-fpm 的 pm.max_requests = 1 或 Apache 的 MaxRequestsPerChild 1,让每个 worker 只处理一个请求。

这个漏洞为什么难以被 WAF 或应用层防护拦截?

因为攻击发生在 PHP 请求初始化阶段(RINIT 之前),此时应用代码尚未执行,路由、中间件、WAF 规则和 ini_set 都来不及生效。攻击请求就是普通的 HTTP POST,且 session.use_only_cookies=1 也挡不住,因为攻击者可将 PHPSESSID 放在表单字段中。

除了内存破坏利用,还有更简单的 RCE 变体吗?

有。攻击者可将恶意序列化对象写入 session 文件,upload_progress 在 RINIT 前读取 session 时会反序列化该对象,触发 __destruct()。如果目标应用存在可接力到 system() 的魔术方法链(如 WordPress、Laravel 等生态中常见),即可实现 RCE,完全不需要内存布局操作。

🏷️

标签

➡️

继续阅读