【转】Mysql基于报错注入之floor(rand(0)*2)报错原理探究

原文写的很清楚很详细,这里给出原文链接:
https://www.freebuf.com/column/235496.html

一、简述
floor报错注入是利用 select count(*),(floor(rand(0)*2)) x from users group by x这个相对固定的语句格式,导致的数据库报错。实际利用中通过 concat 函数,连接注入语句与 floor(rand(0)*2)函数,就实现了注入结果与报错信息回显的注入方式。具体利用本文不做阐述,本文重点探究该语句报错的原因,要理解该语句的报错原因,首先大家需要理解如下的关键函数的作用: count() 、group by 、floor()、rand()。

二、关键函数说明
1.理解rand函数
rand() 是一个随机函数,通过一个固定的随机数的种子0之后,可以形成固定的伪随机序列。结果如下图所示:

可见,直接使用rand函数每次产生的数都不同,但是当提供了一个固定的随机数的种子0之后:

这样每次产生的值都是一样的。也可以称之为伪随机(产生的数据都是可预知的)。
查看多个数据看一下。(users是一个有6行数据的表)
在这里插入图片描述

这样第一次产生的随机数和第二次完全一样,也就是可以预测的。
那么floor报错注入利用的时候rand(0)*2为什么要乘以 2 呢?这就要配合floor 函数来说了。

2.理解floor(rand(0)*2)函数
floor() 函数的作用就是返回小于等于括号内该值的最大整数,也就是取整。

floor(rand(0)*2)就是对rand(0)产生的随机序列诚意2后的结果,再进行取整。得到伪随机序列为如下图所示:
在这里插入图片描述

因为使用了固定的随机数种子0,他每次产生的随机数列都是相同的0 1 1 0 1 1的顺序。

3.group by 函数
group by 主要用来对数据进行分组(相同的分为一组)。

例如建立如下表进行实验

图片.png

通过如下语句进行查询。(这里在a和x之前缺省了as ,作用为用a和x代替原有的字段显示),显示的结果如下图所示:

在这里插入图片描述
但通过group by进行分组排序是,结果会进行分组,相同名字为合并。如下图所示

注意:最后x这列中显示的每一类只有一次,前面的a的是第一次出现的id值

在这里插入图片描述

4.理解count()函数
count(
)统计结果的记录数。

这里与group by结合使用看一下:

在这里插入图片描述
这里就是对a中的重复性的数据进行了整合,然后计数,后面的x就是每一类的数量。也就是lisi有2个,wangwu有1个,zhangsan有3个。注意显示同样是按照ascii排序。

三、报错原因分析
大家已经了解,当执行如下语句时,就会产生一个报错。如下图所示

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

在这里插入图片描述
根据前面函数的理解,这句话本义就是统计后面产生随机数的种类并计算每种数量。原本执行结果一共6行数据,产生的随机序列应该为0 1 1 0 1 1 ,按照语句的含义,统计如果应该是:0是2个,1是4个,但是此处却产生了报错?这是为什么呢?下面来分析一下。

这里最关键的及时要理解group by函数的工作过程。group by key 在执行时循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则更新临时表中的数据(更新数据时,不再计算rand值);如果该key不存在于临时表中,则在临时表中插入key所在行的数据。(插入数据时,会再计算rand值)

如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)*2)结果可能为1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值。

具体报错原因可以通过下列过程展示:

mysql执行结果,会产生 011011 这个序列,group by时,会建立空虚拟表如下图,然后从sql语句执行结果序列(011011)读取数据并插入虚表:

在这里插入图片描述

(1)虚表写入第一条记录,执行floor(rand(0)*2),发现结果为0(此时为第一次计算)

在这里插入图片描述

(2)查询虚拟表,发现0的键值不存在,则插入新的键值的时候floor(rand(0)*2)会被再计算一次,结果为1(此时为第二次计算),插入虚表,第一条记录插入完毕,结果为1。如下图:

在这里插入图片描述

(3)虚表写入第二条记录,再次计算floor(rand(0)*2),发现结果为1(此时为第三次计算),此时结算结果为1,所以floor(rand(0)2)不会被计算,直接count()加1,第二条记录写入完毕。(5)查询虚表,发现1的键值存在,所以floor(rand(0)2)不会被计算第二次,直接count()加1,第二条记录查询完毕,结果如下:

在这里插入图片描述

(4)虚表写入第三条记录,再次计算floor(rand(0)*2),发现结果为0(此时为第4次计算),计算结果为0,此时虚表中没有0的数据记录,则执行插入该数据,插入时会再次计算floor(rand(0)*2)(此时为第5次计算),计算结果为1。然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以就产生了主键冲突的错误,也就是:Duplicate entry 的报错。

在这里插入图片描述
总结:
通过上述分析,在虚表中写入第三条记录是时,产生了报错。此时floor(rand(0)*2)一共被计算了5次,这也解释了为什么数据表中需要最少3条数据才会报错的原因。
另外,要注意加入随机数种子的问题,如果没加入随机数种子或者加入其他的数,那么floor(rand()*2)产生的序列是不可测的,这样可能会出现正常插入无法报错的情况。最重要的是前面几条记录查询后不能让虚表存在0,1键值,如果存在了,那无论多少条记录,也都没办法报错,因为floor(rand()*2)不会再被计算做为虚表的键值,这也就是为什么不加随机数种子有时候会报错,有时候不会报错的原因。

比如下面用1作为随机数种子,就不会产生报错:

在这里插入图片描述
在这里插入图片描述

