在sql报错注入中有一种floor()型的题,它通常以count(*),floor(rand(0)*2)...group by作为payload实现报错。那么它报错的具体原由是什么呢?
才没有在水博客🤫
在这之前,我们首先该理清这些mysql语法的作用
- rand():从0-1之间随机返回一个浮点数,rand(value)则为rand定制了一个种子,使得rand返回为一个固定的伪随机数列。当这个函数被调用时,就会从这个数列里依次抽取返回值
- count()...group by expr*:mysql的分组函数,这两个语句必须搭配在一起,group by* expr*负责根据后面的表达式的返回值作为gruop_key进行分组,而count负责对各组的数据进行计数
- floor():向下取整,就是只舍不入
我们再详细讨论一下group by的分组逻辑
首先必须要注意的是一个组只能对应一个key值,这也是报错诱发原因
- 取得一条数据,根据表达式计算的返回值作为group_key,根据group_key检查它的组是否存在。如果之前存在相等的group_key,则分到对应的组
- 如果这个group_key是个新值,则以这个key值新建一个组,并把数据放在这个新组中。但要注意,在mysql插入新组之前还会再次计算一遍表达式的key值用来检查,然后用这个检查计算得到的key建立新组,这就是group by的双重调用
然后我们再详细解析为什么按floor(rand(0)*2)表达式分组会报错
从内而外,rand(0)定义了一个返回0-1的浮点数的伪随机数列,rand(0)*2使它的范围扩大到了0-2,而floor则使所有可能返回的浮点数向下取整为整数,使rand(0)*2变成只会返回0或1的固定数列。调用时会按照序列依次返回值
所以floor(rand(0)*2)其实是一组固定序列:
0 1 1 0 1 1...
我们再按照group by的逻辑分组:
- 取第一条记录,表达式返回为0,由于没有此类key值所以新建key,在建组之前进行检查,再次调用表达式,结果得到1,所以新建了一个key值为1的组
- 取第二条记录,表达式返回为1,已经有key为1的组了,所以分组成功,count++
- 第三条记录就出问题了,表达式先返回0,由于没有这个组,所以group by新建一个组,结果建组前检查再次调用了表达式使key变成了1,然后建立了一个key=1的新组,但第2步时已经建立了一个key=1的组,这就导致了两组key值冲突,不符合一组一值原则,进而引发了报错
这就是floor报错的缘由,不得不说发现这个报错方式的人简直是个天才,构造了如此精妙的payload
