news 2026/6/25 14:37:27

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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的检测点通常有几个关键位置:

  1. 应用层解析后:这是最理想的状态,WAF能够像后端应用一样,完整解析URL编码、解析POST数据格式(如application/x-www-form-urlencoded,multipart/form-data),然后对解析后的纯净数据进行检测。但这种模式对WAF性能要求高。
  2. 原始数据流:WAF直接检测原始的、未经应用完全解析的HTTP请求字节流。这种方式性能开销小,但容易被各种编码和协议特性干扰。
  3. 混合模式:结合以上两种,先进行部分解析或规范化,再进行检测。

绕过的基本思路,就是制造一种“分歧”:让WAF看到的请求内容,与后端Web服务器/应用框架最终解析出来的内容不一致。如果WAF基于原始流或错误解析了请求,而后端正确解析了,那么攻击载荷就可能“瞒天过海”,直达漏洞点。

2.2 HPP参数污染的原理与利用

HPP,全称HTTP Parameter Pollution,即HTTP参数污染。它的核心在于,当同一个参数名在HTTP请求中出现多次时,不同的Web技术栈(如Apache/PHP, Nginx, IIS/ASP.NET, Tomcat/JAVA等)处理这些重复参数的方式不同。

举个例子,一个正常的请求:/test.php?id=1。 一个污染的请求:/test.php?id=1&id=2

  • PHP/Apache:通常取最后一个值,即$_GET[‘id’]2
  • JSP/Tomcat:通常取第一个值,即id1
  • ASP.NET/IIS:有时会将所有值用逗号连接,即id1,2
  • Python Flask:会得到一个包含所有值的列表,即id[‘1’, ‘2’]

绕过WAF的利用点在于:WAF的规则可能只检测第一个id参数的值(1),认为它是安全的。但后端应用(例如PHP)实际取用的是最后一个参数的值(2)。攻击者可以将恶意载荷放在被WAF忽略的那个参数值里。

更高级的用法是结合其他技巧。例如,在SQL注入中: 原始攻击载荷:/test.php?id=1 union select 1,2,3--+会被WAF轻易拦截。 污染攻击载荷:/test.php?id=1/*&id=2 union select 1,2,3--+*/这里,WAF可能只检查第一个id=1/*,看到的是一个被注释掉的、看似无害的值。但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绕过的原理

  1. 解析复杂度:一些老旧或配置不当的WAF可能不支持或不完全解析Transfer-Encoding: chunked请求,它们可能直接放过这类请求,或者解析出错,导致无法正确提取请求体内容进行检测。
  2. 分块切割关键字:攻击者可以将一个危险的攻击载荷(如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字符串。
  3. 大小写变换与畸形分块:协议规定块大小是十六进制数。攻击者可以利用0x0(零大小块)、在块大小数字前后添加空格(如7\r\n)、使用大写十六进制字母(如0A)等畸形格式,干扰WAF的解析器,而主流Web服务器(如Apache、Nginx)的解析容错性更强,仍能正确处理。

2.4 垃圾数据填充的干扰策略

这是一种相对“简单粗暴”但往往有效的思路:用大量的无用数据(垃圾数据)淹没请求,干扰WAF的检测引擎。

主要形式有

  1. 超长参数名/值:将一个参数的值填充得非常长,例如数万甚至数十万个随机字符,然后在末尾附上攻击载荷。某些WAF可能因为性能或规则限制,只检测参数值的前N个字节,从而漏掉末尾的恶意内容。
    /test.php?foo=AAAA...(10万个A)...AAAA&id=1 union select 1,2,3
  2. 大量无用参数:在请求中添加数百个无关紧要的参数,将真正的攻击参数隐藏其中。这可能会增加WAF规则匹配的计算开销,甚至触发某些WAF的“防洪水”机制而直接放行,或者导致日志分析困难。
    /test.php?p1=1&p2=a&p3=xx&...&p99=zzz&id=1 union select 1,2,3
  3. 注释符填充:在SQL注入中,大量使用内联注释/**/。例如uni/**/on/**/sel/**/ect。虽然很多WAF现在能过滤简单注释,但复杂嵌套或超大量的注释仍可能干扰基于正则的检测。
  4. 参数分割:利用一些Web框架的特性,将参数拆散。例如,在PHP中,可以通过id[0]=uni&id[1]=on&id[2]=sel&id[3]=ect传递数组,最终在代码中可能被拼接。或者利用HPP,但填充大量中间参数。

核心思想:消耗WAF的解析和检测资源,利用WAF与后端应用在请求处理容量、超时时间、解析深度上的差异,让恶意载荷成为“漏网之鱼”。

