SQL注入绕过技巧全解析:从WAF检测原理到实战绕过方法

发布时间:2026/7/6 9:24:06
SQL注入绕过技巧全解析:从WAF检测原理到实战绕过方法 1. 项目概述从“注入”到“绕过”的攻防博弈在Web安全领域SQL注入无疑是一个“古老”却又历久弥新的议题。作为一名长期与各类Web应用打交道的安全从业者我见过太多因为一个简单的注入点而导致整个数据库沦陷的案例。然而随着企业安全意识的提升Web应用防火墙WAF和各种安全过滤机制已成为标配。这就引出了一个更深入、也更考验技术功底的问题当常规的注入手法被拦截时我们该如何“绕”过去这里的“绕过”并非鼓励攻击行为而是从防御者视角深刻理解攻击者的思维和手段从而构建更坚固的防线。对于渗透测试人员和安全研究员而言掌握这些绕过技巧是评估WAF防护有效性、挖掘深层漏洞的必备技能。简单来说SQL注入绕过技巧的核心在于“变形”与“混淆”。WAF的检测规则本质上是基于模式匹配它有一张“黑名单”里面记录了UNION SELECT、OR 11、sleep()等大量已知的危险字符串或函数。我们的目标就是通过各种方式让我们的注入载荷Payload“看起来”不像黑名单里的任何一项但其被数据库引擎解析执行后又能产生与原Payload等价的效果。这就像一场猫鼠游戏防守方不断更新规则库攻击方则不断寻找规则的盲区。本文将结合我多年的实战和测试经验为你系统性地拆解那些在真实渗透测试和CTF比赛中高频出现的SQL注入绕过技巧并通过具体场景举例让你不仅知道“是什么”更理解“为什么”以及“怎么用”。2. 绕过技巧的核心原理与分类解析要有效绕过必须先理解WAF是如何工作的。绝大多数WAF的检测逻辑可以简化为以下几个步骤首先对用户输入的字符串进行标准化处理如URL解码、去除多余空白符然后与预定义的正则表达式规则集进行匹配一旦匹配到高危模式则阻断请求并记录日志。因此我们的绕过思路也围绕破坏这两个环节展开。2.1 基于Payload变形的绕过这类技巧不改变注入语句的最终执行逻辑只改变其“书写形式”旨在绕过基于字符串匹配的规则。1. 大小写混合绕过这是最基础的手法。许多简单的WAF规则是大小写敏感的只匹配union select但不会匹配UnIoN SeLeCt。原理数据库引擎如MySQL在解析SQL关键字时通常不区分大小写但字符串匹配规则可能区分。示例?id1 UnIoN SeLeCt 1,2,3--2. 双写关键字绕过针对一些采用简单字符串替换、且只替换一次的过滤机制。例如代码中使用了str_replace(“union”, “”, $input)。原理将union写为ununionion。当过滤器移除中间的union后剩下的字符恰好又拼接成了union。示例?id1 ununionion selselectect 1,2,3--。过滤后变为1 union select 1,2,3--。3. 使用等价函数或操作符替换当特定函数名如substr(),sleep()被拦截时寻找功能相同的“冷门”函数。原理WAF的规则库可能未覆盖所有功能等价的函数。示例substr(database(),1,1)被拦截 → 可尝试mid(database(),1,1)或substring(database() from 1 for 1)。报错注入中updatexml()被拦截 → 可尝试extractvalue()或polygon()。逻辑运算符and被拦截 → 使用or被拦截 → 使用||not被拦截 → 使用!。4. 编码与十六进制绕过对注入语句的全部或部分进行编码以绕过对明文字符的检测。原理WAF可能只检测解码前的数据或者其解码逻辑与后端应用不一致。示例URL编码union select→%75%6e%69%6f%6e%20%73%65%6c%65%63%74。某些WAF可能只做一次解码但后端框架或数据库驱动可能会做多次解码。十六进制编码常用于绕过引号过滤。例如表名users的十六进制是0x7573657273。select * from users where id1可写为select * from 0x7573657273 where id1。Unicode编码在特定环境下有效如u的Unicode形式%u0075。5. 注释符与空白符混淆利用SQL注释符和多种空白符变体打断危险字符串的连续性。原理WAF的正则可能将union select作为一个整体来检测。插入注释或特殊空白符可以将其切分。示例union select→union/**/select或union%0aselect%0a是换行符。?id1→?id1e0union select利用科学计数法1e0即1。MySQL中还可以使用内联注释/*!50001union select*/其中的50001表示MySQL版本号大于等于5.00.01时才执行其中的语句常用于绕过一些简单的过滤。实操心得这些方法往往需要组合使用。一个有效的Payload可能是这样的?id1UnIoN/*!50000SeLeCt*/1,2,group_concat(table_name)from%0ainformation_schema.tables%23。它混合了大小写、内联注释、特殊空白符和注释符。2.2 基于SQL语法特性的绕过这类技巧更深入一层利用数据库SQL语法的灵活性构造出语法正确但形态迥异的语句。1. 逗号绕过在substr(),limit等子句中逗号常被过滤。原理利用from...for...语法替代逗号参数。示例substr(database(), 1, 1)→substr(database() from 1 for 1)。limit 0,1→limit 1 offset 0。2. 比较符绕过当被过滤时。原理使用like,rlike,regexp或in进行等价比较。示例?id1 and substr(database(),1,1)a→?id1 and substr(database(),1,1) like a或?id1 and substr(database(),1,1) in (a)。3. 浮点数与科学计数法绕过在数字型注入中干扰数字的书写形式。原理1、1.0、1E0在数据库中都被视为数字1。示例?id1 union select→?id1.0union select或?id1E0union select。4. 反引号与花括号在MySQL中反引号用于引用标识符数据库名、表名、字段名有时可以干扰WAF解析。示例selectusernamefromusers。某些WAF的解析器可能无法正确处理嵌套的反引号导致检测失效。花括号{}在特定上下文如Hibernate HQL注入中也可能有类似效果但在纯SQL中不常见。2.3 基于HTTP协议与请求结构的绕过这类技巧跳出了SQL语句本身从HTTP请求的层面做文章针对的是WAF处理HTTP数据包的方式。1. 参数污染利用应用程序在处理多个同名参数时的特性。原理不同语言/框架对id1id2的处理方式不同。PHP/Apache会取最后一个值(id2)而JSP/Tomcat可能取第一个(id1).NET/IIS则会拼接成数组(id1,2)。如果WAF检测第一个参数而应用取最后一个参数则可绕过。示例构造请求?id1id2 union select 1,2,3--。WAF检查id1安全而PHP后端实际使用id2 union select 1,2,3--恶意。2. 分块传输编码将请求体分块发送。原理有些WAF可能因为不支持分块传输或者为了性能只检查第一个数据块从而放过恶意载荷。使用Burp Suite的“分块传输编码”插件可以轻松实现。示例将union select拆分到不同的数据块中发送。3. 脏数据溢出在POST请求体中在真正的参数前填充大量无用数据垃圾参数。原理一些WAF可能由于性能考虑只检查请求体前一定字节如1KB。用垃圾数据填满这个检查窗口后续的注入语句就被“挤”到检测范围之外了。示例POST /login.php HTTP/1.1请求体为a垃圾数据...重复上千次usernameadmin union select 1,2,3--passwordtest4. GET/POST方法转换同一个处理接口可能同时支持GET和POST但WAF对两者的检测规则强度可能不同。原理WAF策略可能对GET请求的参数检测更严格因为URL在日志中更显眼而对POST请求体检测较松。尝试将注入点从GET参数改为POST参数有时能直接绕过。实操步骤用Burp Suite抓取GET请求将其改为POST请求并将参数移到Body中Content-Type改为application/x-www-form-urlencoded。3. 实战场景举例与Payload构造理论说再多不如看几个“活”的例子。我们假设一个常见的场景一个存在数字型SQL注入漏洞的URLhttp://target.com/news.php?id1后端使用了某种WAF进行防护。3.1 场景一联合查询注入的绕过目标获取数据库表名。初始Payload?id1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schemadatabase()--假设WAF拦截了union select和information_schema。绕过构造过程第一步处理union select尝试大小写UnIoN SeLeCt→ 可能被更智能的WAF无视大小写匹配到。尝试内联注释空白符union/*!50000select*/或union%0aselect。尝试双写ununionion selselectect→ 如果WAF是简单替换一次则有效。组合拳?id1ununionion/*!50000selselectect*/1,2,3--第二步处理information_schema这个库名太敏感容易被直接关键词过滤。我们可以使用十六进制编码information_schema.tables→0x696E666F726D6174696F6E5F736368656D612E7461626C6573。注意整个“库.表”字符串需要一起编码。利用MySQL 5.7的sys.schema_table_statistics等替代视图如果可用union select 1,2,table_name from sys.schema_table_statistics where table_schemadatabase()。这不是标准方法但在某些环境下是有效的旁路。最终组合Payload?id1ununionion/*!50000selselectect*/1,2,group_concat(table_name)from0x696E666F726D6174696F6E5F736368656D612E7461626C6573wheretable_schemadatabase()--3.2 场景二报错注入的绕过目标利用updatexml()报错带出数据。初始Payload?id1 and updatexml(1,concat(0x7e,(select user()),0x7e),1)--假设WAF拦截了updatexml和concat。绕过构造过程寻找替代函数updatexml()被拦 → 尝试extractvalue()and extractvalue(1,concat(0x7e,(select user())))如果都被拦尝试更冷门的polygon()、multipoint()等几何函数但需注意这些函数的参数格式与updatexml不同需要构造特定的错误。示例and polygon((select*from(select*from(select user())a)b))可能会产生错误信息。处理concat和引号concat()被拦 → 尝试使用||连接符需在数据库开启PIPES_AS_CONCAT模式Oracle/PG默认支持updatexml(1, 0x7e||(select user())||0x7e, 1)。单引号被过滤 → 所有字符串都用十六进制表示。0x7e就是波浪线~的十六进制。最终Payload?id1andextractvalue(1,0x7e||(selecthex(table_name)frominformation_schema.tableslimit1))--3.3 场景三布尔盲注/时间盲注的绕过目标判断数据库名第一个字符是否为‘a’。初始Payload布尔?id1 and substr(database(),1,1)a初始Payload时间?id1 and if(substr(database(),1,1)a,sleep(3),0)假设WAF拦截了substr、sleep和等号。绕过构造过程替换substr使用mid()或substring() from for语法。substr(database(),1,1)a→mid(database() from 1 for 1)a替换等号使用like或in。...a→... like a或... in (a)替换sleep时间盲注使用重型查询制造延迟。sleep(3)→benchmark(10000000,md5(test))。benchmark函数会重复执行表达式多次消耗时间。笛卡尔积查询select count(*) from information_schema.columns A, information_schema.columns B, information_schema.columns C。但这种方式不稳定且可能对数据库造成压力慎用。处理逗号如果substr(database(),1,1)中的逗号被过滤使用from...for...语法。mid(database() from 1 for 1)最终组合Payload布尔?id1 and mid(database() from 1 for 1) like 0x61(0x61是‘a’的十六进制)最终组合Payload时间?id1 and if(mid(database() from 1 for 1) like 0x61, benchmark(5000000,md5(0x61)),0)4. 高级绕过技巧与协议层攻击当应用层绕过全部失效时我们需要将目光投向更底层的HTTP协议和WAF部署架构本身。4.1 HTTP参数污染与畸形请求原理利用WAF与后端Web服务器如Nginx、Apache、IIS或应用框架如PHP、Java Spring在解析HTTP请求时的差异。实战案例多个Content-Type头在请求中同时包含Content-Type: application/x-www-form-urlencoded和Content-Type: multipart/form-data。WAF可能按前者解析认为无文件上传而后端服务器按后者解析可能成功处理注入。畸形的参数分隔符标准的GET参数分隔符是。但有些解析器也接受;。尝试?id1;select 1name2WAF可能只看到id1而后端看到了id1;select 1。参数位置变换将注入参数放在URL路径、Cookie或HTTP头中如X-Forwarded-For。有些WAF默认只检测GET/POST参数忽略其他位置。注意事项这类方法高度依赖于具体的WAF产品、版本和后端技术栈。在实战中需要利用Burp Suite的Intruder模块加载畸形的请求头/参数列表进行Fuzz测试观察哪些变体能够“通”到后端并产生预期的SQL错误或延迟。4.2 分块传输编码与Pipeline这是针对那些不支持或不能正确解析这些HTTP/1.1特性的WAF的“杀手锏”。分块传输编码在Burp Suite中将请求的Content-Type改为application/x-www-form-urlencoded如果是POST。在Burp的Proxy - Options - Match and Replace 中添加规则或使用“HTTP Request Smuggler”等插件自动将请求转换为分块格式。核心原理是将union select这样的关键词拆分到不同的“块”中。WAF可能重组失败或只检查第一个块从而放过恶意载荷。HTTP Pipeline在一个TCP连接中连续发送两个HTTP请求。第一个请求是正常的、无害的请求。第二个请求是包含注入语句的恶意请求但紧跟在第一个请求的尾部中间没有换行。某些WAF可能只检测每个TCP连接中的第一个请求或者因为解析混乱而漏掉第二个请求。4.3 白名单与边界绕过如果目标存在WAF但防护的路径或文件不全就可能存在“后门”。静态文件白名单WAF可能配置了对.jpg,.png,.css,.js等静态资源的请求不进行检测。如果存在文件上传漏洞且能上传一个.jpg文件但内容被当作PHP执行配合解析漏洞那么向这个.jpg文件发起的POST请求中的SQL注入就可能绕过WAF。管理后台/API接口遗漏WAF策略可能只应用于主站www.target.com而遗漏了管理后台admin.target.com或某个特定的API域名api.target.com。信息收集时子域名枚举至关重要。云WAF的“源站暴露”很多企业使用云WAF如阿里云云盾、腾讯云WAF。流量路径是用户 - 云WAF - 源站服务器。如果源站服务器的IP地址被泄露通过历史DNS记录、子域名探测、SSRF漏洞等攻击者直接访问源站IP就完全绕过了云WAF。5. 自动化工具与Fuzz测试思路手动构造绕过Payload效率低下在实际渗透测试中我们通常借助工具进行半自动化或自动化的Fuzz。1. 使用Sqlmap的Tamper脚本Sqlmap内置了数十个tamper脚本专门用于绕过WAF。其原理就是自动应用我们上面讨论的各种技巧。常用命令sqlmap -u “http://target.com/news.php?id1” --tamperspace2comment,randomcase,charencodespace2comment将空格替换为/**/randomcase随机大小写charencodeURL编码between用between替换比较符高级用法可以同时使用多个tamper脚本甚至编写自定义的tamper脚本Python定义自己的绕过规则。2. 使用Burp Suite Intruder进行Fuzz对于更复杂的、协议层的绕过需要手动Fuzz。步骤抓取存在注入点的正常请求。发送到Intruder。在注入点参数位置设置Payload标记。Payload Sets这是关键。你需要准备多种Payload列表关键词变异列表包含union select的各种变形大小写、双写、注释插入等。函数替换列表substr-mid,substringsleep-benchmark等。特殊字符列表各种空白符%09,%0a,%0b,%0c,%0d,%a0、注释符--,#,/*!*/。编码列表URL编码、双重URL编码、十六进制编码。根据响应长度、时间、状态码或关键词如SQL错误信息来识别成功的Payload。3. 组合思维与迭代测试自动化不是万能的。最有效的绕过往往是多种技巧的组合并且需要根据WAF的响应进行动态调整。流程先使用Sqlmap的--tamper进行初步探测观察被拦截的特征。分析如果Sqlmap被拦查看其Payload和WAF的拦截页面/日志判断是哪个关键词或模式触发了规则。定制针对触发的规则手动设计绕过方案。例如如果发现information_schema被精准拦截就尝试用十六进制编码或寻找替代的系统视图。升级如果应用层全部失败考虑协议层攻击如分块传输、参数污染等。6. 防御视角如何构建更有效的防线理解了攻击者的绕过手段作为防御者我们可以做得更好。单纯的“黑名单”式WAF规则是脆弱的必须采用纵深防御策略。输入验证与参数化查询根本解决白名单验证对于id参数严格验证其是否为整数。不是则拒绝。使用预编译语句参数化查询这是防止SQL注入的终极武器。让数据库将代码和数据分开处理无论用户输入什么都被视为数据无法改变SQL语句的结构。在Java中使用PreparedStatement在PHP中使用PDO的prepare和bindParam。WAF规则优化语义分析不要只做简单的字符串匹配。尝试解析SQL语法树识别出无论怎么变形其语义仍是union select的语句。多次规范化对输入进行多次URL解码、HTML解码、UTF-7解码等确保在匹配前已还原到最原始的形式。检查所有输入点包括URL参数、POST Body、Cookie、HTTP头如User-Agent,X-Forwarded-For。关注协议合规性严格校验HTTP协议格式对分块传输、Pipeline等特性有完善的处理逻辑拒绝畸形请求。Web应用自身加固最小权限原则数据库连接账户只赋予应用所需的最小权限如只有SELECT权限无DROP,FILE等。错误信息处理自定义错误页面避免将数据库的详细错误信息如表名、列名、SQL语句直接返回给用户。定期安全扫描与代码审计使用DAST/SAST工具扫描自身应用或进行人工代码审计从源头消灭漏洞。架构层面隐藏真实IP如果使用云WAF确保源站IP不对外暴露只接受来自云WAF IP段的流量。RASP运行时应用自我保护在应用内部监控异常行为如异常的SQL语句执行模式比外置WAF更贴近业务逻辑更难被绕过。绕过的艺术本质上是攻防双方对同一套系统理解深度的较量。攻击者在寻找规则集合的“缝隙”而防御者在努力让这个集合变得“密不透风”。这场博弈不会停止新的绕过技巧和防御技术总会不断涌现。对于安全从业者而言保持持续学习、深入理解底层原理、并辅以大量的动手实践才是应对这场动态博弈的不二法门。我个人在每一次绕过测试中最大的收获不是那个成功的Payload而是在构造和调试过程中对数据库行为、HTTP协议和应用逻辑更深一层的认知。