thinkphp sql盲注漏洞本质是开发者绕过安全机制,低版本中exp/parsedata组合导致未过滤拼接,修复需禁用exp用户输入、强制参数化、最小化数据库权限并手工验证。

ThinkPHP 框架的 SQL 盲注漏洞,本质不是框架“自带后门”,而是开发者在动态构造查询时绕过了框架的安全机制,尤其在低版本(如 5.0.13–5.0.15、5.1.0–5.1.5)中,exp、inc、dec 等链式方法若配合用户可控参数使用,极易触发未过滤的 SQL 拼接,为布尔或时间盲注提供温床。
漏洞成因:exp() 和 parseData() 的危险组合
ThinkPHP 的 where('id', 'exp', 'IN (1,3,8)') 这类写法本意是支持灵活表达式,但 exp 参数不参与参数化处理,直接拼入 SQL 字符串。当该值来自用户输入(如 $_GET['order']),且未经白名单校验,攻击者就能注入 AND IF(1=1,SLEEP(3),0) 等 payload。
关键路径是:Builder->insert() → 调用 parseData() → 若字段值含 exp 类型,进入字符串替换逻辑(str_replace)→ 恶意 SQL 被原样拼入执行。
此时即使开启了调试模式关闭、错误不回显,只要响应时间或页面状态存在可区分差异,就构成典型的时间型或布尔型盲注。
修复核心:堵死非参数化入口
不能依赖“加个过滤”或“转义单引号”,必须从调用源头切断可控数据进入 SQL 结构的机会:
-
禁用 exp() 接收用户输入:所有
where(..., 'exp', $user_input)必须重写。改用白名单映射,例如:$allowed = ['status=1', 'type IN (1,2)', 'is_deleted=0']; if (in_array($userInput, $allowed)) { where('exp', $userInput); } -
升级并强制使用参数化语法:TP 5.1.12+ 和 6.x 已默认对
where()中的普通字段值启用预处理。确认配置中'params' => true生效,且避免混用Db::query()原生拼接 -
拦截危险链式操作:在中间件或基类中全局拦截
setInc($field, $value)、setDec()的$value参数——只允许数字类型,拒绝字符串表达式
加固细节:不止于代码层
盲注成功往往依赖前期侦察,需同步收紧数据库和运行环境:
-
数据库权限最小化:应用账号禁止
EXECUTE权限(禁用SLEEP、BENCHMARK),撤销SELECT对INFORMATION_SCHEMA表的访问,防止自动探测表名/列名 -
关闭元信息泄露:TP 配置中设
'show_error_msg' => false,PHP 层禁用display_errors;MySQL 启用sql_mode = STRICT_TRANS_TABLES,减少模糊报错带来的字段名提示 -
监控异常行为:在 Nginx 或 WAF 层记录平均响应时间突增(如 >2s)的请求,特别关注含
IF(、SLEEP(、BENCHMARK(等关键词的 GET/POST 参数
验证是否修复到位
修复后务必手工验证,不能仅靠工具扫描:
- 尝试传入
?id[exp]=1 AND SLEEP(3),观察响应是否仍延迟; - 用布尔型 payload 测试状态变化:
?name[exp]=username='admin' AND 1=1与1=2是否导致页面内容或 HTTP 状态码出现可区分差异; - 检查日志中是否存在未被拦截的
where.*exp.*\$\_类调用痕迹。