3. 实战环境搭建与漏洞复现

3.1 靶场与工具准备

为了安全地演示这些技术,我们需要一个包含漏洞且受WAF保护的环境。

  1. 靶场选择:DVWA、Pikachu、WebGoat都是不错的选择。这里以Pikachu为例,它集成了多种漏洞类型,方便测试。

    • 部署:从GitHub下载Pikachu源码,放置于PHP运行环境(如XAMPP、PHPStudy)的www目录下。根据其README文件初始化数据库。
    • 访问:浏览器打开http://localhost/pikachu,完成安装。
  2. WAF模拟:为了演示绕过,我们需要一个WAF。可以选择:

    • ModSecurity(开源):作为Apache/Nginx的一个模块,功能强大,规则可自定义。在服务器上安装并启用OWASP CRS规则集,它提供了严格的防护。
    • 云WAF模拟:使用一些开源的WAF测试项目,或者直接利用带有WAF功能的Web应用防火墙设备/软件。对于本地测试,用ModSecurity是最贴近实战的。
    • 简易规则模拟:如果没有条件部署完整WAF,我们可以用Burp Suite的Intruder或编写简单的Python脚本,模拟一个基于正则的检测逻辑,例如拦截所有包含union select的请求。这有助于理解原理。
  3. 核心工具

    • 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?name=admin&submit=查询

步骤一:基础探测与WAF拦截

  1. 浏览器访问页面,输入admin,查询。用Burp拦截请求。
  2. 在Burp Repeater中,将name参数值改为admin',发送。观察响应。如果页面报错或显示异常,说明可能存在注入点。如果被WAF拦截,可能会返回403、406状态码,或者一个统一的拦截页面。
  3. 尝试经典注入admin' and '1'='1admin' and '1'='2,确认注入点。如果被WAF拦截,记录下拦截特征。

步骤二:实施HPP污染

  1. 在Repeater中,修改请求URL,将参数污染为:/vul/sqli/sqli_str.php?name=admin'/*&name=*/ and '1'='1&submit=查询
    • 这里,第一个name的值是admin'/*,WAF可能认为这是一个被注释掉的单引号,无害。
    • 第二个name的值是*/ and '1'='1。如果后端PHP取最后一个值,那么实际执行的查询条件就是name='*/ and '1'='1。注意,这里需要根据后端SQL语句的闭合方式调整。原语句假设是SELECT ... WHERE name='$input'。我们传入*/ and '1'='1,拼接后成为WHERE name='*/ and '1'='1'。由于我们提前用/*注释掉了第一个单引号,所以*/之后的内容and '1'='1就被当作SQL语句执行了。关键在于,/**/需要在数据库层面被当作注释处理(MySQL支持)。
  2. 发送请求。观察页面是否返回与admin' and '1'='1相同的结果(即正常查询结果)。如果返回正常,说明HPP污染成功,WAF可能没有检测第二个name参数。
  3. 进一步验证:将第二个name值改为*/ union select 1,2,3 --+,尝试进行联合查询。注意调整字段数。

实操心得:HPP污染的成功与否,极度依赖后端语言和框架对重复参数的处理顺序。在实战中,如果目标使用PHP,可以优先尝试“取最后一个值”的污染方式。对于JSP,则尝试“取第一个值”。探测方法可以发送?a=1&a=2&a=3,然后观察回显中a的值是1、3还是1,2,3

3.3 利用分块传输编码绕过

我们使用Burp Suite来手动构造分块请求。目标仍然是绕过对union select的检测。

步骤一:准备原始攻击载荷假设我们要注入的最终SQL片段是:‘ union select 1,2,3 --+。我们需要将其嵌入到POST请求的body中。

步骤二:开启Burp的“分块传输编码”插件或手动构造

  1. 使用插件(推荐):Burp Suite的BApp Store中有如“Chunked Transfer Encoding Enabler”等插件,可以方便地将一个普通请求转换为分块编码请求。
  2. 手动构造
    • 在Repeater中,将请求的Content-Type头修改为application/x-www-form-urlencoded(如果需要),并添加Header:Transfer-Encoding: chunked
    • 删除原始的Content-Length头,因为分块编码不需要它。
    • 在请求体(Body)部分,直接写入分块数据。例如,我们将union select拆分:
      name=admin‘+and+1=1+un 6\r\n ion+se 4\r\n lect 0\r\n \r\n
      注意,这里需要将空格编码为+%20,并且每个分块后必须跟\r\n,最后以0\r\n\r\n结束。在Burp的Repeater中,需要切换到“Hex”视图精确编辑,或者在“Raw”视图下确保换行是\r\n。这非常容易出错。

