1. 项目概述当SQL注入遇上WAF在Web安全领域SQL注入SQL Injection和Web应用防火墙WAF就像一对永恒的“矛与盾”。作为一名长期在一线进行渗透测试和安全评估的从业者我几乎每天都会遇到这对组合。很多刚入门的朋友一看到WAF的拦截页面就头疼觉得无计可施。但实际情况是WAF并非铜墙铁壁它更像是一个基于规则和模式的过滤器。理解它的工作原理就能找到绕过它的方法。今天我就结合自己多年的实战经验系统性地拆解一下那些行之有效的SQL注入绕过WAF的技巧。这不仅仅是几个Payload的堆砌更是一种对抗思路的建立让你在面对防护时能像解谜一样找到那条隐藏的路径。简单来说这个“项目”的核心就是在目标网站部署了WAF如云WAF、硬件WAF或软件WAF模块的情况下如何通过精心构造的SQL注入语句成功执行恶意查询并获取数据。它适合所有对Web安全感兴趣的朋友无论是正在学习渗透测试的学生还是需要评估自身应用安全性的开发者。通过本文你将不仅学到“怎么做”更能理解“为什么能这么做”从而建立起主动防御和有效攻击的双重视角。2. 核心对抗思路理解WAF的运作与盲点在开始具体技巧之前我们必须先搞清楚对手。WAF绕过的本质是“规则对抗”。绝大多数WAF尤其是基于正则表达式或关键字匹配的WAF的工作流程可以简化为截获HTTP请求 - 解析请求参数 - 与内置的恶意规则库进行匹配 - 若匹配则拦截/告警否则放行。2.1 WAF的常见检测维度WAF的检测并非单一维度通常是一个组合拳关键字/模式匹配这是最基础的一层。WAF维护了一个庞大的“黑名单”包含union select、information_schema、sleep(、benchmark(、extractvalue(、updatexml(等敏感SQL关键字和函数。一旦请求中出现这些字符串就可能被拦截。语法/语义分析高级WAF会尝试解析SQL语句的语法结构。例如它可能检测不正常的括号嵌套、异常的运算符使用如1‘ and ‘1’’1中的逻辑或语句结构的异常。流量/行为异常检测通过统计模型检测异常请求频率、异常参数长度、非常规的HTTP方法等。例如短时间内大量提交包含sleep()函数的请求即使每次Payload都不同也可能触发行为规则。编码/混淆识别WAF会尝试对URL编码、Unicode编码、十六进制编码等进行解码然后再次进行规则匹配。2.2 绕过的基本哲学制造“差异”我们的目标就是制造WAF解析结果与数据库SQL引擎解析结果之间的“差异”。让WAF“看”到一个无害或无法识别的请求而数据库“执行”的却是我们精心构造的恶意语句。这种差异主要通过以下几种方式实现混淆让恶意代码“看起来”不像恶意代码。例如将关键字拆散、插入注释、使用非常规的空白字符。等效替换用功能相同但写法不同的语法或函数来替代被拦截的关键字。利用解析差异利用WAF解析器与数据库解析器在处理特定字符、编码、注释时的不同行为。资源耗尽/逻辑绕过通过构造极其复杂的Payload试图耗尽WAF的解析资源如正则回溯或者利用WAF规则逻辑的漏洞如只检测第一个参数。理解了这些我们再看具体技巧就不会觉得是零散的“奇技淫巧”而是有章可循的战术组合。3. 字符级绕过从最细微处着手这是最基础也往往最有效的第一层尝试。核心思想是干扰WAF的字符串匹配。3.1 注释符的妙用SQL注释--,#,/*...*/在绕过中扮演着“分隔符”和“扰乱器”的角色。拆分关键字这是绕过union select拦截的经典方法。WAF的规则可能是/union[[:space:]]select/i。我们可以用注释将其拆开U/**/NION/**/SEL/**/ECT 1,2,3 --对于数据库/**/被视为一个可忽略的注释语句等价于UNION SELECT 1,2,3。但对于简单的正则匹配union和select没有被空格直接连接可能逃脱检测。内联注释MySQL特有的/*!...*/注释其中的内容会被MySQL执行但其他数据库或解析器可能忽略。这可以用来包裹整个恶意语句或者指定数据库版本。/*!50000UNION*/ /*!50000SELECT*/ 1,2,3 --这表示在MySQL 5.00.00及以上版本执行内部的语句。实操心得注释符的选择很重要。--后面有个空格在URL中需要编码为--或--%20。#在URL中表示锚点需要编码为%23。/**/最通用但要注意不要破坏SQL语法。3.2 空白符的多样化WAF规则里的[[:space:]]通常只匹配常规空格、制表符、换行符。我们可以使用非常规空白符水平制表符%09(URL编码)换行符%0a,%0d,%0d%0a垂直制表符%0b换页符%0c例如union%0aselect 1,2,3 unio%0bn%0cselect 1,2,3这些字符在SQL中通常也被视为空白能正常分割关键字但可能绕过简单的空白匹配规则。3.3 大小写与双写绕过大小写混合针对不区分大小写的匹配规则/union select/i这招无效。但有些老旧或配置不当的WAF可能使用区分大小写的匹配这时UnIoN SeLeCt可能有效。双写关键字这是一种针对简单字符串替换型过滤的绕过。如果防护代码是$input str_replace(‘union’, ‘’, $input)那么ununionion经过替换后中间的union被移除两边的字符又组成了一个新的union。ununionion selselectect 1,2,3 --4. 编码与混淆让Payload“面目全非”当字符级绕过无效时我们需要对Payload进行更深层次的“化妆”。4.1 URL编码与多重编码全编码将整个或部分Payload进行URL编码。union select - %75%6e%69%6f%6e%20%73%65%6c%65%63%74如果WAF没有解码或只解码一次就可能绕过。部分编码只编码关键字符。例如编码空格为%20单引号为%27等号为%3d。%27%20and%201%3d1%20--%20多重编码对已经编码的字符串再次编码。%本身编码为%25。select - %73%65%6c%65%63%74 - %25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34这主要用于对抗一些简单的解码逻辑。4.2 十六进制与Unicode编码十六进制编码在MySQL中字符串可以用0x开头表示。union select 1,2,0x70617373776f7264 from users -- (0x70617373776f7264 是 ‘password’ 的十六进制)这可以绕过对特定敏感单词如password的检测。Unicode编码利用数据库对特定Unicode字符的兼容性。例如在某些环境下%u0055代表 ‘U’。但这种方式通用性不强严重依赖于数据库和连接驱动的配置。4.3 等价函数与语法替换如果某个函数如sleep()被禁我们可以寻找功能相同的替代品。延时函数替换sleep(5)-benchmark(10000000, md5(‘test’))(执行大量运算达到延时效果)PostgreSQL:pg_sleep(5)-generate_series(1,10000000)字符串连接函数MySQL:concat()-concat_ws(),group_concat()‘a’’b’(在某些数据库如MSSQL中可用)信息获取函数获取当前用户user()-current_user(),system_user(),session_user()获取数据库database()-schema()4.4 特殊符号与语法技巧反引号、引号在MySQL中反引号用于包裹数据库名、表名、字段名。有时可以用于分隔关键字。unionselect 1,2,3 -- (可能语法错误但有时能干扰解析)科学计数法数字1可以写成1e00.1可以写成.1。在数字型注入中可以用于绕过对纯数字的简单过滤。id1 and 1e01e0括号与表达式大量使用无意义的括号或数学表达式。id1 and (1)(1) id1 and (select(1))(1) id1 and (select1)!25. 高级绕过技术利用解析器差异与逻辑缺陷当常规混淆无效时我们需要更深入地研究WAF与数据库的解析差异。5.1 HTTP参数污染HTTP参数污染HPP是指向同一个参数名提交多个值。例如?id1id2。不同的Web服务器和WAF处理方式不同Apache/PHP通常取最后一个值id2。IIS/ASP.NET通常取所有值用逗号连接id1,2。J2EE/Tomcat通常取第一个值id1。绕过思路如果WAF只检查第一个id参数而应用程序如PHPApache使用最后一个id参数我们就可以构造?id1id2 union select 1,2,3 --WAF看到id1安全放行。应用程序使用id2 union select 1,2,3 --成功注入。5.2 分块传输编码分块传输编码是HTTP/1.1的一种特性允许将请求体分块发送。有些WAF可能无法正确解析分块格式或者解析逻辑与后端服务器不同。通过工具如Burp Suite的 “Chunked-encoding converter” 扩展可以将Payload编码为分块格式可能绕过一些WAF的请求体检测。但这需要对HTTP协议有较深理解且成功率因WAF而异。5.3 畸形请求与协议级绕过这属于更高级的技巧包括请求方法混淆将POST请求改为GET或者使用非标准的HTTP方法。Content-Type混淆例如将application/x-www-form-urlencoded改为multipart/form-data或text/plainWAF的解析器可能无法正确处理。参数位置变换将注入参数从URL查询字符串移到Cookie、HTTP头如X-Forwarded-For、User-Agent或JSON/XML请求体中。WAF可能默认只检测URL和POST body忽略其他部分。5.4 利用数据库特性与注释陷阱MySQL/*!*/注释的版本控制/*!50000select*/只有在MySQL版本5.00.00时才会被执行。可以用于构造在特定环境下才触发的Payload。嵌套注释与解析错误过于复杂的注释嵌套可能导致解析器崩溃或行为异常。例如/*!/*!select*/ */。但这需要大量测试。利用数据库对字符集的宽松解析某些数据库在特定字符集下可能会将一些特殊字符视为空白或忽略。6. 自动化工具辅助与手工结合虽然手工构造Payload能锻炼思维但在实战中我们常借助自动化工具提高效率。最著名的就是sqlmap。sqlmap内置了大量绕过脚本tamper它们就是上述各种技巧的自动化实现。6.1 常用sqlmap tamper脚本解析使用sqlmap -u “url” –tamper”脚本名”来调用。了解脚本原理比单纯使用更重要space2comment用/**/替换空格。对应我们手工的注释拆分。space2plus/space2randomblank用或随机空白符替换空格。between用not between 0 and #替换。用不等价但逻辑相同的语法。charencode/chardoubleencodeURL编码/双重URL编码。randomcase随机大小写。equaltolike用like替换。greatest用greatest()函数绕过比较。apostrophemask用%EF%BC%87全角单引号替换单引号’。versionedmorekeywords在关键字前添加MySQL版本注释如/*!50000UNION*/。实操流程先进行基本探测sqlmap -u “http://target.com/page?id1” –batch如果被WAF拦截尝试使用通用组合–tamper”space2comment,randomcase”根据拦截特征如拦截了union但没拦截select选择针对性脚本。可以查看sqlmap的tamper/目录下的脚本源码学习。使用–random-agent随机化User-Agent使用–delay设置请求延迟避免触发频率限制。注意事项不要过度依赖自动化。sqlmap的流量特征非常明显高强度的WAF很容易识别并封禁IP。应该先用手工方式探测出WAF的薄弱点例如发现它不检测Cookie中的参数再指导sqlmap针对性地进行测试例如用–cookie”id1*”指定注入点。7. 实战场景与综合Payload构造理论需要结合实践。我们模拟一个常见场景一个搜索功能参数为keyword疑似存在字符型注入但部署了云WAF。初始测试keywordtest’ and ‘1’’1返回正常。keywordtest’ and ‘1’’2返回异常。确认存在注入。但当我们尝试keywordtest’ union select 1,2,3 –立刻被WAF拦截。逐步绕过尝试尝试注释拆分和空白符keywordtest’%0aun/**/ion%0dsel/**/ect%201,2,3%23%0a换行/**/拆分%0d回车%23是# 如果依然被拦进入下一步。尝试等价函数和编码 假设我们需要获取数据库版本。直接version()可能被拦。keywordtest’%0aun/**/ion%0dsel/**/ect%201,version,3%23使用version全局变量替代version()函数 或者对关键字进行十六进制编码的一部分需要数据库支持keywordtest’ and (select mid(version,1,1))0x35%23判断版本第一位是否是 ‘5’0x35是 ‘5’ 的十六进制尝试参数污染 如果应用是PHP可能取最后一个参数。keywordtest’keyworda’ union select 1,2,3%23将恶意Payload放在第二个keyword参数。改变参数位置 查看请求发现还有一个X-Forwarded-For头。尝试将注入点移到头部需要应用代码存在漏洞GET /search?keywordtest HTTP/1.1 Host: target.com X-Forwarded-For: 127.0.0.1’ union select 1,2,3%23综合构造最终Payload 经过测试发现WAF对union select检测严格但对||Oracle/PostgreSQL连接符或MSSQL连接符检测不严且应用是MySQL但支持||作为逻辑或不MySQL默认||是逻辑或除非更改模式。我们换一种思路使用报错注入因为报错注入的Payload往往更“畸形”。keywordtest’ and updatexml(1,concat(0x7e,(select%0auser()),0x7e),1)%23将select user()用%0a换行分隔。如果updatexml被拦可以尝试extractvaluekeywordtest’ and extractvalue(1,concat(0x7e,(select%0aversion)))%23常见问题与排查技巧实录问题1无论怎么构造都返回相同的WAF拦截页面。排查可能IP已被WAF拉黑。更换IP或使用代理池。也可能是触发了频率限制需要大幅降低请求速度加入随机延迟。问题2Payload在本地测试成功但打到目标上无效。排查首先确认注入点类型字符/数字/搜索型是否正确。字符型需要闭合引号。其次检查数据库类型。MySQL的Payload不一定适用于MSSQL。使用version、version()、db_name()等数据库特有的函数进行探测。问题3联合查询能执行但无法获取数据显示的列数不对。排查联合查询要求前后列数、数据类型一致。先用order by N确定列数。然后观察页面回显点。将想要的数据放到回显的位置上。例如union select 1,2,3如果页面在显示“2”和“3”的位置输出了我们查询的结果那么后续查询就应该是union select 1, database(), user()。问题4使用sqlmap的tamper脚本依然被拦截。排查WAF可能已经识别了sqlmap的tamper特征。尝试组合使用不常见的tamper脚本或者自己编写tamper脚本。更有效的方法是先手工fuzz出一个能绕过的简单Payload比如一个能触发的布尔盲注条件然后用这个Payload作为模板通过sqlmap的–prefix和–suffix参数来指导其测试而不是完全依赖自动化的tamper。问题5目标是JSON或XML格式的API接口。排查注入点可能在JSON的某个值里。例如{“id”: “1’ and ‘1’’1”}。需要将整个JSON作为POST数据发送并设置Content-Type: application/json。WAF可能对JSON的解析不完善。同样可以尝试JSON语法混淆比如插入多余的逗号、换行符等。绕过WAF是一个持续对抗的过程。今天有效的技巧明天可能就被加入规则库。因此最核心的能力不是记住所有Payload而是掌握分析WAF行为、理解HTTP/SQL解析差异、并灵活组合各种混淆技巧的思维。在实战中保持耐心从最简单的测试开始观察每一次请求的响应变化逐步迭代你的Payload这才是通往成功的路径。最后请务必在合法授权的范围内进行所有测试技术本身无罪但使用技术的意图和方式决定了它的性质。