报错型sql注入原理研究学习

前言

Mysql在执行语句的时候会抛出异常信息信息,而php+mysql架构的网站往往又将错误代码显示在页面上,这就让一些不法分子有机会从中获取敏感信息

通过floor报错

利用方式

1
?id=1' and (select 1 from (select count(*),concat(floor(rand(0)*2),(select(select(报错语句)) from information_schema.tableslimit 0,1))x from imformation_schema.tables group by x)a)--+

公式解析

1
2
3
4
5
6
7
8
9
10
11
12
13
floor() 是向下取整数
rand()01之间产生一个随机数
rand(0)*2 将取02的随机数
floor(rand()*2) 有两条记录就会报错
floor(rand(0)*2) 记录需要3条以上,且3条以上必报错,返回的值是有规律的
count(*)是用来统计结果,相当于刷新一次结果
group by 在对数据进行分组时会先看看虚表中有没有这个值,没有的话就插入 存在的话count(*)加1,在使用group by 时floor(rand(0)*2)会被执行一次,若虚表不存在记录,插入虚表会在执行一次

报错过程

  1. rand()用于产生一个0~1的随机数
  2. floor()向下取整
  3. rand()函数生成0~1的整数,向下取整,值是固定的’0’,将rand*2,得到的值就是不固定的,’0’或’1’
  4. concat()将符合条件的同一列中的不同列数据拼接,0x3a是十六进制的”:”
  5. 将之前的rand()函数和floor()函数整合起来
  6. 查询名字太长,起一个别名
  7. 再次查询.information_schema.tables有多少表,会显示多少列
  8. group by 依据我们想要的规矩对结果进行分组
  9. count()统计元组的个数
  10. 接着,多重复几次

floor(rand()2)与floor(rand(0)2)的不确定性与确定性

floor(rand()*2)不加随机因子的时候是随机出错的,而在3条记录以上用floor(rand(0)*2)就一定报错,由此可猜想floor(rand()*2)是比较随机的,不具备确定性因素,而floor(rand(0)*2)具备某方面的确定性。

通过测试发现,floor(rand()*2)毫无规律可言,而floor(rand(0)*2)是有规律的

那么mysql在遇到`select count(*)from tables group by x`这句话(实际就是建立虚拟表)
1.先建立虚拟表,其中key是主键,不可重复
2.开始查询数据,取数据库数据,然后查看虚拟表存在不,不存在则插入新纪录,存在则count(*)字段直接加,即如果key存在就+1,不存在话就新建一个key

其实mysql官方有给过提示,就是查询的时候如果使用rand()的话,该值会被计算多次,那这个“被计算多次”到底是什么意思,就是在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次,我们来看下floor(rand(0)*2)报错的过程就知道了,从0x04可以看到在一次多记录的查询过程中floor(rand(0)*2)的值是定性的,为`011011…`(记住这个顺序很重要),报错实际上就是floor(rand(0)*2)被计算多次导致的。

完整过程

  1. 查询前默认建立空虚拟表
  2. 取第一条记录,执行floor(rand(0)2),发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)2)会被再计算一次,结果为1(第二次计算),插入虚表,这是第一条记录
  3. 查询第二条记录,再次计算floor(rand(0)2),发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)2)不会被计算第二次,直接count(*)加1,第二条记录查询完毕
  4. 查询第三条记录,再次计算floor(rand(0)2),发现结果为0(第四次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的的数据,再插入数据时floor(rand(0)2)被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在与虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了

整个查询过程floor(rand(0)*2)被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。

updatexml

MySQL5.1.5版本中添加了对XML文档进行查询的修改的函数,分别是updatexml()和extracvalue()

1
2
3
4
5
6
7
8
9
10
11
12
mysql> use test;
Database changed
mysql> create table users
-> (
-> id int(3) NOT NULL AUTO_INCREMENT,
-> username varchar(20) NOT NULL,
-> password varchar(20) NOT NULL,PRIMARY KEY (id)
-> );
Query OK, 0 rows affected (1.74 sec)
mysql> insert into users(id,username,password) values (1,'admin','123456');
Query OK, 1 row affected (0.00 sec)

执行报错的payload:

1
2
3
4
5
6
7
mysql> select 1,2,3 and updatexml(1,concat(null,(select @@version),null),1);
+---+---+-----------------------------------------------------------+
| 1 | 2 | 3 and updatexml(1,concat(null,(select @@version),null),1) |
+---+---+-----------------------------------------------------------+
| 1 | 2 | NULL |
+---+---+-----------------------------------------------------------+
1 row in set (0.00 sec)

报错原因:

  1. updatexml第二个参数需要的是Xpath格式的字符串,我们输入的格式显然不符合,故故障由此报错
  2. uodatexml的最大长度是32位的,所以有局限(PS:但是应对大多的已经足够。)如果密码长度超过了32位就不会被显示出来。

参考链接

------*** end*** ------