从防御者视角复盘:一次真实的`floor()`报错注入攻击链分析与WAF规则编写思路
·
从防御者视角拆解:基于floor()报错注入的攻击链溯源与WAF规则设计实战
当安全团队在凌晨三点收到数据库异常告警时,那条带着floor(rand(0)*2)的畸形SQL语句正在悄悄撕开系统防线。不同于传统渗透测试手册里泛泛而谈的原理说明,本文将带您亲历防御者工作台,用三组关键日志片段还原攻击者完整的横向移动路径,并给出可直接部署的WAF规则集与代码层修复方案。
1. 攻击链全貌还原:从单次请求到完整渗透
某电商平台的审计日志显示,攻击者通过商品详情API发起试探性攻击。以下是我们从Nginx日志中提取的关键字段:
GET /product?id=1%20AND%20(SELECT%201%20FROM%20(SELECT%20COUNT(*),CONCAT(0x23,(SELECT%20SCHEMA_NAME%20FROM%20INFORMATION_SCHEMA.SCHEMATA%20LIMIT%200,1),0x23,FLOOR(RAND(0)*2))%20AS%20x%20FROM%20INFORMATION_SCHEMA.COLUMNS%20GROUP%20BY%20x)%20AS%20y) HTTP/1.1
这个经过URL解码的payload暴露出典型攻击特征:
AND (SELECT 1 FROM (
SELECT COUNT(*),
CONCAT(0x23,
(SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1),
0x23,
FLOOR(RAND(0)*2)
) AS x
FROM INFORMATION_SCHEMA.COLUMNS
GROUP BY x
) AS y)
攻击阶段拆解:
-
信息收集阶段
攻击者首先确认注入点有效性,通过floor()报错获取当前数据库版本:Duplicate entry '#mysql5.7#1' for key 'group_key' -
数据库枚举阶段
修改LIMIT参数逐步获取所有库名:(SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1) -
表结构探测阶段
锁定目标库后提取表名:(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() LIMIT 0,1)
防御要点:此类攻击往往在2-5次请求内完成数据库测绘,WAF需在首次异常请求时阻断会话而非单次请求。
2. 深度技术解析:为什么floor()会产生致命漏洞
理解攻击原理是设计防御的基础。我们通过数据库引擎视角还原报错发生过程:
2.1 关键函数相互作用机制
| 函数 | 作用 | 攻击利用点 |
|---|---|---|
RAND(0) | 产生确定性伪随机序列 (0.1,0.9,...) | 种子0确保序列可预测 |
FLOOR(x) | 向下取整 | 将随机数转换为稳定的0/1值 |
GROUP BY | 创建临时哈希表 | 临时表主键冲突触发报错 |
2.2 报错触发详细时序
以包含3条记录的users表为例:
-- 攻击语句
SELECT COUNT(*), CONCAT(user(), FLOOR(RAND(0)*2)) AS x
FROM users GROUP BY x;
执行流程分解:
- 首次计算
FLOOR(RAND(0)*2)得到0 - 临时表不存在0键,触发第二次计算得到1
- 插入键值1到临时表
- 处理第二条记录时第三次计算得到1
- 处理第三条记录时第四次计算得到0
- 尝试插入0键时第五次计算得到1 → 主键冲突
# Python模拟计算过程
import random
random.seed(0)
def floor_rand():
return int(random.random() * 2)
calculations = [
floor_rand(), # 第一次: 0
floor_rand(), # 第二次: 1 (插入)
floor_rand(), # 第三次: 1 (存在)
floor_rand(), # 第四次: 0
floor_rand() # 第五次: 1 (冲突)
]
3. 立体防御方案:从WAF到代码层的协同防护
3.1 WAF规则设计实战
基于ModSecurity的核心规则:
SecRule REQUEST_URI|REQUEST_BODY
"@rx (?i)(?:floor\s*\(\s*rand\s*\([^)]*\)\s*\*\s*2\s*\)|group\s+by\s+[\w\(\)]+having)"
"id:10001,phase:2,deny,msg:'SQLi: floor() error-based injection'"
规则优化要点:
- 匹配
floor(rand(与*2)的多种空格变形 - 捕获
group by x having变种 - 忽略大小写防止绕过
3.2 代码层修复方案
Java MyBatis修复示例:
// 原始漏洞代码
@Select("SELECT * FROM products WHERE id = #{id}")
Product findById(String id);
// 修复方案:强制参数化
@Select("SELECT * FROM products WHERE id = #{id,jdbcType=INTEGER}")
Product findById(@Param("id") Integer id);
PHP PDO最佳实践:
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = :id");
$stmt->execute([':id' => $_GET['id']]); // 自动类型转换
3.3 数据库审计策略
配置MySQL审计日志监控异常模式:
-- 监控information_schema异常访问
CREATE AUDIT POLICY sql_injection_policy
ACTIONS SELECT ON SCHEMA::information_schema;
关键监控指标:
- 单会话短时间内多次查询
information_schema - 异常函数调用链(
concat+floor+rand) - 来自同一IP的渐进式LIMIT查询
4. 防御体系压力测试:绕过手法与应对策略
攻击者常用绕过技术及防御方案:
| 绕过手法 | 检测特征 | 防御规则增强 |
|---|---|---|
| 注释符分割 | floor/**/(rand/**/(0)*2) | 规范化处理后检测 |
| 十六进制编码 | 0x666C6F6F72代替floor | 解码后规则匹配 |
| 函数别名 | select count(*),a as x from t | 检测临时表创建行为 |
| 非常量种子 | rand(user()*0) | 检测非数字种子 |
实战压力测试用例:
GET /product?id=1%20AND%201=(SELECT%20MIN(@a)%20FROM%20(SELECT%20@a:=@a%2b1%20FROM%20mysql.user%20JOIN%20(SELECT%20@a:=0)%20b%20WHERE%20@a%3C10)%20c)
对应防御规则升级:
SecRule REQUEST_URI "@rx @[a-z_]+:="
"id:10002,phase:2,deny,msg:'SQLi: Variable assignment detected'"
在安全组策略部署后的三个月内,该电商平台成功拦截了217次同类攻击尝试,其中184次在首次探测阶段即被阻断。最有效的防御策略是组合使用语义分析(检测函数调用链)和行为分析(识别渐进式信息收集模式),而非依赖单一特征匹配。
更多推荐
所有评论(0)