SQL 注入系统学习(四):布尔与时间盲注
本文仅用于 CTF、靶场、本机实验和明确授权环境。时间测试应控制延时、并发和请求数量,避免影响环境可用性。
盲注的核心不是“爆破”,而是把看不到的数据改写成一系列能够观察的真假问题。
1. 什么是布尔盲注
页面不显示数据库内容,但条件真假会产生稳定差异:
1 | 条件为真 → 页面出现 Welcome |
在本地靶场中:
1 | 1 AND 1=1 |
如果两者稳定对应不同页面,就建立了布尔反馈通道。
2. 先找稳定特征
不要只比较完整响应长度,因为页面可能包含随机数、时间戳或动态 Token。
优先寻找:
1 | 固定文字是否存在 |
每组真假条件都应重复验证。
3. 判断长度
假设目标是当前数据库名:
1 | LENGTH(DATABASE()) = 4 |
长度函数替代:
1 | LENGTH |
如果目标可能为 NULL,应先判断:
1 | target IS NULL |
因为 LENGTH(NULL) 返回 NULL,不能和普通空字符串混为一谈。
4. 逐字符判断
1 | SUBSTRING(DATABASE(),1,1) = 'a' |
同一个目标可以通过字符、数字、十六进制或模式比较表达。
5. 二分法
假设字符位于可打印 ASCII 范围 32~126:
1 | ASCII(SUBSTRING(value,position,1)) > midpoint |
每次根据真假舍弃一半范围:
1 | low = 32 |
一个字符通常只需约 7 次判断,比逐个枚举高效得多。
6. 二分最容易写错的地方
1 | 把 > 和 >= 混用 |
在写自动化脚本前,应手工验证一个已知真条件和一个已知假条件。
7. 时间盲注
页面内容完全相同,但条件真假产生稳定耗时差异:
1 | IF(condition,SLEEP(2),0) |
判断流程:
1 | 测量正常请求分布 |
单次响应慢不能证明时间盲注。
8. SLEEP 被过滤
先判断失效原因:
1 | WAF 是否拦截函数名 |
替代顺序:
1 | 优先使用布尔反馈 |
不要使用超大笛卡尔积或高负载表达式制造延时。
9. secure_file_priv 的时间判断示例
secure_file_priv 限制 MySQL 文件导入导出的目录。读取它之前应区分三种状态:
1 | NULL 相关文件能力被禁用 |
9.1 判断 NULL
1 | @@global.secure_file_priv IS NULL |
9.2 判断长度
1 | LENGTH(@@global.secure_file_priv) > 10 |
9.3 判断第一个字符
1 | ASCII(SUBSTR(@@global.secure_file_priv,1,1)) > 64 |
9.4 转换为时间反馈
1 | SELECT IF( |
它只回答一个真假问题,并不会直接返回目录。需要先判断 NULL、再判断长度、最后逐位置二分。
10. MYSQLI_INIT_COMMAND 特殊场景
某些 CTF 题允许用户控制 MySQL 客户端连接选项,其中初始化命令会在连接建立后执行 SQL:
1 | 用户可控连接选项 |
这不属于典型的“HTTP 参数拼进 SELECT”,但只要能执行表达式而没有回显,盲注思想仍然适用。
11. 自动化脚本结构
1 | def is_true(condition): |
脚本应该保存请求次数、失败重试和原始响应证据,不要把一次超时直接算作真。
12. 常见失败原因
1 | 真条件和假条件都返回其他记录 |
13. 防御
使用参数化查询;限制数据库账户权限;不允许用户控制连接选项;设置连接和语句超时;监控大量重复条件请求与异常延时。
下一篇整理 WAF 和输入限制:怎样区分拒绝、删除、转义和类型转换,以及空格、引号、逗号、运算符和关键词受限时如何分析。