TP6.0.13反序列化分析
字数 781 2025-08-06 18:07:44

ThinkPHP 6.0.13 反序列化漏洞分析

0x00 环境搭建

  1. 使用Composer安装ThinkPHP 6.0.13:
    composer create-project topthink/think tp
    
  2. 要求PHP版本>7.2
  3. 使用PHPStudy搭建运行环境

0x01 漏洞分析

漏洞触发流程

  1. 入口点__destruct()魔术方法

    • 位于AbstractCache类(Psr6Cache的父类)
    public function __destruct()
    {
        if ($this->lazySave) {
            $this->save();
        }
    }
    
  2. 调用链

    • __destruct()save()getItem()__call()log()record()save()Socket::save()

关键调用分析

  1. Psr6Cache类

    public function save()
    {
        $item = $this->pool->getItem($this->key);
        $item->set($this->getForStorage());
        $item->expiresAfter($this->expire);
        $this->pool->save($item);
    }
    
  2. Channel类的__call方法

    public function __call($method, $parameters)
    {
        $this->log($method, ...$parameters);
    }
    
  3. record方法

    public function record($msg, string $type = 'info', array $context = [], bool $lazy = true)
    {
        // ...省略部分代码...
        if (!$this->lazy || !$lazy) {
            $this->save();
        }
    }
    
  4. Socket驱动类的save方法(关键点):

    public function save(array $log = []): bool
    {
        // ...省略部分代码...
        if (!empty($this->config['format_head'])) {
            try {
                $currentUri = $this->app->invoke($this->config['format_head'], [$currentUri]);
            } catch (NotFoundExceptionInterface $notFoundException) {
                // Ignore exception
            }
        }
        // ...省略部分代码...
    }
    

漏洞利用关键

  1. 反射调用

    • $this->app->invoke()方法支持动态反射调用
    • 可以调用任意类的任意方法
  2. Php视图驱动类的display方法

    public function display(string $content, array $data = []): void
    {
        $this->content = $content;
        extract($data, EXTR_OVERWRITE);
        eval('?>' . $this->content);
    }
    
    • 通过eval执行任意PHP代码

完整利用链

  1. 构造Psr6Cache对象,设置lazySave为true
  2. 设置poolChannel对象
  3. Channel对象中设置loggerSocket驱动
  4. Socket驱动中设置config['format_head'][new \think\view\driver\Php, 'display']
  5. 通过反射调用最终执行eval代码

0x02 漏洞复现

  1. 构造Payload

    $b = new think\log\Channel();
    $a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
    // 设置相关属性构造完整利用链
    
  2. 触发方式

    • 序列化恶意对象
    • 在应用反序列化该对象时触发漏洞

0x03 防御措施

  1. 升级到最新版本
  2. 避免反序列化用户可控数据
  3. __destruct__wakeup等魔术方法进行安全审查
  4. 限制反射调用的使用范围

流程图

__destruct() → save() → getItem() → __call() → log() → record() → save() → Socket::save() → invoke() → display() → eval()
ThinkPHP 6.0.13 反序列化漏洞分析 0x00 环境搭建 使用Composer安装ThinkPHP 6.0.13: 要求PHP版本>7.2 使用PHPStudy搭建运行环境 0x01 漏洞分析 漏洞触发流程 入口点 : __destruct() 魔术方法 位于 AbstractCache 类( Psr6Cache 的父类) 调用链 : __destruct() → save() → getItem() → __call() → log() → record() → save() → Socket::save() 关键调用分析 Psr6Cache类 : Channel类的__ call方法 : record方法 : Socket驱动类的save方法 (关键点): 漏洞利用关键 反射调用 : $this->app->invoke() 方法支持动态反射调用 可以调用任意类的任意方法 Php视图驱动类的display方法 : 通过eval执行任意PHP代码 完整利用链 构造 Psr6Cache 对象,设置 lazySave 为true 设置 pool 为 Channel 对象 在 Channel 对象中设置 logger 为 Socket 驱动 在 Socket 驱动中设置 config['format_head'] 为 [new \think\view\driver\Php, 'display'] 通过反射调用最终执行 eval 代码 0x02 漏洞复现 构造Payload : 触发方式 : 序列化恶意对象 在应用反序列化该对象时触发漏洞 0x03 防御措施 升级到最新版本 避免反序列化用户可控数据 对 __destruct 和 __wakeup 等魔术方法进行安全审查 限制反射调用的使用范围 流程图