好久好久没写博客了。。最近忽略了学习安全。今天抽时间回头重温了一下floor的报错注入,收获颇多
以前在研究SQL注入时只理解表面却不得精髓,很多原理都是一知半解。所以才有了本篇。。
一般的floor的报错语句为
select count(*) from user group by concat(database(),floor(rand(0)*2));
那么他是怎样报错 如何报错的?
这个报错的意思就是它说group_key
的主键test1
重复了
可以看到爆出数据库名test1
但是我的数据库名是test
那么这个1是哪里来的
1是来自floor(rand(0)*2)
的。
rand()是一个函数 这个函数在0和1之间产生一个随机数
而它后面的*2
,则是选定获取数据的范围[0,2],其实就是乘以2。
rand(n)
这个n是种子值 每个种子产生的序列是不一样的
而floor(n)
这个函数的功能时返回不大于n的整数,比如
floor(rand(0)*2)
这样组合起来的话就会必定返回0或者1其中一个了
concat()
是字符串拼接函数
返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。
count(*)
这个函数我一直给他理解为统计返回数值的函数.
例如这个,表示user表下面会返回五条数值。
而group by
是分组。需要和count
连用
group by在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键。如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中,注意是插入!s
具体是怎样一个过程呢
username是admin发现表中没有这个主键,则将admin插入到主键 然后count(*)记为1。
接着取第二条记录。第二条记录发现admin已经作为主键了所以直接讲count(*)加1最终结果为
key | count(*) |
---|---|
admin | 5 |
root | 2 |
test | 3 |
这回我们回来看这个报错注入的payload
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先初始化种子,然后以这个种子初始化随机数(每次执行这个的结果都是一样的)
group by
创建临时表的时候过程是这样的 因为第一个是test0,第二个是test1 ,参考上图
key | Count(*) |
---|---|
test0 | 1 |
test1 | 1 |
最终结果应该是
key | count(*) |
---|---|
test0 | 4 |
test1 | 6 |
那么为什么不是这个结果而是会报错 爆出
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也硬要插入,从而导致主键重复报错
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)去尝试报错注入表里只要有两条数据以上就可以成功触发报错。这里我们尝试一下
总结一下就是floor会报错的原因就是group by在向临时表插入数据时,插入重复主键导致的报错,又因为报错之前concat()
里的database()
语句已经执行过了所以说,会直接爆出concat函数里执行后的结果