吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1270|回复: 25
上一主题 下一主题
收起左侧

[漏洞分析] 5 个 HTTP 请求拿下 PHP 站点:CVE-2026-109618 / 109619 从 OOM 到 RCE 的完整验证

  [复制链接]
跳转到指定楼层
楼主
神奇的人鱼 发表于 2026-10-27 20:26 回帖奖励
本帖最后由 神奇的人鱼 于 2026-10-27 20:33 编辑

本文基于测试环境。文中给出的是机制与验证结果,不含可直接复用的利用参数;
每个构建都需要重新标定。请勿用于未授权目标。



简介

  • CVE-2026-109618 / 109619 影响 PHP 5.4.0 – 8.5.11,修复版本:8.2.35 / 8.3.36 / 8.4.27 / 8.5.12
  • 漏洞成因:session.upload_progress 的清理回调里初始化会话,内存耗尽(OOM)跳过了收尾,留下悬空的会话全局量
  • 我们在授权环境实测:
    • PHP 7.4.33 :完全纯 HTTP 的 RCE(ASLR无效,基址从响应 cookie 自动推导)
    • 真实场景:一个只有 index.php 的 7.4 站点,5 个未认证HTTP 请求后页面被改写为 webshell
  • 触发前提:session.upload_progress.enabled=On(默认开启);若要"改文件"还需 webroot 对 web 用户可写
  • 注意:disable_functions 不能防住这类攻击(利用不经过函数表)

1. 用一句人话说漏洞在做什么

PHP 有一个"上传进度"功能:请求里带上PHP_SESSION_UPLOAD_PROGRESS 字段,PHP 就会在
表单解析结束时清理进度,并顺手初始化 session——也就是读 session 文件、必要时写 Set-Cookie。

而"写 Set-Cookie"需要一小块内存(把 session id 做 URL 编码)。
如果攻击者先把整个请求的内存限额吃光,这一步分配就会失败,PHP 会直接跳出请求(bailout)。

问题就在"跳出":正常收尾会清空会话全局量,bailout 不会。 于是 PHP 留下四个坏掉的全局变量:

PS(id)          指向的 session id 字符串 —— 但它的内存已被整体回收
PS(mod_data)    指向的 session 文件句柄 —— 同样悬空
status          = active
send_cookie     = 1("这个请求要发 cookie")

接下来的所有利用,都是围绕这四个坏变量做文章。


2. 阶段一:让内存"恰好"耗尽

这一步是整条链最需要耐心的部分:OOM 不能早(在解析阶段就爆,状态会被正常清理),也不能晚(请求跑完,状态同样没了),必须精确落在"写 cookie"的那一次分配上。

PHP 处理 multipart 时会为每个字段值分配两份内存(原始字节 + 复制成字符串),所以字段尺寸和数量直接决定内存曲线:

大值字段(每个 15000 字节)→ 每字段约占 32KB
数百个这样的字段 + 一批中小字段 → 刚好顶到 memory_limit

3. 阶段二:把内存读出来

PHP 的字符串在内存里有个"长度"字段,读字符串就是"从内容起点按长度读"。如果我们能让这个长度变大,PHP 就会把后面的内存一起读出来。这里是典型的内存越界行为。

具体做法是堆整形:让一个 231 字节的值恰好落在悬空 session id 字符串前方的槽位,并在值的特定偏移写入 0x4000(16384)——这个数字正好落在 id 字符串的"长度"字段上。

      值槽(231B 值)                          悬空 id 字符串
 ┌──────────────────────────────┐      ┌───────────────────────────────┐
 │ ... 0x4000(写入的伪造长度)  │ ───► │ refcount │ len=0x4000 │ 内容… │
 └──────────────────────────────┘      └───────────────────────────────┘
                                                │
                                                └─► PHP 从内容起点读 16KB

结果,响应里的 Set-Cookie 变成一条超长 cookie——里面是 id 之后 16KB 的原始堆内存(url 编码过)。解码后就是一份内存快照。

