ThinkPHP5.1.X反序列化利用链
字数 1308 2025-08-26 22:11:15
ThinkPHP5.1.X反序列化利用链分析
环境搭建
- 使用Composer创建项目:
composer create-project --prefer-dist topthink/think tp5137
cd tp5137
- 修改composer.json文件,将框架版本指定为5.1.37:
"topthink/framework": "5.1.37"
- 更新依赖:
composer update
- 修改控制器代码(application/index/controller/Index.php):
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$u = unserialize($_GET['c']);
return 'hhh';
}
}
利用条件
-
必要条件:
- 存在完全可控的反序列化点(如
unserialize(可控变量))
- 存在完全可控的反序列化点(如
-
可选条件(满足任意一个即可):
- 存在文件上传且文件名完全可控,并使用了文件操作函数(如
file_exists('phar://恶意文件'))
- 存在文件上传且文件名完全可控,并使用了文件操作函数(如
漏洞链分析
1. 入口点:Windows类的__destruct方法
think\process\pipes\Windows类的__destruct方法中存在文件删除功能,其中$filename变量可控。如果将类对象赋值给$filename,在file_exists($filename)时会触发该对象的__toString方法。
2. __toString方法利用
think\model\concern\Conversion类的__toString方法中:
$relation来自$this->data[$name](可控)$name来自$this->append(可控)- 最终调用形式:
可控类->visible(可控变量)
3. 寻找visible或__call方法
由于没有直接可利用的visible方法,转而寻找__call方法:
think\Request类的__call方法中call_user_func_array的第一个参数完全可控- 可构造为
call_user_func_array(array(任意类,任意方法),$args)
4. 方法链调用
虽然Request类的input方法常被用于RCE,但由于$args的第一个元素固定为类对象,需要间接调用:
- 通过
request方法间接调用input方法 - 进一步通过
isAjax或isPjax方法调用param方法
5. 最终利用
构造Request对象:
$this->filter = 'system'$this->param = array('id')
影响版本
- 5.1.16 <= ThinkPHP <= 5.1.37
- 5.1.14 <= ThinkPHP <= 5.1.15
- 5.1.3 <= ThinkPHP <= 5.1.13
漏洞链流程图
Windows::__destruct->file_exists- 触发
Conversion::__toString - 调用
Request::__call - 通过
isAjax/isPjax->param->input - 执行任意命令
防御建议
- 升级到安全版本
- 避免直接反序列化用户输入
- 对文件操作函数进行严格过滤
- 使用类型检查确保
file_exists等函数参数为字符串
参考
- 官方GitHub issue
- 挖掘暗藏thinkphp中的反序列利用链