漏洞描述
我在深入分析 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);
}
本地验证
我在本地验证了此漏洞:
- 发送一个带有
Transfer-Encoding: ChunKed头且未发送结束块0的请求。 - Express 会挂起(正确等待数据)。
- h3 会立即响应(存在漏洞,它认为请求体长度为 0)。
影响分析
由于 H3/Nuxt/Nitro 通常部署在容器化环境中,并置于 TCP 负载均衡器之后,攻击者可以利用此漏洞绕过 WAF(Web应用防火墙),或者破坏 socket 状态,从而污染其他用户的连接。
修复方案
只需要在检查前对头部值进行规范化处理:.map((e) => e.trim().toLowerCase())。
参考资料
- GHSA-mp2g-9vg9-f4cg
- github.com/h3js/h3/rel…
- nvd.nist.gov/vuln/detail…
- h3js/h3@618ccf4 glyoVzOLZA9nMhz/bDHDAWzfRfZ0dSZtQUalpUyOmxeKzxLDxDAPGK3S/tnJpm1L8YfZld/9xHEAZY5t2OAyDQ==