步骤三:发送与调试

  1. 发送构造好的分块请求。
  2. 如果服务器返回错误(如400 Bad Request),可能是分块格式不正确。检查:
    • 每个分块的大小(十六进制数)是否与实际数据字节数完全一致?
    • 每个分块数据后是否有\r\n
    • 结束标志0\r\n\r\n是否正确?
  3. 如果请求成功,但注入未生效,可能是拆分点破坏了SQL语法。需要调整拆分位置,确保重组后的字符串语法正确。
  4. 可以尝试在分块大小数字前后加空格、使用大写十六进制字母等“畸形”格式,测试WAF的解析容错性。

注意事项:手动构造分块请求极其繁琐且易错。在实际渗透测试中,更常见的做法是使用SQLMap的--chunked参数,或者编写Python脚本自动化这个过程。理解原理是为了更好地使用工具和编写检测/防御规则。

3.4 垃圾数据填充实战

我们模拟一个场景:WAF规则设置为“检查URL参数值的前1000个字符”。

步骤一:构造超长参数在Burp Repeater中,针对GET请求的id参数:

  1. 先测试正常注入:id=1 union select 1,2,3,确认被WAF拦截。
  2. 构造超长参数:id=AAAA...(此处用Burp的‘Paste from file’功能粘贴一个包含1500个‘A’的文本文件)...AAAAunion select 1,2,3
  3. 发送请求。如果WAF只解析前1000字节,那么它看到的只是AAAA...A,而union select位于1000字节之后,可能被忽略。后端应用(如PHP的$_GET)则会读取整个字符串,从而执行注入。

步骤二:使用大量无用参数

  1. 在GET或POST请求中,添加大量无关参数。可以用Burp Intruder的“Pitchfork”模式,同时生成大量参数名和随机值。
    /test.php?z0=random0&z1=random1&...&z99=random99&id=1 union select 1,2,3
  2. 观察WAF的响应。有些WAF对单个请求的参数数量或总长度有限制,超出后可能进入“宽松模式”或直接报错放行。

步骤三:注释符填充(SQL注入特定)在SQLMap中,可以直接使用--tamper脚本,如space2comment会将空格替换为/**/。手工测试时,可以尝试:

id=1/**/union/**/select/**/1,2,3 id=1/*!union*//*!select*/1,2,3 (MySQL特性,内联注释中的内容会被执行)

观察哪种变形能绕过检测。

4. 自动化工具辅助与高级技巧

4.1 SQLMap的Tamper脚本运用

SQLMap的强大之处在于其丰富的篡改脚本库,这些脚本自动化地实现了各种绕过技术。

  1. 基础使用sqlmap -u “http://target.com/vul.php?id=1” --tamper=space2comment,charencode

    • space2comment:用/**/替换空格。
    • charencode:对 payload 进行 URL 编码。
    • 可以同时使用多个脚本,SQLMap会按顺序应用。
  2. 针对特定WAF:SQLMap有一些脚本是针对知名WAF的,如modsecurityversioned,apostrophemask等。查看所有脚本:sqlmap --list-tampers

  3. 结合垃圾数据:使用--random-agent随机化User-Agent,--delay设置请求延迟,--time-sec调整时间型注入的响应时间判断,这些都可以增加请求的“噪声”,辅助绕过。

  4. 分块传输支持:使用--chunked参数,SQLMap会自动尝试使用分块编码发送POST数据。这在遇到对POST body有严格检查的WAF时可能有效。

示例命令

sqlmap -u “http://localhost/pikachu/vul/sqli/sqli_str.php?name=admin&submit=查询” \ --data=“name=admin&submit=查询” \ --level=3 --risk=2 \ --tamper=space2comment,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:i+chunk_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, data=body, headers=headers) # 这行不适用于自定义分块 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, params=params) return response.text def send_junk_data_request(url, param, payload, junk_length=10000): """ 发送填充了垃圾数据的请求。 """ junk = ‘’.join(random.choices(string.ascii_letters, k=junk_length)) final_value = junk + payload params = {param: final_value} response = requests.get(url, params=params) 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. 防御视角:如何应对这些绕过手法

