Floor 报错与原理分析

基础知识

floor() # 去除小数部分 
rand()  # 产生随机数
rand(x) # 以 x 为种子产生随机值,以个 x 对应一个固定的值,但是如果在一个语句中连续执行多次值会变化,不过也是可预测的

floor 报错 payload:

select count(*), floor(rand(0)*2) a from information_schema.tables group by a;

原理分析

这个 payload 的关键是 group by agroup by floor(rand(0)*2)floor(rand(0)*2) 意思是随机产生 0 或 1,但事实上并不是,其每次执行的结果都是固定的,是 011011... 这个序列。

那为什么会报错呢?先来解释一下 count(*)group by 是如何共同工作的:

count(*) 与 group by 共同工作流程

首先,系统会建立一个虚拟表:

假设有表:

执行 select count(*) from table_name group by age 的过程中会形成这样的虚拟表:

它是如何一步步形成更这张表的呢?由于 group by 的是 age,第一次读取的就是 18,在虚拟表中寻找是否存在 18, 由于表示空的,直接插入一条新数据,这时虚拟表变成这样:

继续。下一个是 19,由于虚拟表中没有 key 为 19 的字段,故插入。在下一个 20,继续插入。在下一个是 20,由于已经有 20,故将 key 为 20 的字段的 count(*) 值加一,变成 2 。 剩下的依次类推形成最后的虚拟表。

group by 与 floor 结合

我们的 playload 的关键部分为 group by floor(rand(0)*2),我们已知 floor(rand(0)*2) 产生固定序列 011011...,现在我们生成 group bycount(*) 结合的虚拟表:

首先建立一张虚拟表:

接着进行 group by floor(rand(0)*2)floor(rand(0)*2) 第一次运算的值为0,在表中没有找到 key 为 0 的数据,故插入,在插入的过程中需要在去一次group by后的值(即在进行一次 floor(rand(0)*2) 运算,结果为1),取到了 1,故将其插入,并将 count(*) 置为1:

继续,再次进行 group by floor(rand(0)*2),进行 floor 表达式运算,这是第三次运算,故值为 1,刚好表中有 key 为 1的数据,因此直接将对应的 count(*) 加一:

继续,再次进行 group by floor(rand(0)*2),进行 floor 表达式运算,这是第四次运算,根据011011.. 序列,这次的值为 0,在表中没有找到 key 为 0 的数据,因此需要插入,在插入时需要再次执行一次 floor(rand(0)*2),但此次得到的值为 1,并将 count(*) 置为 1,但是此时虚拟表中已经有了key为 1 的数据了,会抛出主键冗余的异常,这就是 floor 报错。

利用:

select count(*), concat(0x3a, (select database()), 0x3a, floor(rand(0)*2)) a from information_schema.tables group by a;

select database() 换成想要查询的信息即可。

原文链接: Floor报错原理分析