WAF绕过实战:HPP污染、分块传输与垃圾数据填充技术解析

WAF绕过实战:HPP污染、分块传输与垃圾数据填充技术解析
1. 项目概述当WAF遇上“奇技淫巧”在渗透测试或安全研究的实战中Web应用防火墙WAF常常是横亘在攻击路径上的一道坚实壁垒。它像一位经验丰富的门卫能识别并拦截大量常规的、特征明显的攻击载荷。然而正如再坚固的城墙也可能存在视觉盲区WAF的防御逻辑也并非无懈可击。今天要聊的就是绕过WAF的几种经典且有效的“奇技淫巧”HPP参数污染、分块传输编码以及垃圾数据填充。这不仅仅是几个技术名词的堆砌而是面对现代WAF防御体系时攻击者或安全测试人员为了达成目标在HTTP协议层面进行的精妙“手术”。理解这些技术对于防守方而言是加固防线、查漏补缺的关键对于攻击方在授权测试范围内则是突破瓶颈、验证漏洞真实危害的必经之路。我们将从原理拆解到实战复现一步步揭开这些技术的神秘面纱让你不仅知道怎么用更明白为什么能用以及如何防御。2. 核心攻击原理与WAF绕过思路拆解2.1 WAF的工作机制与检测盲区要绕过WAF首先得知道它是怎么工作的。绝大多数WAF无论是云WAF、硬件WAF还是软件WAF其核心检测引擎都依赖于规则匹配。这些规则通常基于正则表达式对HTTP请求的各个部分如URL、参数、Header、Body进行扫描寻找已知的攻击模式例如SQL注入的关键字union,select,sleep、XSS的脚本标签script、命令执行的管道符|等。WAF的检测点通常有几个关键位置应用层解析后这是最理想的状态WAF能够像后端应用一样完整解析URL编码、解析POST数据格式如application/x-www-form-urlencoded,multipart/form-data然后对解析后的纯净数据进行检测。但这种模式对WAF性能要求高。原始数据流WAF直接检测原始的、未经应用完全解析的HTTP请求字节流。这种方式性能开销小但容易被各种编码和协议特性干扰。混合模式结合以上两种先进行部分解析或规范化再进行检测。绕过的基本思路就是制造一种“分歧”让WAF看到的请求内容与后端Web服务器/应用框架最终解析出来的内容不一致。如果WAF基于原始流或错误解析了请求而后端正确解析了那么攻击载荷就可能“瞒天过海”直达漏洞点。2.2 HPP参数污染的原理与利用HPP全称HTTP Parameter Pollution即HTTP参数污染。它的核心在于当同一个参数名在HTTP请求中出现多次时不同的Web技术栈如Apache/PHP, Nginx, IIS/ASP.NET, Tomcat/JAVA等处理这些重复参数的方式不同。举个例子一个正常的请求/test.php?id1。 一个污染的请求/test.php?id1id2。PHP/Apache通常取最后一个值即$_GET[‘id’]为2。JSP/Tomcat通常取第一个值即id为1。ASP.NET/IIS有时会将所有值用逗号连接即id为1,2。Python Flask会得到一个包含所有值的列表即id为[‘1’, ‘2’]。绕过WAF的利用点在于WAF的规则可能只检测第一个id参数的值1认为它是安全的。但后端应用例如PHP实际取用的是最后一个参数的值2。攻击者可以将恶意载荷放在被WAF忽略的那个参数值里。更高级的用法是结合其他技巧。例如在SQL注入中 原始攻击载荷/test.php?id1 union select 1,2,3--会被WAF轻易拦截。 污染攻击载荷/test.php?id1/*id2 union select 1,2,3--*/这里WAF可能只检查第一个id1/*看到的是一个被注释掉的、看似无害的值。但PHP后端取最后一个id其值为2 union select 1,2,3--*/注释符*/闭合了前面的/*使得union select语句成功注入。注意HPP的成功率高度依赖于目标服务器和WAF的具体实现。在实战中需要先进行参数处理方式的探测。2.3 分块传输编码的协议特性与利用分块传输编码Chunked Transfer Encoding是HTTP/1.1协议中定义的一种数据传输机制。它允许服务器在未知内容总长度的情况下逐步发送数据。客户端或攻击者作为客户端也可以使用它来发送请求体。一个标准的分块请求体格式如下POST /test.php HTTP/1.1 ... Transfer-Encoding: chunked 7\r\n Mozilla\r\n 9\r\n Developer\r\n 7\r\n Network\r\n 0\r\n \r\n每一“块”由“块大小十六进制\r\n” “数据内容\r\n”组成。以“0\r\n\r\n”表示结束。WAF绕过的原理解析复杂度一些老旧或配置不当的WAF可能不支持或不完全解析Transfer-Encoding: chunked请求它们可能直接放过这类请求或者解析出错导致无法正确提取请求体内容进行检测。分块切割关键字攻击者可以将一个危险的攻击载荷如union select切割到两个或多个分块中。例如4\r\n uni\r\n 4\r\n on s\r\n 4\r\n elec\r\n 2\r\n t\r\n 0\r\n \r\n单独看每个分块uni,on s,elec,t都不构成完整的SQL关键字可能绕过基于正则表达式的检测。但后端服务器在重组这些分块后得到的是完整的union select字符串。大小写变换与畸形分块协议规定块大小是十六进制数。攻击者可以利用0x0零大小块、在块大小数字前后添加空格如7\r\n、使用大写十六进制字母如0A等畸形格式干扰WAF的解析器而主流Web服务器如Apache、Nginx的解析容错性更强仍能正确处理。2.4 垃圾数据填充的干扰策略这是一种相对“简单粗暴”但往往有效的思路用大量的无用数据垃圾数据淹没请求干扰WAF的检测引擎。主要形式有超长参数名/值将一个参数的值填充得非常长例如数万甚至数十万个随机字符然后在末尾附上攻击载荷。某些WAF可能因为性能或规则限制只检测参数值的前N个字节从而漏掉末尾的恶意内容。/test.php?fooAAAA...10万个A...AAAAid1 union select 1,2,3大量无用参数在请求中添加数百个无关紧要的参数将真正的攻击参数隐藏其中。这可能会增加WAF规则匹配的计算开销甚至触发某些WAF的“防洪水”机制而直接放行或者导致日志分析困难。/test.php?p11p2ap3xx...p99zzzid1 union select 1,2,3注释符填充在SQL注入中大量使用内联注释/**/。例如uni/**/on/**/sel/**/ect。虽然很多WAF现在能过滤简单注释但复杂嵌套或超大量的注释仍可能干扰基于正则的检测。参数分割利用一些Web框架的特性将参数拆散。例如在PHP中可以通过id[0]uniid[1]onid[2]selid[3]ect传递数组最终在代码中可能被拼接。或者利用HPP但填充大量中间参数。核心思想消耗WAF的解析和检测资源利用WAF与后端应用在请求处理容量、超时时间、解析深度上的差异让恶意载荷成为“漏网之鱼”。3. 实战环境搭建与漏洞复现3.1 靶场与工具准备为了安全地演示这些技术我们需要一个包含漏洞且受WAF保护的环境。靶场选择DVWA、Pikachu、WebGoat都是不错的选择。这里以Pikachu为例它集成了多种漏洞类型方便测试。部署从GitHub下载Pikachu源码放置于PHP运行环境如XAMPP、PHPStudy的www目录下。根据其README文件初始化数据库。访问浏览器打开http://localhost/pikachu完成安装。WAF模拟为了演示绕过我们需要一个WAF。可以选择ModSecurity开源作为Apache/Nginx的一个模块功能强大规则可自定义。在服务器上安装并启用OWASP CRS规则集它提供了严格的防护。云WAF模拟使用一些开源的WAF测试项目或者直接利用带有WAF功能的Web应用防火墙设备/软件。对于本地测试用ModSecurity是最贴近实战的。简易规则模拟如果没有条件部署完整WAF我们可以用Burp Suite的Intruder或编写简单的Python脚本模拟一个基于正则的检测逻辑例如拦截所有包含union select的请求。这有助于理解原理。核心工具Burp Suite Professional必备。其Repeater、Intruder、Decoder功能是手动构造和测试畸形请求的利器。Proxy用于拦截流量Repeater用于修改重放Intruder用于自动化参数Fuzz和绕过测试。SQLMap自动化SQL注入工具。它内置了多种绕过WAF的篡改脚本--tamper如charencode,space2comment,equaltolike等其中一些就运用了今天讨论的技术。我们将用它来验证手工绕过的有效性并学习其自动化技巧。浏览器开发者工具用于观察原始请求和响应。Python脚本用于自定义复杂的请求构造例如生成分块编码或大量垃圾数据。3.2 手工HPP污染绕过实验假设Pikachu靶场的SQL注入字符型漏洞点在/vul/sqli/sqli_str.php参数为name。正常请求为GET /vul/sqli/sqli_str.php?nameadminsubmit查询。步骤一基础探测与WAF拦截浏览器访问页面输入admin查询。用Burp拦截请求。在Burp Repeater中将name参数值改为admin发送。观察响应。如果页面报错或显示异常说明可能存在注入点。如果被WAF拦截可能会返回403、406状态码或者一个统一的拦截页面。尝试经典注入admin and 11和admin and 12确认注入点。如果被WAF拦截记录下拦截特征。步骤二实施HPP污染在Repeater中修改请求URL将参数污染为/vul/sqli/sqli_str.php?nameadmin/*name*/ and 11submit查询。这里第一个name的值是admin/*WAF可能认为这是一个被注释掉的单引号无害。第二个name的值是*/ and 11。如果后端PHP取最后一个值那么实际执行的查询条件就是name*/ and 11。注意这里需要根据后端SQL语句的闭合方式调整。原语句假设是SELECT ... WHERE name$input。我们传入*/ and 11拼接后成为WHERE name*/ and 11。由于我们提前用/*注释掉了第一个单引号所以*/之后的内容and 11就被当作SQL语句执行了。关键在于/*和*/需要在数据库层面被当作注释处理MySQL支持。发送请求。观察页面是否返回与admin and 11相同的结果即正常查询结果。如果返回正常说明HPP污染成功WAF可能没有检测第二个name参数。进一步验证将第二个name值改为*/ union select 1,2,3 --尝试进行联合查询。注意调整字段数。实操心得HPP污染的成功与否极度依赖后端语言和框架对重复参数的处理顺序。在实战中如果目标使用PHP可以优先尝试“取最后一个值”的污染方式。对于JSP则尝试“取第一个值”。探测方法可以发送?a1a2a3然后观察回显中a的值是1、3还是1,2,3。3.3 利用分块传输编码绕过我们使用Burp Suite来手动构造分块请求。目标仍然是绕过对union select的检测。步骤一准备原始攻击载荷假设我们要注入的最终SQL片段是‘ union select 1,2,3 --。我们需要将其嵌入到POST请求的body中。步骤二开启Burp的“分块传输编码”插件或手动构造使用插件推荐Burp Suite的BApp Store中有如“Chunked Transfer Encoding Enabler”等插件可以方便地将一个普通请求转换为分块编码请求。手动构造在Repeater中将请求的Content-Type头修改为application/x-www-form-urlencoded如果需要并添加HeaderTransfer-Encoding: chunked。删除原始的Content-Length头因为分块编码不需要它。在请求体Body部分直接写入分块数据。例如我们将union select拆分nameadmin‘and11un 6\r\n ionse 4\r\n lect 0\r\n \r\n注意这里需要将空格编码为或%20并且每个分块后必须跟\r\n最后以0\r\n\r\n结束。在Burp的Repeater中需要切换到“Hex”视图精确编辑或者在“Raw”视图下确保换行是\r\n。这非常容易出错。步骤三发送与调试发送构造好的分块请求。如果服务器返回错误如400 Bad Request可能是分块格式不正确。检查每个分块的大小十六进制数是否与实际数据字节数完全一致每个分块数据后是否有\r\n结束标志0\r\n\r\n是否正确如果请求成功但注入未生效可能是拆分点破坏了SQL语法。需要调整拆分位置确保重组后的字符串语法正确。可以尝试在分块大小数字前后加空格、使用大写十六进制字母等“畸形”格式测试WAF的解析容错性。注意事项手动构造分块请求极其繁琐且易错。在实际渗透测试中更常见的做法是使用SQLMap的--chunked参数或者编写Python脚本自动化这个过程。理解原理是为了更好地使用工具和编写检测/防御规则。3.4 垃圾数据填充实战我们模拟一个场景WAF规则设置为“检查URL参数值的前1000个字符”。步骤一构造超长参数在Burp Repeater中针对GET请求的id参数先测试正常注入id1 union select 1,2,3确认被WAF拦截。构造超长参数idAAAA...此处用Burp的‘Paste from file’功能粘贴一个包含1500个‘A’的文本文件...AAAAunion select 1,2,3。发送请求。如果WAF只解析前1000字节那么它看到的只是AAAA...A而union select位于1000字节之后可能被忽略。后端应用如PHP的$_GET则会读取整个字符串从而执行注入。步骤二使用大量无用参数在GET或POST请求中添加大量无关参数。可以用Burp Intruder的“Pitchfork”模式同时生成大量参数名和随机值。/test.php?z0random0z1random1...z99random99id1 union select 1,2,3观察WAF的响应。有些WAF对单个请求的参数数量或总长度有限制超出后可能进入“宽松模式”或直接报错放行。步骤三注释符填充SQL注入特定在SQLMap中可以直接使用--tamper脚本如space2comment会将空格替换为/**/。手工测试时可以尝试id1/**/union/**/select/**/1,2,3 id1/*!union*//*!select*/1,2,3 MySQL特性内联注释中的内容会被执行观察哪种变形能绕过检测。4. 自动化工具辅助与高级技巧4.1 SQLMap的Tamper脚本运用SQLMap的强大之处在于其丰富的篡改脚本库这些脚本自动化地实现了各种绕过技术。基础使用sqlmap -u “http://target.com/vul.php?id1” --tamperspace2comment,charencodespace2comment用/**/替换空格。charencode对 payload 进行 URL 编码。可以同时使用多个脚本SQLMap会按顺序应用。针对特定WAFSQLMap有一些脚本是针对知名WAF的如modsecurityversioned,apostrophemask等。查看所有脚本sqlmap --list-tampers。结合垃圾数据使用--random-agent随机化User-Agent--delay设置请求延迟--time-sec调整时间型注入的响应时间判断这些都可以增加请求的“噪声”辅助绕过。分块传输支持使用--chunked参数SQLMap会自动尝试使用分块编码发送POST数据。这在遇到对POST body有严格检查的WAF时可能有效。示例命令sqlmap -u “http://localhost/pikachu/vul/sqli/sqli_str.php?nameadminsubmit查询” \ --data“nameadminsubmit查询” \ --level3 --risk2 \ --tamperspace2comment,equaltolike \ --random-agent \ --dbs这条命令尝试使用注释替换空格、将替换为LIKE并使用随机User-Agent来绕过可能的简单过滤。4.2 自定义Python构造器当现成工具不够用时编写Python脚本提供最大的灵活性。import requests import random import string def send_chunked_request(url, param, payload): 构造一个包含分块编码的POST请求将payload隐藏在分块中。 # 将payload拆分成随机大小的块这里简单按固定大小拆 chunk_size 4 chunks [payload[i:ichunk_size] for i in range(0, len(payload), chunk_size)] # 构建分块体 body b“” for chunk in chunks: size_hex hex(len(chunk))[2:].encode() # 块大小十六进制去掉‘0x’ body size_hex b“\r\n” chunk.encode() b“\r\n” body b“0\r\n\r\n” # 结束块 headers { ‘Host’: ‘localhost’, ‘Transfer-Encoding’: ‘chunked’, ‘Content-Type’: ‘application/x-www-form-urlencoded’, } # 注意requests库默认不支持直接发送原始的、自定义的分块体。 # 通常需要更低层的库如http.client或修改requests的适配器。 # 这里仅为逻辑演示。实际中常用socket或httplib。 print(“[] Chunked body constructed. Need raw socket to send.“) # 实际发送部分省略示意逻辑 # response requests.post(url, databody, headersheaders) # 这行不适用于自定义分块 def send_polluted_request(url, base_param, payload_param): 发送HPP污染请求。 params {} # 添加多个同名参数将payload放在特定位置如最后一个 for i in range(5): params[f‘{base_param}’] f‘dummy{i}’ # 填充垃圾参数值 params[f‘{base_param}’] payload_param # 关键payload作为最后一个 response requests.get(url, paramsparams) return response.text def send_junk_data_request(url, param, payload, junk_length10000): 发送填充了垃圾数据的请求。 junk ‘’.join(random.choices(string.ascii_letters, kjunk_length)) final_value junk payload params {param: final_value} response requests.get(url, paramsparams) return response # 使用示例 if __name__ “__main__”: target_url “http://localhost/pikachu/vul/sqli/sqli_str.php” # 测试HPP污染 # resp send_polluted_request(target_url, ‘name’, “admin‘ union select 1,2,3 --”) # print(resp)提示使用Python的socket库或http.client库可以更精细地控制HTTP请求的每一个字节是构造畸形协议请求的终极手段。但对于大多数情况熟练使用Burp Suite和SQLMap的tamper脚本已经足够。5. 防御视角如何应对这些绕过手法作为防御方了解攻击是为了更好地防御。强化WAF规则与解析引擎规范化处理WAF在检测前应对请求进行彻底的规范化URL解码、Unicode解码、去除多余空格/换行等并模拟目标Web服务器的解析逻辑。对于HPP应收集所有同名参数的值并按照后端语言的可能处理方式取第一个、最后一个、合并进行多次检测。协议完整性校验必须完整支持并正确解析HTTP/1.1协议包括分块传输编码。对于畸形的分块格式如大小写错误、额外空格应遵循严格标准可以拒绝服务或将其规范化后再检测。流式检测与深度分析应对超长请求体、大量参数有所准备。可以采用流式检测不一定要完全加载到内存同时设置合理的检测深度限制并对超限部分进行标记或拒绝。应用层自身加固使用预编译语句参数化查询这是防止SQL注入的根本方法。无论输入多么畸形数据库只将参数视为数据而非指令。HPP、分块、垃圾数据在这些面前都无效。严格的输入验证与过滤在接收到参数后根据业务逻辑进行白名单验证。例如id参数只允许数字那么任何非数字字符都应被拒绝。这需要在应用代码中实现。统一参数处理明确代码中获取参数的方式。例如在PHP中对于可能重复的参数使用$_GET[‘id’]默认取最后一个但可以主动检查$_GET中id键的数量如果大于1则视为非法请求。架构与监控多层防御不要只依赖WAF。结合网络层防火墙、入侵检测系统IDS、运行时应用自保护RASP等形成纵深防御。日志记录与分析记录完整的、原始请求日志注意隐私合规。当发生安全事件时这些原始日志是分析绕过手法的关键。特别要注意记录请求头如Transfer-Encoding和完整的请求体。异常行为监控监控是否突然出现大量包含Transfer-Encoding: chunked的请求、参数数量异常多的请求、或单个参数值超长的请求。这些可能是攻击的前兆。定期测试与更新定期使用SQLMap等工具在授权范围内对自身系统进行扫描测试WAF规则的有效性。及时更新WAF规则库关注公开的WAF绕过技巧和POC并评估自身风险。理解WAF绕过技术是一个持续的攻防对抗过程。攻击手法在演进防御策略也需要不断迭代。对于安全从业者而言掌握这些底层原理和实战技巧无论是为了攻还是防都是构建扎实能力基础的必经之路。真正的安全源于对细节的深刻理解和对整个攻击面的清醒认知。