赞
踩
报错注入的使用场景一般是在页面无法显示数据库的信息,但是是有报错内容的,我们就可以尝试利用报错手段报错将错误信息与子查询语句查询到的内容带出,当然,并不局限于这种场景,只要是有将mysql错误输出,都可以尝试进行报错注入。
报错注入形式上一般是做两个嵌套的查询,里面的那个查询被称为子查询,执行顺序也是从子查询开始的,所以我们可以通过子查询查询我们想要的数据,然后在通过报错函数将我们查询到的数据带出,从而达到爆出想要查询的数据。
UPDATEXML (XML_document, XPath_string, new_value);
updatexml是更新xml文档的函数,作用是改变文档中符合条件的节点的值。
举个简单例子:
第二个参数必须是一个Xpath的字符串,比如应该像data/user/name这种格式,这有点像我们通过html标签 < div>< p>< a> 查找元素一样,但如果我们我们写入其他错误的格式,就会报错,并且会返回我们写入的非法格式内容,比如:~ payload ~,而这个非法的内容我们可以通过子查询来变成我们想要查询的内容。
需要注意的是,updatexml报错函数的结果有32位的长度限制,也就是说它默认只会输出32位的结果,所以我们需要通过substr(),mid(),left()等截断取字符函数去截取大于32位的数据。
extractvalue() :对XML文档进行查询的函数
extractvalue() 是对文档进行查询,作用:使用XPath符号从XML字符串提取值。
举个例子:
这个其实用法和updatexml()是差不多的,也是通过输入错误的Xpath格式来引发mysql报错从而带出数据
extractvalue()也是一样,只能显示32位数据,我们还是可以用截取函数进行读取。
在这需要注意一点,mysql版本小于5.1.5是不能用ExtractValue和UpdateXML进行报错注入,因为MySQL是在5.1.5版本才添加了对XML文档进行查询:ExtractValue()和修改XML的函数:UpdateXML()。
在这时候,我们就可以floor()函数进行报错,这个报错函数在Mysql5.0及以上版本都是能用的,一般实战中还是floor()用的还是比较多。
在这里贴一篇文章,我以前刚入门的时候,也是看这篇文章的
https://wooyun.js.org/drops/Mysql%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%28count%28%29%E3%80%81rand%28%29%E3%80%81group%20by%29.html
这里需要先了解几个函数:
rand()随机函数,返回0~1之间的某个值
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by clause 分组语句,按照查询结果分组
一般网上 floor报错注入的payload都是这样的:
select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
floor()都是直接放在count(*)的后面,其实这不一定需要这么做,在这里只是起了一个别名x,所以我们可以将payload改成:
select count(*) from information_schema.tables group by (floor(rand(0)*2));
其实效果都是一样的
在这就能看出floor是能成功报错并带出了想要查询的消息的,那实现floor报错需要什么条件呢?
1、mysql版本大于5.0
2、查询表记录数必须在3条或3条以上
3、rand()需要有随机因子rand(0)
查询表记录数:
我们先建张表,并且在里面先插入一条数据:
我们这时执行报错语句:
发现并不会报错。
我们再插入一条记录,然后在执行报错语句:
执行多次依然不会报错。
我们继续插入一条记录,然后再次执行:
发现插到第三条记录就报错,经上述实验发现,表记录在三条或以上就会报错。
这里你可能发现了,在使用子查询的时候,这里给它起来一个别名a,这是因为sql语句是无法为子查询指定一个表名,但却要把它做为一个表来使用,所以在这需要人为地加一个别名,以别名或用户指定的名称来引用这些派生表。
随机因子rand(0)影响是否报错?
我们试着将随机因子去掉,变成rand(),然后再从一条记录开始执行,我们来看看报错是否与随机因子。
一条记录的话,还是一样,执行多少次都不会报错。
在加一条记录,再来执行:
结果变成不确定性,也就是执行后随机报错。
插入三条数据,也还是随机发生错误。
由此可知,报错确实是与随机因子是有关联的,而这也证明随机因子具有确定性,在三条记录或以上一定会报错。
随机因子的确定性
我们可以通过下面一个实验进一步证实随机因子的确定性:
分别在一张有10条记录以上的表执行floor(rand()*2)和floor(rand()*2)多次,对比一下结果:
无随机因子:floor(rand()*2)
发现无任何规律
接着加上随机因子floor(rand(0)*2),再次执行:
可以看到执行结果是相同的,也进一步确定了随机因子的确定性,也就是导致报错的原因。
count 与group by 的虚拟表:
我们经常通过聚合函数和group by对查询对象进行计数并按照查询结果进行分组。
mysql在进行聚合函数查询分组的时候,会建立一张虚拟表,如下图所示:
(其中key是主键,不可重复)
接着开始查询数据,如果数据不存在,就插入新纪录,如果存在的话,count字段就+1。
如下图:
由此看到 如果key存在的话就+1, 不存在的话就新建一个key。
floor(rand(0)*2)报错:
其实mysql官方有给过提示,就是查询的时候如果使用rand()的话,该值会被计算多次,那这个“被计算多次”到底是什么意思,就是在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次,我们来看下floor(rand(0)*2)报错的过程就知道了,从0x04可以看到在一次多记录的查询过程中floor(rand(0)*2)的值是定性的,为011011…(记住这个顺序很重要),报错实际上就是floor(rand(0)*2)被计算多次导致的,具体看看select count(*) from keepbu1e group by floor(rand(0)*2);的查询过程。
查询前默认创建虚拟表:
取第一条记录,执行floor(rand(0)*2),发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则在插入的时候,floor(rand(0)*2)会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕,如下图:
3.查询第二条记录,再次计算floor(rand(0)*2),发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)*2)不会被计算第二次,直接count(*)加1,第二条记录查询完毕,结果如下:
4.查询第三条记录,再次计算floor(rand(0)*2),发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了。
5.整个查询过程floor(rand(0)*2)被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。
floor(rand()*2)报错
同样我们也可以根据上文推理出不加入随机因子的情况,由于没加入随机因子,所以floor(rand()*2)是不可测的,因此在两条数据的时候,只要出现下面情况,即可报错,如下图:
当前面记录让虚表长成这样子后,由于不管查询多少条记录,floor(rand()2)的值在虚表中都能找到,所以不会被再次计算,只是简单的增加count()字段的数量,所以不会报错,比如floor(rand(1)*2),如图:
在前两条记录查询后,虚拟表已经存在0和1两个键值了,所以后面再怎么弄还是不会报错。
总之报错需要count(*),rand()、group by,三者缺一不可。
以下是在Mysql 5.0版本中存在的函数但是不会报错,5.1后才可以报错:
geometrycollection()multipoint()polygon()multipolygon()linestring()multilinestring()
MySQL>5.0:
floor()
在MySQL5.7中新增了很多能报错的函数:
ST_LatFromGeoHash()ST_LongFromGeoHash()GTID_SUBSET()GTID_SUBTRACT()ST_PointFromGeoHash()
exp() :
在mysql5.5之前,整形溢出是不会报错的,根据官方文档说明out-of-range-and-overflow,只有版本号大于5.5.5时,才会报错。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。