h3 v1 中的 HTTP 请求走私漏洞 (TE.TE) 分析

2 阅读2分钟

漏洞描述

我在深入分析 h3 v1(具体版本为 v1.15.4)时,发现了一个严重的 HTTP 请求走私漏洞。

基本上,readRawBody 函数在对 Transfer-Encoding 头进行校验时,执行了严格的大小写敏感检查。它明确查找 "chunked" 字符串,但根据 RFC 规范,该头部应该是大小写不敏感的。

问题所在: 如果我发送一个带有 Transfer-Encoding: ChuNked(混合大小写)的请求,h3 会忽略它。由于它没有识别出 "chunked" 且请求中也没有 Content-Length 头,h3 会认为请求体为空并立即处理该请求。

这导致实际的请求体滞留在 socket 上。如果应用程序运行在不对头部进行规范化处理的第 4 层代理(如 AWS NLB 或 Node 代理)后面,就会触发经典的 TE.TE 不同步问题,即请求走私攻击。

漏洞代码 (src/utils/body.ts)

if (
    !Number.parseInt(event.node.req.headers["content-length"] || "") &&
    !String(event.node.req.headers["transfer-encoding"] ?? "")
      .split(",")
      .map((e) => e.trim())
      .filter(Boolean)
      .includes("chunked") // <--- 问题所在。这里 "ChuNkEd" 会返回 false。
  ) {
    return Promise.resolve(undefined);
  }

本地验证

我在本地验证了此漏洞:

  1. 发送一个带有 Transfer-Encoding: ChunKed 头且未发送结束块 0 的请求。
  2. Express 会挂起(正确等待数据)。
  3. h3 会立即响应(存在漏洞,它认为请求体长度为 0)。

影响分析

由于 H3/Nuxt/Nitro 通常部署在容器化环境中,并置于 TCP 负载均衡器之后,攻击者可以利用此漏洞绕过 WAF(Web应用防火墙),或者破坏 socket 状态,从而污染其他用户的连接。

修复方案

只需要在检查前对头部值进行规范化处理:.map((e) => e.trim().toLowerCase())

参考资料