你真的懂floor报错注入嘛

好久好久没写博客了。。因为我在准备一个对我非常重要的考试,最近忽略了学习安全。今天抽时间回头重温了一下floor的报错注入,收获颇多

以前在研究SQL注入时只理解表面却不得精髓,很多原理都是一知半解。所以才有了本篇。。

一般的floor的报错语句为

1
select count(*) from user group by concat(database(),floor(rand(0)*2));

那么他是怎样报错 如何报错的?

报错函数分析

image-20201104143929317

这个报错的意思就是它说group_key的主键test1重复了

可以看到爆出数据库名test1但是我的数据库名是test 那么这个1是哪里来的

1是来自floor(rand(0)*2)的。

rand

rand()是一个函数 这个函数在0和1之间产生一个随机数

而它后面的*2 ,则是选定获取数据的范围[0,2],其实就是乘以2。

image-20201104111614914

rand(n)这个n是种子值 每个种子产生的序列是不一样的

image-20201104112755685

floor

floor(n)这个函数的功能时返回不大于n的整数,比如

image-20201104113402183

floor(rand(0)*2)这样组合起来的话就会必定返回0或者1其中一个了

image-20201104113723911

concat

concat()是字符串拼接函数

返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。

image-20201104114409776

count和group by

count(*)这个函数我一直给他理解为统计返回数值的函数.

image-20201104114752509

例如这个,表示user表下面会返回五条数值。

group by是分组。需要和count连用

image-20201104140449425group by在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键。如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中,注意是插入!s

具体是怎样一个过程呢

username是admin发现表中没有这个主键,则将admin插入到主键 然后count(*)记为1。

接着取第二条记录。第二条记录发现admin已经作为主键了所以直接讲count(*)加1最终结果为

key count(*)
admin 5
root 2
test 3

再探payload

这回我们回来看这个报错注入的payload

1
select count(*) from user group by concat(database(),floor(rand(0)*2));

报错语句就是 select count(*) from user group by test0或者select count(*) from user group by test1

下图是 用0先初始化种子,然后以这个种子初始化随机数(每次执行这个的结果都是一样的)

image-20201104150027460

group by创建临时表的时候过程是这样的 因为第一个是test0,第二个是test1 ,参考上图

key Count(*)
test0 1
test1 1

最终结果应该是

key count(*)
test0 4
test1 6

那么为什么不是这个结果而是会报错 爆出

1
ERROR 1062 (23000): Duplicate entry 'test1' for key '<group_key>'

因为还有一个最重要的特性,就是group by与rand()使用时,如果临时表中没有该主键,则在插入前rand()会再计算一次。

当group by取第一条from记录时,group by的结果是 test0发现临时表中并没有test0这个主键,这个时候rand(0)*2会再算一次然后floor()后得到test1率先插入临时表的主键不是test0,而是test1,并计数1。

然后取第二条记录group by中的0,1仍然由floor(rand(0)*2)计算获得,第二次得到的数是1,也就是,第二条记录得到的是test1。因为此时临时表里已经有test1了,所以count(*)直接加1就可以了。

第几条 key count(*) Floor(rand(0)*2)
第一条 0
第一条 test1 1 1
第二条 test1 2 1

继续从from表中继续取下一条数据,再次计算floor(rand(0)2),结果为0,与database()拼接为test0

因为临时表的主键中并不存在test0,在插入前,floor(rand(0)*2)又计算一次,拼接后与test1,但是是直接插入,即使临时表中已经有了主键test1也硬要插入,从而导致主键重复报错

1
ERROR 1062 (23000): Duplicate entry 'test1' for key '<group_key>'

优化

网上大部分文章都说必须要有三条记录以上才可以报错。

因为上面共从from的表中取了三条记录,因为floor(rand(0)*2)的值为011011…,但其实第三次计算的1可以不要的,如果某个floor(rand(x)*2)满足0101或1010,那么from的表中两条数据就是可以报错的。

经测试floor(rand(14)*2)的序列为1010……..

也就是说 如果我们用floor(rand(14)*2)去尝试报错注入表里只要有两条数据以上就可以成功触发报错。这里我们尝试一下

image-20201104154009413

总结

总结一下就是floor会报错的原因就是group by在向临时表插入数据时,插入重复主键导致的报错,又因为报错之前concat()里的database()语句已经执行过了所以说,会直接爆出concat函数里执行后的结果