php2shell漏洞利用oom机制触发内存破坏:通过合法multipart请求精准耗尽memory_limit,使php在longjmp异常跳转中留下半初始化/半释放的危险状态,依赖常驻进程复用残缺内存指针实现远程代码执行。

PHP2Shell 的 OOM 内存错误机制不是单纯“内存超了就崩”,而是利用 PHP 在 memory_limit 触发瞬间的异常退出路径,让内部状态停留在半初始化、半释放的危险中间态,从而为后续内存破坏铺路。关键在于“错位”——分配动作被打断,但计数器已更新;内存已被释放,但指针还留着。
触发核心:在合法限制内精准耗尽 memory_limit
攻击者不靠上传超大文件,而是用看似合规的 multipart/form-data 请求“悄悄榨干”内存:
- 构造含大量嵌套字段名的表单(如
file[0][a][b][c]...[z]),每个字段名都会触发哈希表插入,在 Zend 引擎中引发多次 key 分配尝试 - 混入多个空文件控件(
<input type="file" name="f1"><input type="file" name="f2">...),即使 filename="",PHP 仍会为每个控件分配 Bucket 结构并尝试初始化 - 添加大量 HTTP 头部(如重复的
X-Forwarded-For)或超长 header 值,占用 SAPI 层请求上下文内存 - 所有操作都控制在
post_max_size和upload_max_filesize限制内,绕过常规上传拦截
OOM 发生时的致命跳转:longjmp() 绕过清理逻辑
当某次内存分配(例如为新哈希表 Bucket 分配 key 字符串)恰好撞上 memory_limit 上限,PHP 不会逐层返回、安全释放,而是直接调用 longjmp() 跳回最外层错误处理点。结果是:
- Zend 哈希表的
nNumUsed计数器已经 +1,但对应 Bucket 的 key 和 value 字段根本没初始化 - 请求内存堆被整体标记为可回收,但 session、error、PS(PHP Session)等全局结构里仍存有指向这块堆的裸指针
- Apache mod_php 或 PHP-FPM worker 进程进入“关闭阶段”,开始按计数器遍历析构——却去读写那些从未初始化或早已释放的内存地址
为什么必须是长驻进程模式?
漏洞利用依赖“跨请求状态复用”。只有在 PHP-FPM(static/dynamic 模式)、Apache mod_php 等常驻进程中,一个请求的残缺状态才可能被下一个请求的清理流程误读。CGI 模式每请求起新进程,无法复用,天然免疫。
注意:调整 pm.max_requests 设为 1 只是让进程更早重启,缩小攻击窗口,但不能阻止单次请求内的 OOM 触发与破坏——补丁才是根本解法。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











