双查询注入

原理:

  rand():随机函数

  floor():取整函数

  count():汇总函数

  group by values:分组函数

  当在一个聚合函数,比如count函数后面使用分组语句就会把查询的一部分以错误的形式显示出来;

先来看看子查询 select,就是在一个select语句中还含有一个select语句叫做子查询;

看个例子: Select concat ((select database()));

在查询的时候,子查询的select会把当前的数据库反馈给concat函数 (concat函数就是用来连接里面的结果)

1
2
3
4
5
6
7
mysql> select rand();
+--------------------+
| rand() |
+--------------------+
| 0.6080478007048814 |
+--------------------+
注:rand函数是一个随机函数,所以每次出现的值不一样;会出现0-1之间的值;

接下来执行floor函数

1
2
3
4
5
6
7
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
| 1 |
+-----------------+
注:floor函数是把小数进行取整;

rand() 返回大于0小于1的小数,乘以2之后就成了小于0小于2了。然后对结果进行取整。就只能是0或1了。也就是这个查询的结果不是1,就是0

接下来进行测试

1
2
3
4
5
6
7
mysql> select concat((select database()),floor(rand()*2));
+-------------------------------------------------+
|concat((select database()),floor(rand()*2)) |
+-------------------------------------------------+
| security0 |
| security1 |
+-------------------------------------------------+

SELECT database() 这个就返回数据库名,这里就是security了。然后FLOOR(RAND()*2)这个上面说过了。不是0,就是1.然后把这两个的结果进行concat连接,那么结果不是security0就是security1了。

接下来再加入group by 语句

1
2
3
4
5
6
7
8
9
mysql> select count(*),concat((select database()),floor(rand()*2)) as test from
information_schema.tables group by test;
+----------+-----------+
| count(*) | test |
+----------+-----------+
| 33 | security0 |
| 49 | security1 |
+----------+-----------+
2 rows in set (0.00 sec)

我们把concat((select database()), floor(rand()*2)) 这个结果取了一个别名test ,然后使用他进行分组。这样相同的security0分到一组,security1分到一组。

这里的database()可以替换成任何你想查的函数,比如version(), user(), datadir()或者其他的查询。比如查表啊,查列啊。

最后再加上count(*)函数

1
select count(*), concat((select database()), floor(rand()*2))as qqq from information_schema.tables group by qqq;

count函数

count()是一个聚合函数,对于返回的结果集,一行行地判断,如果count函数的参数不是NULL,累计值就加1,否则不加。最后返回累计值。

*count(id)**

InnoDB引擎会遍历整张表,把每一行的id值都取出来,返回给server层。server层拿到id后,判断是不可能为空的,就按行累加。

count(1)

InnoDB引擎遍历整张表,但不取值。server层对于返回的每一行,放一个数字1进去,判断是不可能为空的,按行累加。

count(字段)

如果这个“字段”是定义为not null的话,一行行地从记录里面读出这个字段,判断不能为null,按行累加;如果这个字段定义允许为null,那么执行的时候,判断到有可能是null,还要把值取出来再判断一下,不是null才累加。

count(*)

不会把全部字段取出来,而是专门做了优化,不取值。count(*)肯定不是null,按行累加。

按照效率排序的话,count(字段)<count(主键id)<count(1)≈count(),所以建议尽量使用count()

count(id)虽然走的索引,但是还是需要一行一行的扫描才能统计出来总数,所以效率没有那么高。

*“通过floor报错的方法来爆数据的本质是group by语句的报错。group by语句报错的原因是floor(random(0)2)的不确定性,即可能为0也可能为1(group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中则更新临时表中的数据;如果该key不存在于临时表中,则在临时表中插入key所在行的数据。group by floor(random(0)2)出错的原因是key是个随机数,检测临时表中key是否存在时计算了一下floor(random(0)2)可能为0,如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时floor(random(0)2)结果可能为1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值。具体原理参考:http://www.mysqlops.com/2012/05/15/mysql-sql-analyze.html)。”

结论是:当与临时表里面的值进行比较,如果不同,就插入,但是插入的时候又计算了一次,所以如果插入时计算的值与直接比较的值不一样,则报错!