返回资讯中心
外部精选
软件工程
#社区热议

Qpack amplification attacks against HTTP/3 servers

.checked_sub((name.len() + value.len()) as u64)

Hacker Newskozika2026/10/21 · 5 天前7 分钟阅读

以下正文同步自 Hacker News,版权归原站所有,已转换为易读排版。

quiche/src/h3/qpack/decoder.rs left = left .checked_sub((name.len() + value.len()) as u64) .ok_or(Error::HeaderListTooLarge)?;

// quiche/src/h3/qpack/decoder.rs left = left .checked_sub((name.len() + value.len()) as u64) .ok_or(Error::HeaderListTooLarge)?;

// quiche/src/h3/qpack/decoder.rs left = left .checked_sub((name.len() + value.len()) as u64) .ok_or(Error::HeaderListTooLarge)?;

With max_field_section_size = 1 MiB, the RFC allows ~32,000 headers (each costing at least 33 bytes). quiche's calculation allows ~1,000,000 one-byte headers to pass.

Second, quiche's HTTP/3 frame parser pre-allocates buffers based on the declared frame length before receiving the payload. An attacker declares a 16 MiB SETTINGS or GOAWAY frame, triggering immediate memory allocation, then never sends the data.

Fix: quiche 0.29.3 adds the 32-byte overhead and fixes frame pre-allocation.

lsquic's vulnerability is the deepest I found. Three issues compound:

header_out_dynamic_entry allocates and copies per reference with no cumulative decoded-size limit

es_max_header_list_size defaults to 0 (unlimited) and is never passed to the QPACK decoder

The 64 KiB header cap resets per HEADERS block, not per stream, and lsquic accepts unlimited HEADERS blocks before DATA on a single stream

Dynamic-table references produce greater amplification in this implementation:

Insert one ~4 KiB entry via the encoder stream.

On each request stream, send a chain of HEADERS frames, each containing 15 one-byte indexed references.

Less than 10 MiB of wire traffic and 50 connections manage to pin the resident memory at the 5 GiB cap within a second, with cgroup memory.events recording 900+ limit hits. The PoC defaults (10 connections, 256 MiB container) show the same effect at a smaller scale.

Fix: fixed in lsquic 4.9.0 (July 15, 2026), which adds es_max_header_sets (server default 1) to cap the decoded header sets buffered per stream and suspend HTTP/3 parsing until the application drains them.

During testing, I also observed production servers, including www.litespeedtech.com, advertising QPACK_MAX_TABLE_CAPACITY=4096 and MAX_HEADER_LIST_SIZE=65536.

Bun's HTTP/3 support uses lsquic under the hood but replaces lsquic's built-in header-set callbacks (which cap at 64 KiB cumulative) with its own us_quic_hsi_prepare/process in quic.c, which check per-header size (64 KiB) but never cumulative. The es_max_header_list_size=16384 is advertised in SETTINGS but never enforced.

Same dynamic table trick: 1 connection, 100 streams, up to 500K one-byte references per stream to a 4 KiB entry. Only 60K references per stream (5.8 MiB of wire in total) are enough to OOM-kill a 2 GiB container.

Bun has a second bug in the same function that turns the amplification into a permanent denial of service. us_quic_hsi_prepare tracks buffer capacity in an unsigned int and grows it by doubling:

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →