【web安全】——floor报错注入

作者名:Demo不是emo
主页面链接: 主页传送门
创作初心: 一切为了她
座右铭: 不要让时代的悲哀成为你的悲哀
专研方向: web安全,后渗透技术
每日emo: 树是生活,埋的是我,看花就好,别看我的落魄
今天给大家讲解的是SQL注入中的floor报错注入,这个注入平时我没怎么用,上次看别人面试时遇到了,就再来深究一下,研究其中的原理,利用方式等等

一、漏洞的简介

floor()报错注入的原因group by在向临时表插入数据时,由于 rand()多次计算导致插入临时表时主键重复,从而报错,又因为报错前 concat()中的SQL语句或函数被执行,所以该语句报错且被抛出的主键是SQL语句或数执行后的结果。

乍一看是不是听不懂,没关系,我们来详细讲解一下,首先介绍一下原理中涉及到的函数,如下

二、涉及的函数

floor()函数:对传入的值进行向下取整操作,并返回结果,如floor(1.999),则返回1.

rand()函数,返回随机数

rand(x)函数,x在这里代表参数,当rand()函数有了参数后,生成的就是伪随机数,什么意思呢?比如你使用rand(0)产生的第一个随机数产生的随机数相同,也就是当rand(x)这个参数x已知的时候我们就能知道,如下

同样的,刚才我们说的是使用rand(0)产生的第一个随机数都相同,产生的第n个随机数其实都相同,但是这n个随机数之间可能不相同,这样能懂吗?如下

也就是我user表里有三个数据,所以这个rand(0)执行了三次,可以看到rand(0)在不同的地方,执行的前三次的数据都是相同的

简单来说就是rand()函数有参数,即rand(x)时,rand(x)产生的就是伪随机的一个数组,数组中元素的值由x决定,就是你使用rand(x)产生的第n次数据,与我使用rand(x)产生的第n次数据是相同的

count(*)函数,返回值的条目,与count()的区别在于其不排除NULL,count()如果统计到NULL,返回的结果即为NULL,返回列数。如下,就是统计user表的内容条数

group by语句 分组的意思,如下

这里的意思就是以username字段来分组,会统计出users表中存在username字段的数据条数,但是当有多条数据的username相同时,我们统计出来的数据就不同,比如我们把上面其中四条数据的username都改为admin,如下

此时我们再以username字段来分组,效果如下

可以看到刚才统计了13条数据,现在只统计到了9条,说明以username来分组代表着统计username字段不同的数据,而经过我们刚才的修改,将5条数据的username都改为admin,所以它只识别到了其中一条,而且是第一条数据,所以此时以username来分组来分出来9条数据,这个意思可以理解吧

group by语句和count(*)函数结合 这样就可以统计指定字段的条数,如下

再这样就可以统计所有的username字段以及数据条数

floor报错注入的究极原因就是出在这两个函数混用时的运行原理中,如下

当count(*)和group by一起用,在执行的时候会创建一个类似于下面这样的虚拟表

其中的key就是主键,存放的也就是我们指定的字段数据,count(*)就作为值

当该命令运行后,会 遍历指定表中的数据,将数据中的指定字段带去虚拟表中查询,若虚拟表中存在该数据,则在对应的count(*)位置+1,若虚拟表中不存在,则将新的字段数据添加到主键位置,初始count(*)为1.例如有如下表

此时虚拟表为空,先遍历第一条数据,将指定的username字段数据带入虚拟表中查询,也就是Dumb,因为此时虚拟表为空自然是查不到的,所以就在虚拟表中添加在这条数据,此时虚拟表如下

接着遍历第二条数据,以此类推,就是没有数据就添加数据,有数据就添加count(*)的值,以此达到统计指定字段数据的目的

三、原理的剖析

上面我们已经知道了涉及的函数,这里就给大家讲讲floor报错注入的语句以及为什么会报错,首先来看看下面这个语句

select count(*),floor(rand(0)*2) from users group by floor(rand(0)*2)

执行结果如下

可以看到出现报错信息,提示“1”这个主键重复,这是为什么呢?在讲解之前我们首先要了解一个特性,就是rand()函数的一个特性

这个特性就是 rand()函数的执行速度要比 group by查询并插入key值的速度更快

首先我们讲了当group by和count(*)一起用时,会生成一个虚拟表,记录key和count(*),在上面的演示中我们用的是group by username,也就是以username字段作为key来统计数据,但是在这个报错的语句中,我们使用的是group by floor(rand(0)*2),而floor(rand(0)*2)的执行结果我们上面也已经知道了,就是下面这一串

按照group by语句的流程

1、首先将floor(rand(0)*2)的第一次执行结果,也就是0带入虚拟表的key中查询是否存在

2、此时不存在,所以会将此时的floor(rand(0)*2)的结果插入虚拟表中

3、但是不要忘了rand()函数的特性, rand()函数执行是比group by语句查询并插入key值更快的,也就是floor(rand(0)*2)执行了一次后,就被带去查询,此时floor(rand(0)*2)仍在执行,等查询完确认虚拟表中没有0这个key后,就将floor(rand(0)*2)此时的结果插入虚拟表

4、但此时floor(rand(0)*2)已经执行完第二遍了,结果为1,就导致了 带去查询的数据为0,但插入的数据却为1,对应的count(*)也为1,此时虚拟表如下
5、接着 floor(rand(0)*2)第三次执行,结果为1,group by也遍历到了第三个结果,也就把1带入虚拟表中的key值去查,发现存在“1”这个key值,所以直接在该key值对应的count(*)加1,也就是计数,注意这里并不需要插入操作,所以floor(rand(0)*2)的第四次执行还没有完成

6、接着 floor(rand(0)*2)第四次执行完成,结果为0,group by语句带0进入虚拟表key中查询,发现没有这个key值,所以将此时的floor(rand(0)*2)结果插入虚拟表,但是,因为rand()函数的特性,插入还没完成之前,floor(rand(0)*2)第五次执行结果已经完成,结果为1,所以导致带入查询的数据为0,插入的数据却为1,此时虚拟表如下

这样就导致出现了上面报错信息中的问题——主键重复

四、漏洞的利用

上面的讲解我们已经明白了为什么会提示“1”这个主键重复,那我们应该怎样利用呢?

既然是报错,那我们当然就要利用它的报错信息来入手,提示“1”这个主键重复,“1”这个主键是怎么来的?是不是我们的floor(rand(0)*2)执行出来的?那我们可不可以加入我们的恶意语句,让他在报错时进行执行,首先看看下面的语句

select count(*),concat(user(),floor(rand(0)*2)) from users group by concat(user(),floor(rand(0)*2))

我们在floor(rand(0)*2)旁边用concat()函数连接我们的恶意语句试试看,执行结果如下

成功执行我们的恶意语句并回显,说明我们的想法没问题,在floor(rand(0)*2)旁边连接上我们的恶意语句即可,这样就达到了floor报错注入的目的,将上面的恶意语句user()换成database()再试试,如下

同样达到效果

五、利用的拓展

上面我们已经弄懂了floor报错注入,但是每次修改语句都都要前后都修改,比较麻烦,有没有什么更好的方式,那自然是有的,就是下面要介绍的——别名

别名的格式如下

(语句)x

这里的x随便定义,用啥都行

来看看这个语句

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

既然后面的语句和中间的语句相同,所以上面语句中将中间语句加别名,后面直接用x代替,这样每次就只需要修改一个地方的语句就可以了

到这里floor报错注入漏洞就全部给大家介绍完了,有什么问题随时私信和评论都可以哦,希望能帮到你们
  • 183
    点赞
  • 252
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Daylight庆尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值