<think>我们正在讨论SQL报错注入floor函数的工作原理报错注入是SQL注入的一种,它利用数据库的某些函数或特性人为制造错误,从而在错误信息中返回敏感数据。 在MySQL数据库中,`floor()`函数是一个数学函数,用于向下取整。例如,`floor(1.7)`返回1。在报错注入中,`floor()`函数常与`rand()`、`count()`和`group by`结合使用,通过制造主键重复错误来泄漏信息。 常见的报错注入语句结构如下: ```sql select count(*), concat((select database()), floor(rand(0)*2)) as x from information_schema.tables group by x; ``` 这个查询会引发重复键错误,并返回数据库名称。 ### 工作原理分析 1. **`rand(0)*2`**:`rand(0)`生成一个基于种子0的伪随机序列。乘以2后,结果在02之间(不包括2)。例如,序列可能是0.7, 1.5, 0.7, 1.5...(实际序列由MySQL版本决定,但种子固定则序列固定)。 2. **`floor(rand(0)*2)`**:对上述结果向下取整,得到0或1。例如,0.7取整为0,1.5取整为1。因此,序列变为0,1,0,1...(注意:实际序列可能因MySQL版本而异,但种子固定则序列固定)。 3. **`concat((select database()), floor(rand(0)*2))`**:将查询结果(如数据库名)与0或1拼接,形成字符串。例如,若数据库名为`test`,则可能得到`test0`或`test1`。 4. **`group by x`**:按上述拼接结果`x`分组。分组时,MySQL会创建一个临时表,其中`x`作为主键。当插入临时表时,如果遇到重复键,就会报错。 关键点在于`floor(rand(0)*2)`在分组过程中被多次计算。在分组时,对于每一行,`x`的值会被计算: - 第一次计算`x`用于确定分组键。 - 当向临时表插入分组记录时,如果该分组键不存在,则插入新行,此时会再次计算`x`的值作为主键。 由于`rand(0)`的序列是固定的,在分组过程中多次计算会导致同一个分组键在两次计算中可能得到不同的值,从而造成主键冲突。 例如,假设有两条记录: - 第一条记录:计算`x`为0(第一次计算),然后插入临时表时再次计算`x`为1(第二次计算),此时插入主键为1的行。 - 第二条记录:计算`x`为1(第一次计算),然后插入临时表时尝试插入主键为1的行,但发现已存在,于是报错报错信息会包含导致冲突的值,即`concat`的结果,从而泄漏敏感信息。 ### 示例报错信息 ``` Duplicate entry 'test1' for key 'group_key' ``` 这里`test1`就是拼接后的结果,其中`test`是数据库名,`1`是`floor(rand(0)*2)`计算出的值。 ### 注意事项 - 这种报错注入MySQL 5.x版本中有效,但在MySQL 8.0中可能失效,因为MySQL 8.0修改了`rand()`函数的实现和分组操作的行为。 - 实际利用时,需要调整`concat`中的子查询来获取不同数据(如表名、列名、数据值)。 §§相关问题§§ 1. 除了`floor()`函数,还有哪些函数可以用于SQL报错注入2. 如何防御SQL报错注入攻击? 3. 在MySQL 8.0中,报错注入还有效吗?如果无效,有哪些替代方法? 4. 在报错注入中,如何利用`updatexml()`或`extractvalue()`函数?</think>### SQL报错注入floor函数的工作原理 在SQL报错注入中,`floor()`函数常与`rand()`、`count()`和`group by`结合使用,通过人为制造主键冲突错误来泄漏数据库信息。其核心原理是利用MySQL处理分组查询时的计算特性。 #### 关键步骤分析 1. **`rand(0)*2`生成伪随机序列** 固定种子`0`使`rand(0)`产生可预测序列(如0.7, 1.5, 0.7, 1.5...),乘以2后范围在[0,2)之间。 2. **`floor()`取整制造0/1值** `floor(rand(0)*2)`将序列换为固定模式(如0→1→0→1...)。例如: - `floor(0.7*2)=floor(1.4)=1` - `floor(1.5*2)=floor(3.0)=3?` 错误!实际范围是[0,2),所以是`floor(1.5*2)=floor(3.0)=3`超出预期? **修正**:`rand(0)*2`的范围是[0,2),因此`floor()`结果只能是0或1。实际序列需测试确定,但种子固定则序列固定。 3. **`concat()`拼接敏感数据** 将查询结果(如`(SELECT @@version)`)与`floor(rand(0)*2)`拼接,形成如`"MySQL5.71"`的字符串。 4. **`group by`触发主键冲突** MySQL执行分组时: - 首次计算分组列值(如得`"MySQL5.71"`) - 插入临时表时**重新计算**该值(可能得`"MySQL5.70"`) - 若两次计算结果不同,则尝试插入重复主键报错: ```sql SELECT count(*), concat((SELECT @@version), floor(rand(0)*2)) as x FROM information_schema.tables GROUP BY x; -- 报错:Duplicate entry 'MySQL5.71' for key 'group_key' ``` #### 为什么能泄漏数据? - **错误信息包含拼接值**:报错信息直接返回`concat()`的结果(如`'MySQL5.71'`),其中包含子查询数据。 - **可控触发**:通过修改子查询可获取任意数据: ```sql concat((SELECT password FROM users LIMIT 1), floor(rand(0)*2)) ``` #### 影响因素 1. **MySQL版本**:5.x有效,8.0+因优化可能失效[^1] 2. **表数据量**:需足够行触发多次计算(通常≥3行) 3. **`rand()`种子**:必须固定种子(如`rand(0)`)确保序列可预测 ### 防御建议 1. 参数化查询避免拼接SQL 2. 过滤特殊字符(如`floor`、`rand`、`group by`) 3. 关闭错误回显(生产环境禁用详细报错
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值