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(*): 计算所有行数,包括NULL
  • count(column_name): 计算指定列非NULL的行数

三、GROUP BY工作机制

1. 正常GROUP BY流程

SELECT count(*) FROM users GROUP BY username;

执行过程:

  1. 创建临时表,GROUP BY字段作为主键
  2. 遍历原表记录:
    • 如果主键不存在:插入主键,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));

详细步骤:

  1. 取第一条记录,计算floor(rand(0)*2)=0 → 'sqli0'

    • 临时表无'sqli0',准备插入
    • 重新计算floor(rand(0)*2)=1 → 实际插入'sqli1',count=1
  2. 取第二条记录,计算floor(rand(0)*2)=1 → 'sqli1'

    • 临时表已有'sqli1',count加1 → count=2
  3. 取第三条记录,计算floor(rand(0)*2)=1 → 'sqli1'

    • 临时表已有'sqli1',count加1 → count=3
  4. 取第四条记录,计算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)

六、防御措施

  1. 使用参数化查询
  2. 对用户输入进行严格过滤
  3. 最小权限原则,限制数据库用户权限
  4. 避免在SQL语句中直接拼接用户输入

七、总结要点

  1. Floor报错注入依赖于GROUP BY与RAND()的特殊交互行为
  2. 核心是利用主键冲突强制MySQL返回错误信息
  3. 需要表中至少有2条记录才能触发
  4. rand(14)rand(0)更适合少量数据的情况
  5. 错误信息会泄露拼接字段的内容(如数据库名)

通过深入理解这些机制,安全人员可以更好地识别和防御此类注入攻击,同时也能在授权测试中有效利用这种技术。

MySQL Floor报错注入原理详解 一、Floor报错注入概述 Floor报错注入是一种基于MySQL数据库GROUP BY子句与随机数函数结合使用的SQL注入技术,通过人为制造主键冲突来获取数据库信息。 典型Payload形式: 错误输出示例: 二、前置知识 1. 测试表结构 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() : 向下取整函数 示例输出: CONCAT()函数 字符串拼接函数,如 concat(database(),floor(rand(0)*2)) 会生成类似'sqli0'或'sqli1'的结果 COUNT()函数 count(*) : 计算所有行数,包括NULL count(column_name) : 计算指定列非NULL的行数 三、GROUP BY工作机制 1. 正常GROUP BY流程 执行过程: 创建临时表,GROUP BY字段作为主键 遍历原表记录: 如果主键不存在:插入主键,count(* )设为1 如果主键存在:count(* )加1 2. 关键特性:GROUP BY与RAND()的特殊行为 当GROUP BY与RAND()函数结合使用时: 如果临时表中不存在该主键,在插入前RAND()会 再次计算一次 这个特性是导致主键重复报错的核心原因 四、报错注入原理分析 1. 执行流程详解 Payload执行过程: 详细步骤: 取第一条记录,计算 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...更适合少量数据: 五、实际应用案例 CVE-2022-24124漏洞Payload示例: 六、防御措施 使用参数化查询 对用户输入进行严格过滤 最小权限原则,限制数据库用户权限 避免在SQL语句中直接拼接用户输入 七、总结要点 Floor报错注入依赖于GROUP BY与RAND()的特殊交互行为 核心是利用主键冲突强制MySQL返回错误信息 需要表中至少有2条记录才能触发 rand(14) 比 rand(0) 更适合少量数据的情况 错误信息会泄露拼接字段的内容(如数据库名) 通过深入理解这些机制,安全人员可以更好地识别和防御此类注入攻击,同时也能在授权测试中有效利用这种技术。