php8 首个 bypass disable function漏洞
字数 3167
更新时间 2026-05-08 07:29:22

PHP 8 首个 Bypass Disable Function 漏洞分析与利用教学文档

1. 概述

本文档针对先知社区(xz.aliyun.com)发布的一篇关于“PHP8 首个 bypass disable function漏洞”的技术文章进行系统性梳理与教学化总结。该漏洞是首个在 PHP 8 环境下可达成绕过 disable_functions 限制的利用技巧,并已被武器化为蚁剑(AntSword)插件。

2. 漏洞基本信息

  • 漏洞性质:一个潜伏在 PHP 核心代码中长达 20 年(自 PHP 5.1 引入 Serializable 接口起至 PHP 8.5)的高危 Use-After-Free(UAF)漏洞。
  • 影响版本:目前公开的利用工具(蚁剑插件)支持 PHP 8.4 和 8.5 两个小版本。其他版本的 PHP 8 可能存在此漏洞,但因内存偏移不同,需进行二进制适配。
  • 漏洞价值:可从“内存破坏”到“任意代码执行”,特别是在目标服务器开启 disable_functions 限制(禁用 systemshell_exec 等函数)的情况下,实现远程命令执行(RCE)。

3. 漏洞成因深度分析

3.1 核心触发点

漏洞位于 PHP 反序列化函数 unserialize() 在处理实现了 Serializable 接口的类时。

3.2 技术细节

  1. var_hash 的作用

    • PHP 反序列化底层引擎维护一个名为 var_hash 的哈希表,用于记录已解析过的变量,以支持处理 R:N;(引用)语法时能找回对应对象。
  2. 漏洞代码位置

    • 位于 Zend/zend_interfaces.c 文件中的 zend_user_unserialize() 函数。
  3. 漏洞触发逻辑

    • zend_user_unserialize() 调用一个类自定义的 unserialize() 方法时,没有递增 BG(serialize_lock) 这个锁变量
    • 如果在自定义的 unserialize() 方法内部再次递归调用unserialize(),则内部的反序列化过程会继承外层的 var_hash
    • 如果内部反序列化注册了一些对象,随后通过某些操作(例如,向对象动态添加属性,导致底层 Hash Table 容量超过初始值 8 而触发扩容),旧的内存块(arData)会被 efree 释放。
    • 然而,外层的 var_hash 仍然保留着指向这些已被释放内存的指针(即悬垂指针,Dangling Pointers)。
    • 当外层数据中再出现一个 R:N; 引用这些“槽位”时,经典的 Use-After-Free(UAF)内存破坏条件即被触发。

4. 反序列化利用链构建

4.1 最小化触发条件

要触发此漏洞,只需一个基础的反序列化操作:

unserialize($data)->x = 0;

4.2 触发步骤分解

  1. 构造恶意序列化字符串 $data
  2. 内部 Payload 结构
    • Payload 反序列化出一个带有 8 个属性stdClass 对象。这恰好填满 PHP 底层 Hash Table 的初始容量(nTableSize = 8)。
  3. 关键触发点->x = 0 赋值操作。
    • 这是尝试写入的第 9 个属性,强制触发底层 Hash Table 的容量倍增(从 8 扩容到 16)。
  4. 内存释放
    • 扩容操作导致原本存储 8 个属性的、大小为 288 字节的 arData 内存块被 efree 回收。
  5. UAF 达成
    • 由于前述漏洞,var_hash 中仍保留着指向这块已释放内存的引用,成功制造出 Use-After-Free 场景。

4.3 漏洞复现环境搭建

在目标服务器上放置以下 PHP 脚本作为攻击端点:

<?php
error_reporting(0);
class CachedData implements Serializable {
    public function serialize(): string { return ''; }
    public function unserialize(string $data): void {
        unserialize($data)->x = 0; // 漏洞触发点
    }
}
echo serialize(@unserialize($_REQUEST['cook']));

5. 从 UAF 到 Bypass Disable Functions 的完整利用链

利用的核心目标是将内存读写能力转化为在 disable_functions 开启情况下的命令执行能力。整个利用流程(PoC流程)可概括为以下步骤:

  1. 堆地址泄露

    • 利用 UAF 的写入贯穿特性,泄露出堆内存的基址,为后续内存操作奠定基础。
  2. 对象喷射

    • 在堆内存中喷射大量精心构造的、伪造的闭包(Closure)对象,目的是占据被释放的内存区域,并控制其内容。
  3. 内存漫游与关键结构定位

    • 构造一个巨大的(例如 64MB)字符串来扫描进程内存空间。
    • 目标是定位 PHP 的核心全局结构:
      • Executor Globals (EG):PHP 执行器全局变量表,包含大量运行时信息。
      • function_table:PHP 内部注册的所有函数的哈希表。即使 disable_functions 在用户层禁用了函数,这些函数的底层 C 实现指针(zend_function)仍注册在此表中。
  4. 定位并劫持 system 函数

    • 从定位到的 function_table 中,解析并找到 zif_system(即 system 函数的底层 C 实现)的内存地址。
    • 关键点:此操作完全绕过了 PHP 用户态的 disable_functions 限制,因为该限制只拦截了用户层对函数名的调用,并未从底层删除函数指针。
  5. 类型混淆与函数指针劫持

    • 通过操纵在步骤2中喷射的伪造闭包对象,将其内部的函数执行指针修改为步骤4中找到的 zif_system 地址。
  6. 最终 RCE 触发

    • 调用被篡改的闭包对象。由于它的函数指针已指向 system,因此调用该闭包等同于调用 system() 函数,从而成功执行任意系统命令,实现远程代码执行。

6. 自动化利用工具

  • 工具名称:蚁剑(AntSword)插件
  • 项目地址:https://github.com/ez-lbz/php8-UAF
  • 工具功能:自动化完成上述从内存泄露、定位到最终命令执行的完整利用链。
  • 当前支持:已验证在 PHP 8.4 和 8.5 环境下有效。

7. 总结与影响

  1. 漏洞渊源:据原始研究(MAD Bugs 博客)透露,此漏洞是通过向 AI(如 Claude)投喂历史上 PHP 的 UAF 漏洞案例,由安全研究员与 AI 协同分析发现的。这展示了AI在漏洞挖掘中的潜在应用。
  2. 历史地位:这是公开的首个在 PHP 8 系列中可实现绕过 disable_functions 的利用方法,具有重要的实战价值。
  3. 后续影响:预计此漏洞及利用技术将在后续的 CTF 竞赛、红队评估及安全研究中频繁出现。

8. 防御建议(基于模型知识的补充)

文档中未详述防御措施,但基于我所掌握的知识,针对此类漏洞的防护可考虑以下层面:

  1. 及时更新:密切关注 PHP 官方发布的安全更新,并及时将 PHP 升级到已修复此漏洞的版本。
  2. 输入过滤:对用户输入的、传递给 unserialize() 函数的数据进行严格校验,避免反序列化不可信的数据。
  3. 禁用危险函数:虽然此漏洞可绕过 disable_functions,但保持该设置仍能防御大多数普通 webshell。可考虑结合其他安全机制,如禁用 unserialize() 函数本身(通过 disable_functions 或配置)。
  4. 深度防御
    • 使用 Suhosin 等 PHP 安全加固扩展。
    • 部署 Web 应用防火墙(WAF),配置规则拦截可疑的反序列化 Payload。
    • 在容器或系统层面进行权限最小化控制,限制 PHP 进程的系统调用能力。
相似文章
相似文章
 全屏