ThinkPHP 5.0.16参数绑定缺陷核心在于数组参数解析逻辑:当$where条件含形如['exp', 'updatexml(...)']的三元数组时,parseData()方法直接走Db::raw()拼接SQL,跳过绑定,导致报错注入;默认strict=false和app_debug=true进一步放大风险。

ThinkPHP 5.0.16 的参数绑定缺陷,核心不在“有没有绑定”,而在于**哪些地方本该绑定却跳过了、哪些绑定被绕过了、哪些绑定对象本身可控**。直接搜 bindParam 或 bindValue 很难见效,因为 TP5 封装了底层操作,真正问题往往藏在框架对数组参数的解析逻辑里。
从入口开始盯住“数组型参数”的流转路径
TP5.0.16 中最典型的绑定缺陷出现在使用数组作为查询条件时(如 where('id', 'in', $arr) 或 whereIn('status', $arr))。关键不是数组内容,而是数组结构本身是否被信任。
- 访问任意带参数的路由,比如
/index.php?username[0]=exp&username[1]=updatexml(1,concat(0x7e,user(),0x7e),1)&username[2]=1,观察响应——若报错中出现 MySQL 函数执行结果(如~root@localhost~),说明数组第二位被原样拼入 SQL - 重点检查控制器中类似
Db::table('user')->where($map)->select()的写法,其中$map来自input()或request()->param(),且未做键名白名单限制 - 跟进
where()调用链:最终会走到think\db\Builder->parseWhere()→parseData()→parseKey();此时若传入的是形如['inc', 'updatexml(...)', 1]的三元数组,parseKey()不清洗值,直接拼进 SQL 字符串
重点审计 Builder 类中的 parseData 方法
thinkphp/library/think/db/Builder.php 是漏洞高发区。5.0.16 的 parseData() 对数组值缺乏类型校验,尤其当数组首元素为 inc、exp、neq 等特殊标识时,框架会将其视为“表达式模式”,跳过参数绑定,改走字符串拼接。
- 打开
Builder.php,定位parseData()方法,查找if (is_array($val) && isset($val[0]))分支 - 关注
switch ($val[0])后的处理:若$val[0] === 'exp',则直接返回Db::raw($val[1]),而$val[1]完全由用户控制 - 验证方式:在该方法内加
var_dump($val); die;,再发包触发,确认恶意 payload 是否真的进入此分支
检查数据库配置与调试模式是否放大风险
即使代码存在缺陷,若环境配置得当,也可能阻断利用链。但 5.0.16 默认配置常埋雷:
立即学习“PHP免费学习笔记(深入)”;
- 确认
config/database.php中'strict' => false(默认值),这会让 MySQL 宽松解析非法语法,更容易触发报错注入 - 检查
config/app.php中'app_debug' => true是否开启——开启后报错会泄露完整 SQL 和路径,极大降低利用门槛 - 若项目启用了
think\facade\Db::execute()或手动拼接 SQL(如"SELECT * FROM user WHERE id IN (" . implode(',', $ids) . ")"),这类写法完全绕过框架绑定机制,属于硬编码缺陷
用最小验证 payload 快速确认是否存在绕过
不依赖复杂工具,一条请求即可试探绑定是否失效:
- GET 请求:
?id[0]=exp&id[1]=extractvalue(1,concat(0x7e,(select%20user()),0x7e)) - POST 请求(Content-Type: application/x-www-form-urlencoded):
name[0]=exp&name[1]=updatexml(1,concat(0x7e,database(),0x7e),1) - 若响应中出现 MySQL 报错且含数据库名或用户名,说明
exp模式被接受,参数绑定被绕过;若返回空或 500 但无敏感信息,则需深入看是否过滤更严或需要其他触发条件



