作为防御方,了解攻击是为了更好地防御。

  1. 强化WAF规则与解析引擎

    • 规范化处理:WAF在检测前,应对请求进行彻底的规范化(URL解码、Unicode解码、去除多余空格/换行等),并模拟目标Web服务器的解析逻辑。对于HPP,应收集所有同名参数的值,并按照后端语言的可能处理方式(取第一个、最后一个、合并)进行多次检测。
    • 协议完整性校验:必须完整支持并正确解析HTTP/1.1协议,包括分块传输编码。对于畸形的分块格式(如大小写错误、额外空格),应遵循严格标准,可以拒绝服务或将其规范化后再检测。
    • 流式检测与深度分析:应对超长请求体、大量参数有所准备。可以采用流式检测,不一定要完全加载到内存;同时,设置合理的检测深度限制,并对超限部分进行标记或拒绝。
  2. 应用层自身加固

    • 使用预编译语句(参数化查询):这是防止SQL注入的根本方法。无论输入多么畸形,数据库只将参数视为数据,而非指令。HPP、分块、垃圾数据在这些面前都无效。
    • 严格的输入验证与过滤:在接收到参数后,根据业务逻辑进行白名单验证。例如,id参数只允许数字,那么任何非数字字符都应被拒绝。这需要在应用代码中实现。
    • 统一参数处理:明确代码中获取参数的方式。例如在PHP中,对于可能重复的参数,使用$_GET[‘id’]默认取最后一个,但可以主动检查$_GETid键的数量,如果大于1则视为非法请求。
  3. 架构与监控

    • 多层防御:不要只依赖WAF。结合网络层防火墙、入侵检测系统(IDS)、运行时应用自保护(RASP)等,形成纵深防御。
    • 日志记录与分析:记录完整的、原始请求日志(注意隐私合规)。当发生安全事件时,这些原始日志是分析绕过手法的关键。特别要注意记录请求头(如Transfer-Encoding)和完整的请求体。
    • 异常行为监控:监控是否突然出现大量包含Transfer-Encoding: chunked的请求、参数数量异常多的请求、或单个参数值超长的请求。这些可能是攻击的前兆。
  4. 定期测试与更新

    • 定期使用SQLMap等工具(在授权范围内)对自身系统进行扫描,测试WAF规则的有效性。
    • 及时更新WAF规则库,关注公开的WAF绕过技巧和POC,并评估自身风险。

理解WAF绕过技术是一个持续的攻防对抗过程。攻击手法在演进,防御策略也需要不断迭代。对于安全从业者而言,掌握这些底层原理和实战技巧,无论是为了攻还是防,都是构建扎实能力基础的必经之路。真正的安全,源于对细节的深刻理解和对整个攻击面的清醒认知。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 14:36:38

ArkClaw一键部署:云原生AI Agent如何重构人机协作范式

1. 项目概述&#xff1a;当“龙虾”不再需要手把手教&#xff0c;我们真正缺的到底是什么&#xff1f;OpenClaw 这只赛博龙虾&#xff0c;最近确实火得有点离谱。不是因为它多聪明、多强大&#xff0c;而是因为它第一次把“AI Agent”这个概念&#xff0c;从论文标题和极客论坛…

作者头像 李华
网站建设 2026/6/25 14:33:24

如何在3分钟内快速搭建B站视频解析API?完整配置指南

如何在3分钟内快速搭建B站视频解析API&#xff1f;完整配置指南 【免费下载链接】bilibili-parse bilibili Video API 项目地址: https://gitcode.com/gh_mirrors/bi/bilibili-parse 还在为获取B站视频直链而烦恼吗&#xff1f;bilibili-parse作为一款开源的视频解析工具…

作者头像 李华
网站建设 2026/6/25 14:32:13

量子密码分析研究

关于量子密码分析研究的核心见解 量子密码分析分为两大核心研究赛道&#xff1a;1. 量子算法对传统密码的破解分析&#xff08;后量子密码PQC密码分析&#xff09;、2. 量子密钥分发&#xff08;QKD&#xff09;系统的漏洞攻击分析&#xff0c;二者目标、底层逻辑、应用场景完全…

作者头像 李华
网站建设 2026/6/25 14:25:24

如何用命令行工具将得到课程永久保存到本地?dedao-dl完全指南

如何用命令行工具将得到课程永久保存到本地&#xff1f;dedao-dl完全指南 【免费下载链接】dedao-dl 得到 APP 课程下载工具&#xff0c;可在终端查看文章内容&#xff0c;可生成 PDF&#xff0c;音频文件&#xff0c;markdown 文稿&#xff0c;可下载电子书。可结合 openclaw …

作者头像 李华
网站建设 2026/6/25 14:23:47

告别打卡焦虑:5分钟掌握Android自动打卡终极方案

告别打卡焦虑&#xff1a;5分钟掌握Android自动打卡终极方案 【免费下载链接】AutoDingding 钉钉自动打卡 项目地址: https://gitcode.com/gh_mirrors/au/AutoDingding 还在为每天匆忙赶路打卡而焦虑吗&#xff1f;还在担心会议延迟错过考勤时间吗&#xff1f;DailyTask…

作者头像 李华