从这 16KB 里"算出"地址

泄漏内容里有两类指针,正好对应我们要的两个基址:

指针类型 典型内容 推算方式 实测命中数
堆块指针 Zend 内存块的地址(2MB 对齐) chunk = 指针 & ~0x1FFFFF xx 个
函数指针 数组对象的析构函数 zval_ptr_dtor libphp = 指针 − 已知偏移(要求页对齐) xx 个

4. 阶段三、四:从泄漏到命令执行

  • 阶段三:再发一个请求,让 PHP 先建立 $_SESSION 数组(内部是一个 reference 指向 HashTable),随后用一个超大字段把内存再次打爆。结果 PS(http_session_vars) 成为一个指向已释放对象的悬空引用,落点非常稳定。
  • 阶段四(fire):发一个精心构造的大字段。里面放两样东西:
    1. 一个伪造的 reference,它的"值指针"指向我们计算好的地址
    2. 在该地址上伪造一个 HashTable,其中析构函数指针被替换成 PHP 的 system 实现函数地址,并附带伪造的"参数结构",参数就是我们要执行的命令

PHP 在处理请求时会销毁这个悬空引用 → 调用 HashTable 的析构函数 → 实际跳进 system()→ 命令执行。

注意一个细节:这里不经过 PHP 的函数表,所以 disable_functions=system 完全无效。
即使把 exec 家族全部禁用,也只需换一个"有用的函数指针"(例如 PHP 代码求值函数)。


5. 真实场景演示

环境

项 值
容器 php:7.4-apache(PHP 7.4.33,EOL,官方从未修复此问题)
memory_limit 8M(与该版本已验证配方匹配)
worker 单进程
站点 只有一个 index.php,内容 Hello, this is the demo page.
webroot 对 web 用户可写(现实中常见:应用需要写模板/缓存/上传目录)

攻击日志(5 个请求,无登录、无文件系统访问)

[1/5] OOM 落在写 cookie 的分配点 → 悬空会话状态
[2/5]  chunk 指针 xx 个 / 函数指针 xx 个   ← 全部来自响应 cookie
[3/5] 解析  chunk 基址 + libphp 基址
[4/5] 留下悬空 $_SESSION 引用
[5/5] fire 命令: printf '<?php system($_GET[c]);?>' > /var/www/html/index.php

效果

攻击前: Hello, this is the demo page.
攻击后: 页面被替换为 webshell

$ curl "http://target/index.php?c=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

$ curl "http://target/index.php?c=uname+-a"
Linux ... x86_64 GNU/Linux

页面内容是持久化的:攻击结束后仍然有效,直到被改回或重装。


6. 看起来简单、做起来很难

这是本次研究最想强调的部分。

6.1 每个版本、每个内存限制都要重新标定

版本/限制 阶段一参数(量级) 阶段二关键点
8.1.34 / 8.2.34 @8M 大值字段 xxx 个 嵌套数组 xxx 个
7.4.33 @8M 大值字段 xxx 个 嵌套数组 xxx 个
7.4.29 @16M 大值字段 xxx 个 嵌套 x 个 + 尾部伪造值 xxx 个

同一个 7.4.29,只是把内存限制从 8M 改到 16M,字段数量几乎翻倍;而 8M 能用的阶段二配方在 16M 上会直接失效(session id 所在内存页被另一类对象抢走,响应里的 cookie 会变成一个奇怪的值)。

6.2 符号偏移、结构布局也全都要重新提取

同一份 PHP 7.4.29 源码,不同发行版的构建:关键函数偏移能差 1MB;二进制还要靠"函数名表"反查。字段偏移、allocator 布局同样会变。

6.3 真实设备还有三个额外门槛

门槛 现象 我们的处置
多 worker 请求飘到别的子进程,拿不到悬空状态 keep-alive 单连接钉住同一个 worker;心跳必须用静态资源(PHP 心跳会清状态)
必须"干净 worker" 只要跑过一次 PHP,状态就没了 重启服务,等待未执行过 PHP 的子进程

