freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

基于floor()函数的主键重复报错注入原理
2022-12-06 20:17:24
所属地 江西省

原理

基于floor()的主键重复报错注入时,需要配合rand()count(*)函数以及group by命令进行配合

floor():用于向下取整 eg. floor(0.472312) = 0 floor(3.9231) = 3

rand():生成一个0~1之间的随机浮点数

count(*):统计某个表下总共有多少条记录

group by x:按照一定的规则 x 进行分组

rand()函数

用于生成 0~1 之间的随机数

未指定rand()参数:如 rand() 此时产生的随机数则不固定(可能是以时间作为种子,由于时间变化所以每次产生的随机数不一致)
https://image.3001.net/images/20220920/1663678701_6329b8ed00189888796a2.png!small

指定rand()参数:如 rand(0) 此时会将该指定参数作为用于产生随机数的种子seed,对于每一个给定的种子,rand() 函数都会产生一列可以复现的数字(每次产生的组合都一致)
https://image.3001.net/images/20220920/1663678703_6329b8ef6ccb8481b7633.png!small

我们payload中要使用的便是 rand(0)

由于 rand(0) 产生的组合是一致的 所以floor(rand(0)*2)后产生组合也是一致的

image3.png

count(*) 和 group by

我们以 dvwa 的 users表 中的 password 为例:

image4.png

select count(*) from users group by password;

数据库系统会首先生成一张空的虚拟表,由于我们是 group by password,所以第一次会读取password列下的第一个值,系统紧接着会在虚拟表中寻找是否存在当前的值(password列下的第一个值),由于当前虚拟表是空的,所以直接插入第一行数据(虚拟表)

虚拟表

KEYcount(*)
5f4dcc3b5aa765d61d8327deb882cf991

接着,第2、3、4次读取的值是password列的第2、3、4个值,由于其值与前面的password的值均没有重复,所以直接依次插入虚拟表的下一个位置

KEYKEY
5f4dcc3b5aa765d61d8327deb882cf991
e99a18c428cb38d5f260853678922e031
8d3533d75ae2c3966d7e0d4fcc69216b1
0d107d09f5bbe40cade3de5c71e9e9b71

紧接着,最后一次也就是最后一个password的值与第一个相同,所以此时虚拟表中第一个KEY值对应的count(*)值+1变为2

KEYKEY
5f4dcc3b5aa765d61d8327deb882cf992
e99a18c428cb38d5f260853678922e031
8d3533d75ae2c3966d7e0d4fcc69216b1
0d107d09f5bbe40cade3de5c71e9e9b71

https://image.3001.net/images/20220920/1663678828_6329b96c9be8f807909ea.png!small

构造报错

前提条件:对应表中必须保证起码有3行数据(原因后面会说)

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

等价于

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

按照上述过程,首先会建立虚拟表

KEYcount(*)


由于此处group by floor(rand(0)*2),根据上述rand(0)的介绍可知,此处得到的结果为011011011...

在执行这条 select语句时,首先会执行前面的第一个 floor(rand(0)*2),此时结果为 0,之后到后面的 group by时会再次执行该语句,所以为 group by 1 ,由于空表中初始没有 KEY = 1(也就是 group by后面的值)的数据,所以此时向虚拟表中插入一条数据,并将 count(*)置为 1

KEYcount(*)
11

再次group by floor(rand(0)*2)时,根据rand(0)的规律特性可知,此时为group by 1,查询虚拟表发现已经有了KEY为1的数据,所以此时不用再插入了(也就不会再次计算group by后面的值了),直接count(*) + 1即可

KEYcount(*)
12

此时还没有出现问题,所以再再次group by floor(rand(0)*2),此时是第四次计算floor(rand(0)*2)所以为group by 0,由于表中并没有KEY=0的数据,所以进行插入,和第一次一样由于要插入数据,所以插入时会重新取一次group by后面的值(再次计算floor(rand(0)*2)),所以实际取到的KEY值为1而不是0!!!!!

而原虚拟表中是有KEY=1的值的,由于主键不能重复,所以此时会抛出主键冗余的错误

Duplicate entry '…' for key 'group_key'也就构成了主键重复报错

所以这也就解释了为什么必须使用rand(0)且原表数据条数不能少于3,因为rand(0)可以保证从第四次开始得出的值开始与前面的相同重复,而由上述过程可知,在配合rand(0)的情况下,从第四次执行group by才会出现主键重复(也就是第三个条数据开始)

Payload构造

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

image6.png

等价于:
select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x;

也可以通过嵌套子查询实现

select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x) as a

等同于

select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a

image7.png

其中as a或a是给子查询起的别名,每个子查询里面的sql都必须有自己的别名,否则会报错

ERROR 1248 (42000): Every derived table must have its own alias

https://image.3001.net/images/20220920/1663678953_6329b9e934af79d280e63.png!small

# 渗透测试 # 网络安全 # web安全 # CTF
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录