ThinkPHP 5.1.25聚合函数注入漏洞源于用户可控字段名未经校验直接拼入SQL。需排查所有max/min/count/sum等调用处,检查首参是否来自input()等用户输入,并验证parseKey是否绕过过滤,构造id)#`类POC探测,修复应采用白名单或升级至5.1.26+。

ThinkPHP 5.1.25 的聚合函数注入(如 max、min、count、sum 等)本质是字段名参数被用户直接控制,而框架未做安全校验,导致 SQL 字段上下文逃逸。排查关键在于识别“谁在把用户输入当字段名用”,而不是查数据值。
确认是否调用了聚合方法且参数可控
搜索项目中所有使用 db()->max()、->min()、->count()、->avg()、->sum() 的地方,重点关注其第一个参数(即字段名)是否来自用户输入:
- 检查
input('field')、request()->get('col')、$this->request->param('key')等接收方式传入聚合函数 - 特别注意控制器中类似
db('user')->max(input('col'))或$model->count($request->param('by'))的写法 - 聚合函数若传入的是固定字符串(如
'id'、'status')或常量,则不构成风险
验证 parseKey 是否绕过过滤
ThinkPHP 5.1.24–5.1.25 中,parseKey 方法在聚合场景下(第三个参数为 true)会直接拼接反引号,不校验内容合法性。可快速验证:
- 在目标聚合调用前加断点,跟进到
thinkphp/library/think/db/Builder.php的aggregate方法 - 观察是否调用了
$this->parseKey($key, $strict = true) - 进入
Mysql.php的parseKey,确认逻辑为:return '`' . $key . '`',且无正则过滤或白名单限制
构造最小化 PoC 验证漏洞存在
无需复杂 payload,用基础语法探测即可确认是否可注入:
立即学习“PHP免费学习笔记(深入)”;
- 访问类似
/index/index/test?col=id%60)%23(URL 编码后为id`)#) - 若返回 MySQL 语法错误(如
Unknown column 'id`)#'或You have an error in your SQL syntax),说明字段名已拼入 SQL - 进一步尝试
col=id%60)from%60users%60where%60id%60=1%23,看是否能触发查询逻辑变化或报错信息泄露 - 注意:需确保表名和字段名真实存在,否则前置语法错误会掩盖注入点
修复与加固建议
不能依赖输入过滤,必须从调用源头切断风险:
- 聚合函数的字段名应限定为白名单数组,例如:
$allowed = ['id', 'price', 'create_time']; if (!in_array($col, $allowed)) throw new Exception('Invalid field'); - 避免动态拼接字段名,改用条件分支:
if ($col === 'price') { $result = db()->sum('price'); } - 升级至 ThinkPHP 5.1.26+(该版本已修复
parseKey在聚合场景下的缺陷) - 禁用调试模式(
'app_debug' => false),防止错误信息暴露 SQL 结构



















