怎样挖掘出属于自己的php反序列化链
字数 1583 2025-08-22 12:23:47

PHP反序列化链挖掘指南

前言

本文旨在系统性地介绍如何挖掘PHP框架中的反序列化利用链(POP Chain)。通过分析多个实际案例,总结出一套有效的挖掘方法论,帮助安全研究人员发现新的反序列化漏洞。

基本概念

PHP反序列化漏洞需要满足两个基本条件:

  1. 存在可被用户控制的unserialize()调用点
  2. 存在合理的POP Chain(属性-方法调用链)

核心原则

1. 变量可控原则

  • 尽可能寻找参数完全可控的方法
  • 关注危险函数和结构中的可控变量

2. 扩大影响原则

  • 寻找可以扩大攻击面的结构和方法
  • 通过多种方式连接不同的调用链

挖掘方法论

1. 寻找起点

魔术方法选择

  • __destruct() - 对象销毁时自动调用,最常用
  • __wakeup() - 反序列化时调用,使用较少

优秀起点的特征

  • 自动调用(魔术方法)
  • 参数完全可控
  • 攻击面广(调用其他方法或函数)

案例分析
Laravel框架中PendingBroadcast.php__destruct()方法:

public function __destruct() {
    $this->events->dispatch($this->event);
}

优势:

  • 两个参数($this->events$this->event)都可控
  • 可导向__call()dispatch()方法

2. 跳板选择

常见跳板类型

  1. 字符串操作函数:如trim(),可触发__toString()
  2. 动态调用结构:
    • call_user_func($this->test)
    • $test()
    • new $test1($test2, $test3)
  3. 数组调用:[(new test), "aaa"]()可调用test类的aaa方法

跳板选择策略

  • 寻找参数可控的函数调用
  • 关注可能触发其他魔术方法的结构
  • 优先选择能扩大攻击面的跳板

3. 终点定位

两类终点

  1. 危险动态调用:

    • ($this->a)($this->b)
    • $this->a[0]($this->b)
  2. 危险函数:

    • RCE:call_user_func, array_walk
    • 文件操作:file_put_contents

实战案例

Yii2/RCE1

调用链

  1. 起点:yii\db\BatchQueryResult::__destruct()

    public function __destruct() {
        $this->reset();
    }
    
  2. 跳板:yii\web\DbSession::close()

    public function close() {
        if ($this->getIsActive()) {
            $this->fields = $this->composeFields();
            session_write_close();
        }
    }
    
  3. 跳板:yii\web\MultiFieldSession::composeFields()

    protected function composeFields() {
        $fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : [];
        // ...
    }
    
  4. 终点:yii\rest\IndexAction::run()

    public function run() {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }
        // ...
    }
    

利用方式

  • 设置$writeCallback[(new IndexAction), 'run']
  • 设置$checkAccess$id为恶意代码

Yii2/RCE2

调用链

  1. 起点:Symfony\Component\String\UnicodeString::__wakeup()

    public function __wakeup() {
        normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string);
    }
    
  2. 跳板:Symfony\Component\String\LazyString::__toString()

    public function __toString() {
        if (\is_string($this->value)) {
            return $this->value;
        }
        return $this->value = ($this->value)();
    }
    

利用方式

  • 通过__toString()触发动态函数调用
  • 直接执行任意代码

防御策略

1. 动态调用与危险函数

  • 对变量和方法进行完整回溯
  • 优先使用静态属性进行动态调用
  • 使用instanceof检查对象类型

2. 方法设计

  • 避免在魔术方法中调用过多其他方法
  • 公共方法中避免直接使用危险函数
  • 不需要序列化的类可使用__wakeup()阻止反序列化

3. 最重要原则

  • 永远不要让unserialize()和文件操作函数用户可控

总结

挖掘PHP反序列化链的关键在于:

  1. 从魔术方法开始,寻找参数可控的起点
  2. 通过多种跳板连接不同的方法和类
  3. 最终导向危险函数或动态调用
  4. 保持耐心,系统性地分析框架结构

通过这种方法论,可以有效提高发现新反序列化漏洞的效率。

PHP反序列化链挖掘指南 前言 本文旨在系统性地介绍如何挖掘PHP框架中的反序列化利用链(POP Chain)。通过分析多个实际案例,总结出一套有效的挖掘方法论,帮助安全研究人员发现新的反序列化漏洞。 基本概念 PHP反序列化漏洞需要满足两个基本条件: 存在可被用户控制的unserialize()调用点 存在合理的POP Chain(属性-方法调用链) 核心原则 1. 变量可控原则 尽可能寻找参数完全可控的方法 关注危险函数和结构中的可控变量 2. 扩大影响原则 寻找可以扩大攻击面的结构和方法 通过多种方式连接不同的调用链 挖掘方法论 1. 寻找起点 魔术方法选择 : __destruct() - 对象销毁时自动调用,最常用 __wakeup() - 反序列化时调用,使用较少 优秀起点的特征 : 自动调用(魔术方法) 参数完全可控 攻击面广(调用其他方法或函数) 案例分析 : Laravel框架中 PendingBroadcast.php 的 __destruct() 方法: 优势: 两个参数( $this->events 和 $this->event )都可控 可导向 __call() 或 dispatch() 方法 2. 跳板选择 常见跳板类型 : 字符串操作函数:如 trim() ,可触发 __toString() 动态调用结构: call_user_func($this->test) $test() new $test1($test2, $test3) 数组调用: [(new test), "aaa"]() 可调用test类的aaa方法 跳板选择策略 : 寻找参数可控的函数调用 关注可能触发其他魔术方法的结构 优先选择能扩大攻击面的跳板 3. 终点定位 两类终点 : 危险动态调用: ($this->a)($this->b) $this->a[0]($this->b) 危险函数: RCE: call_user_func , array_walk 等 文件操作: file_put_contents 等 实战案例 Yii2/RCE1 调用链 : 起点: yii\db\BatchQueryResult::__destruct() 跳板: yii\web\DbSession::close() 跳板: yii\web\MultiFieldSession::composeFields() 终点: yii\rest\IndexAction::run() 利用方式 : 设置 $writeCallback 为 [(new IndexAction), 'run'] 设置 $checkAccess 和 $id 为恶意代码 Yii2/RCE2 调用链 : 起点: Symfony\Component\String\UnicodeString::__wakeup() 跳板: Symfony\Component\String\LazyString::__toString() 利用方式 : 通过 __toString() 触发动态函数调用 直接执行任意代码 防御策略 1. 动态调用与危险函数 对变量和方法进行完整回溯 优先使用静态属性进行动态调用 使用 instanceof 检查对象类型 2. 方法设计 避免在魔术方法中调用过多其他方法 公共方法中避免直接使用危险函数 不需要序列化的类可使用 __wakeup() 阻止反序列化 3. 最重要原则 永远不要让 unserialize() 和文件操作函数用户可控 总结 挖掘PHP反序列化链的关键在于: 从魔术方法开始,寻找参数可控的起点 通过多种跳板连接不同的方法和类 最终导向危险函数或动态调用 保持耐心,系统性地分析框架结构 通过这种方法论,可以有效提高发现新反序列化漏洞的效率。