报错注入
性质:报错注入是利用数据库的某些机制,人为制造错误条件,在报错信息中返回完整的查询结果。
涉及函数或符号:
floor()
extractvalue()
updatexml()
下面介绍extractvalue()函数报错注入的方法:
条件MySQL版本号大于等于5.1.5
extractvalue()函数的语法如下:
extractvalue(XML_document, XPath_string);
第一个参数XML_document是String格式,表示XML文档对象的名称;第二个参数XPath_string表示Xpath格式的字符串。
extractvalue()函数的作用是从目标XML中返回包含所查询值的字符串。当第二个参数不符合Xpath语法时,会产生报错信息,并将查询结果放在报错信息中。
示例SQL语句:
1 and extractvalue(1,concat(0x7e,(select database()),0x7e)); #
extractvalue()函数最长报错输出32位,在注入时若输出不完整要善用切片函数如substr()等获取完整数据。
下面简单介绍一下updatexml()函数报错注入的方法:
条件MySQL版本号大于等于5.1.5
updatexml()函数使用不同的XML标记匹配和替换XML块。
函数语法如下:
updatexml(XML_document,Xpath_string,new_value)
第一个参数是XML_document是string格式,表示XML文档对象的名称;第二个参数XPath_string代表路径,是XPath格式的字符串;第三个参数new_value是string格式,用来替换查找到的符合条件的数据。
示例SQL语句:
1 and updatexml(1,concat(0x7e,(select database())),1); #
下面介绍floor()函数报错注入的方法:
条件MySQL版本号需要大于或等于4.1
需要利用floor()函数产生预知的数字序列01101……,然后利用rand()函数的特殊性和group by语法中的虚拟表,引起报错。
SQL语句:select count(*),concat(floor(rand(0)*2),0x3a, 执行语句) as x from 表名 group by x #
这个报错语句的格式相对固定
具体流程如下:
1、获取当前数据库名
select count(*),concat(floor(rand(0)*2),0x3a, database()) as x from information_schema.tables group by x #
2、获取表名
select count(*),concat(floor(rand(0)*2),0x3a, (select table_name from information_schema.tables where table_schema=‘db' limit 0,1)) as x from information_schema.tables group by x #
3、获取列名
select count(*),concat(floor(rand(0)*2),0x3a, (select column_name from information_schema.columns where table_name=‘table' limit 0,1)) as x from information_schema.tables group by x #
4、获取数据
select count(*),concat(floor(rand(0)*2),0x3a, (select flag from db.flag limit 0,1)) as x from information_schema.tables group by x #
floor()报错原理:
函数介绍:
rand()函数是一个随机函数,产生0~1的小数,通过设置一个固定的随机数种子0之后,可以形成固定的伪随机序列,导致数据出现可预测性。
floor()函数的作用是向下取整,因此在rand(0)后要*2
rand(0)=>0~1
rand(0)*2=>[0~2)
那么floor(rand(0)*2)的结果只有两种可能,0和1
group by的作用是分组,相同的数据会分到同一组内,分组的原理很简单,首先会生成一张临时的表,插入临时表之前会先查询临时表中是否有对应的key,没有就插入,有则不执行插入
count(*)是统计数量的函数,如果结合group by就是统计对应分组的数据数量。
原理分析:
floor(rand(0)*2)会产生可以预测的固定序列0110110011……,此时结合group by会产生一个虚拟表,具体过程如下:
floor(rand(0)*2)第一次计算取值0,
group by查询虚拟表发现没有0的键值,开始执行插入键值操作;
此时floor(rand(0)*2)进行第二次计算取值1,group by插入虚拟表
| key | count(*) |
|---|---|
| 1 | 1 |
第一条插入数据为1;
floor(rand(0)*2)进行第三次计算取值1,group by查询到存在键值,所以直接count(*)+1,更新虚拟表
| key | count(*) |
|---|---|
| 1 | 2 |
floor不再进行计算;
floor(rand(0)*2)开始进行第四次计算取值0,group by查询后发现没有0的键值,开始执行插入操作,此时floor(rand(0)*2)执行第五次计算取值1,然而1这个主键已经存在于虚拟表中了,所以就产生了主键冲突的错误,也就是Duplicate entry。
| 操作 | key | floor(rand(0)*2) | count(*) |
|---|---|---|---|
| 取第一条数据 | 0 | ||
| 插入数据 | 1 | 1 | 1 |
| 取第二条数据不执行插入 | 1 | 1 | 2 |
| 取第三条数据 | 0 | ||
| 取第四条数据 | 1 主键冲突 | 1 |
总结:
在虚拟表中写入第三条数据时,产生了报错,此时floor(rand(0)*2)一共被计算了5次,所以数据表中至少需要3条数据才会导致报错,此外还要注意随机数种子的问题,否则生成的序列可能会无法满足我们的预期。