针对HTTP/3服务器的Qpack放大攻击
TLDR:一个远程的、未经身份验证的客户端可以迫使HTTP/3服务器分配比它发送的多出数十到数千倍的内存。通过一个连接就足以造成内存溢出或锁定默认配置的服务器。测试的十个实现中有七个存在漏洞;其中几个已经修复。QPACK压缩HTTP/3头部以最小化通过网络发送的流量。在受影响的实现中,解码头部的限制缺失、不完整或应用得太晚。因此,一个小的HEADERS帧可以在内存中扩展为数十万个头部对象。为了防止这种扩展耗尽资源,必须在字段被解码和保留时执行累积预算的强制。修复版本截至2026年10月20日: 库: - quic-go 0.57.0 CVE-2025-64702 - Cloudflare quiche 0.29.3 CVE-2026-12523 - lsquic 4.9.0 无CVE - Wireshark 4.6.2 / 4.4.12 CVE-2025-13945 未修复版本截至2026年10月20日: 库: - Bun 1.4.3 - h3 (hyperium) - aioquic / mitmproxy 缓解措施: - Bun:不要暴露HTTP/3服务器 - mitmproxy:禁用HTTP/3 (--set http3=false) - h3 crate:设置合理的max_field_section_size 未受影响:IIS, Envoy, ngtcp2/nghttp3 背景:在对我们的活动TLS扫描器JA4Scan的不同QUIC堆栈进行最近研究时,我发现多个HTTP/3实现中存在相同类别的漏洞。一个远程的、未经身份验证的客户端可以发送HTTP/3流量,使服务器分配的内存远远超过编码请求在网络上占用的内存。这项调查是在阅读Gal Bar Nahum关于HTTP/2请求取消拒绝服务(DoS)的MadeYouReset系列后开始的。这项工作让我想看看HTTP/3的QPACK头部压缩是否存在类似的放大问题。HTTP/3使用QPACK压缩头部,因此像用户代理或内容类型这样的重复字段可以作为小索引发送,而不是完整字符串。它使用两个查找表:一个静态表,这是规范定义的常见字段的硬编码集合,另一个是动态表,在客户端和服务器之间运行时构建,随着新的头部的出现而增加。例如,一个71字节的头部如strict-transport-security: max-age=31536000; includesubdomains; preload变成一个字节:0xFA,静态表对第58个索引的引用。RFC 9114第4.2.2节定义了服务器应如何限制解码头部的大小:字段列表的大小是基于字段的未压缩大小计算的,包括名称和数值的字节长度以及每个字段32字节的开销。测试的一些实现至少在其中一个方面出错了: - 它们检查压缩帧的大小,而不是解码后的大小 - 它们在检查之前解压缩所有内容 - 它们在大小计算中省略了32字节的开销 - 当设置未配置时默认为无限制 - 即使配置了,也从不强制执行该设置 它是如何工作的?目标是最大化单个HEADERS帧内部的头部数量。对于这个变体,我使用唯一名称在接收的头部映射中创建不同的条目:从a到z,从aa到zz,然后是三个和四个字符名称,直到编码大小预算用完为止。676个可能的双字母名称,但我们需要跳过te,因为该头部具有特殊的值要求。一个1,000,000字节的HEADERS帧以这种方式可以容纳约160,000个唯一头部。服务器处理这160,000+个头部的方式就是漏洞所在。示例请求流在quic-go上。手风琴也可以在另一个方向上工作。动态表变体通过编码器流插入一个大的条目(约4 KiB),然后使用1字节的索引引用对其进行数千次引用。每个引用迫使重新分配和复制整个条目。这就是lsquic和Bun攻击的工作原理:每个1字节的引用都会具体化整个条目,比例为4064:1。实现: 实现 是否存在漏洞 CVE 内存到线 quic-go 是 CVE-2025-64702 ~15倍留存堆 Wireshark 是 CVE-2025-13945 崩溃 QPACK解析器无界解码 Cloudflare quiche 是 CVE-2026-12523 ~20倍 缺失32字节开销 + 帧预分配炸弹 lsquic 是 修复了,无CVE ~600-1800倍 无累积解码限制,无限制HEADERS块 aioquic / mitmproxy 是 - ~20-30倍 MAX_FIELD_SECTION_SIZE被解析但从未强制执行 Bun 是 - ~4000倍 用无限制替换lsquic的64K上限 h3 (hyperium) 是 已报告,未修复 线程恐慌 HeaderMap::append在32,768个条目时恐慌 Google quiche 否 - - 解码时强制60K上限,违反时流重置 IIS (http.sys) 否 - - 增量强制,4K QPACK表,H3_EXCESSIVE_LOAD gtcp2 / nghttp3 否 已报告,文档已更新 - 动态表默认值0,立即减少;max_field_section_size仅用作信号 quic-go:quic-go是Caddy、Traefik和cloudflared背后的Go库。它的HTTP/3服务器将HEADERS帧限制为1 MiB(DefaultMaxHeaderBytes),但该限制适用于压缩的QPACK有效负载,而不是解码后的输出。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