7. 与公开研究的关系

2026 年 10 月社区公开了同批 CVE 的 5 条利用链(A–E),其中 D 链就是本文这条 OOM 链,且只对付费订阅开放。
公开的"版本感知"工具链里,7.4 分支的堆标定是空的(rce=False、"heap not calibrated")。
本次工作独立复现了 D 链,并把 7.4 在 8M下都打通,同时补齐了多 worker、真实设备与工具链。


8. 防御建议

措施 有效性
升级到 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 ✅ 根治(7.4/8.0/8.1 已 EOL,只能升大版本)
关闭 session.upload_progress(业务不需要时) ✅ 直接消除触发点
webroot 只读、上传目录单独挂载并禁止执行 ✅ 阻断"改页面/写 webshell"这类后果
低权限运行、只读文件系统、seccomp/AppArmor ⚠️ 缩小执行后的影响面
disable_functions 禁 system/passthru/exec… ❌ 无效(可绕过函数表直接跳地址)
降低 memory_limit ❌ 无效(配方按新限额重算即可)

检测思路

  • 同一连接上出现"内存耗尽响应 → 紧接着的第二个请求"这种组合
  • 响应里出现异常超长的 Set-Cookie(几 KB 到几十 KB,正常 session id 只有 32 字节)
  • multipart 请求包含大量等长字段(几百个同尺寸字段)——但与正常表单仍可能混淆
  • 这些信号只在极短时间窗内存在,单条规则很难覆盖,建议结合"异常长度 + 短时间连续请求"做关联

9. 结语

这类漏洞的难度不在"理解",而在"标定":
同一个 CVE,四个版本四套参数;同一个版本换内存限制又要重来;
真实环境还有 worker、SAPI、权限三道门。"写一个通用脚本"是不可能的,
能做的是把方法论工程化:预算模型、状态判据、堆快照、参数搜索——这也是本次研究留下的最有价值的东西。


本文所有测试均在授权环境进行。请勿用于未授权目标。

免费评分

参与人数 9吾爱币 +8 热心值 +7 收起 理由
yang19950324 + 1 + 1 用心讨论,共获提升!
laozhang4201 + 1 + 1 热心回复!
whereismy + 1 谢谢@Thanks!
surepj + 1 + 1 用心讨论,共获提升!
leechjia + 1 + 1 谢谢@Thanks!
GS9452 + 1 谢谢@Thanks!
apull + 1 热心回复!
helian147 + 1 + 1 热心回复!
ParllelShifterX + 1 + 1 热心回复!

查看全部评分

发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
孤狼微博 发表于 2026-10-28 01:49
我怎么检查自己的是否修复了呢
推荐
CNLibrary 发表于 2026-10-27 21:06
升级到 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12        &#9989; 根治(7.4/8.0/8.1 已 EOL,只能升大版本)
关闭 session.upload_progress(业务不需要时)        &#9989; 直接消除触发点
webroot 只读、上传目录单独挂载并禁止执行        &#9989; 阻断"改页面/写 webshell"这类后果

太专业了,低版本除了这三个方法之外还有其他办法就好了
沙发
anduiy 发表于 2026-10-27 20:37
4#
_知鱼之乐 发表于 2026-10-27 21:11
大佬 太厉害了!
5#
ANTT 发表于 2026-10-27 22:40
谢谢分享。
6#
Cleverwwh 发表于 2026-10-28 00:12
本帖最后由 Cleverwwh 于 2026-10-28 00:50 编辑

从 OOM 到 RCE 这条链讲得很清楚,5 个请求的复现步骤可操作性强,对 PHP 站点排查很有参考价值。
8#
ggxxuser 发表于 2026-10-28 02:07
大佬太厉害了,打上补丁是不是就安全了?
9#
error3 发表于 2026-10-28 05:04
太佬就是大佬,把这么专业的东西,讲得小白一看就明白。
10#
vscos 发表于 2026-10-28 06:59
这些漏洞是怎么被发现的
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-10-28 11:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表