ThinkPHP5漏洞分析之SQL注入(三)
字数 1239 2025-08-18 11:38:45
ThinkPHP5 SQL注入漏洞分析(select方法注入)
漏洞概述
本漏洞存在于ThinkPHP5框架的Mysql类中,具体问题出在parseWhereItem方法。由于程序未对用户输入数据进行充分过滤,导致恶意数据被直接拼接进SQL语句,形成SQL注入漏洞。该漏洞影响ThinkPHP5全版本。
漏洞环境搭建
环境准备步骤
-
创建测试项目:
composer create-project --prefer-dist topthink/think=5.0.10 tpdemo -
修改
composer.json文件:"require": { "php": ">=5.4.0", "topthink/framework": "5.0.10" } -
执行更新:
composer update -
创建控制器文件
application/index/controller/Index.php:<?php namespace app\index\controller; class Index { public function index() { $username = request()->get('username'); $result = db('users')->where('username','exp',$username)->select(); return 'select success'; } } -
配置数据库(
config/database.php)并开启调试模式(config/app.php中设置app_debug和app_trace为true) -
创建测试数据库:
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');
漏洞复现
访问以下URL触发漏洞:
http://localhost:8000/index/index/index?username=) union select updatexml(1,concat(0x7,user(),0x7e),1)#
注意:需要开启app_debug才能看到SQL报错信息。
漏洞分析
漏洞触发流程
- 用户输入通过Request类的get方法获取,经过input方法处理(默认过滤不充分)
- 数据传递到Query类的where方法
- where方法调用parseWhereExp分析查询表达式
- 程序继续调用select方法构建SQL语句
$this->builder为\think\db\builder\Mysql类实例,继承自Builder类- Builder类的select方法填充SQL模板,其中WHERE部分包含用户输入
- buildWhere函数处理WHERE条件
- parseWhereItem函数处理WHERE子单元,当操作符为'exp'时直接拼接用户输入
关键代码分析
漏洞核心在于parseWhereItem方法对'exp'操作符的处理:
// thinkphp/library/think/db/Builder.php
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
{
// ...
case 'exp':
// 表达式查询
$whereStr .= '( ' . $field . ' ' . $val . ' )';
break;
// ...
}
当使用where('username','exp',$username)时,$val(即$username)被直接拼接到SQL语句中,没有任何过滤处理。
漏洞利用条件
- 使用where方法且第二个参数为'exp'
- 用户输入直接作为第三个参数传入
- 应用程序未对用户输入进行额外过滤
防御措施
虽然官方认为这是提供的功能而非漏洞,但开发者应采取以下防护措施:
- 避免直接使用'exp'操作符处理用户输入
- 对用户输入进行严格过滤和转义
- 使用参数化查询或预处理语句
- 最小权限原则,数据库用户只赋予必要权限
- 生产环境关闭调试模式(app_debug和app_trace设为false)
漏洞修复方案
官方未提供专门修复,但开发者可以:
- 重写parseWhereItem方法,增加对'exp'操作符的输入验证
- 在业务层对输入数据进行严格过滤
- 使用框架提供的安全查询方法替代原始'exp'表达式
攻击流程图
用户恶意输入 → Request::get() → Query::where() → parseWhereExp()
↓
Builder::select() → buildWhere() → parseWhereItem('exp')
↓
直接拼接进SQL → SQL注入执行
总结
该漏洞展示了框架设计时安全考虑不足可能导致的问题。虽然官方将其视为功能,但开发者应当理解其风险并采取适当防护措施。关键在于永远不要信任用户输入,所有数据在进入SQL查询前都应进行适当验证和过滤。