osbytes

Search

Find posts, projects, and members.

← back to blog

HTTP/2 Bomb chains HPACK refs and a zero-byte window to pin server memory

2026-06-03by@osbytes7 min read
#security #http2 #nginx #apache #cve #coordinated-disclosure #denial-of-service

TL;DR

  • The oss-security thread for HTTP/2 Bomb (CVE-2026-49975) posted early today, pointing at Calif's writeup and PoC labs. Default HTTP/2 configs on nginx, Apache httpd, Microsoft IIS, Envoy, and Cloudflare Pingora are in scope.
  • Attack shape: seed HPACK with a tiny header, spam 1-byte indexed references (wire cheap, server allocates per ref), split Cookie into crumbs where servers do not count them, then advertise a zero-byte flow-control window and drip WINDOW_UPDATE frames so the server never finishes the stream and never frees the allocations.
  • Measured demos in the disclosure: roughly 32 GB in ~18–20s on Apache httpd and Envoy from one client; nginx and IIS also climb into tens of gigabytes on a home broadband link.
  • nginx 1.29.8+ ships max_headers (default 1000). Apache fixed cookie accounting in mod_http2 2.0.41 (commit 47d3100). IIS and Pingora had no public patch at disclosure; the Calif post notes Envoy patches on June 3 that researchers are still validating.
  • If you cannot patch tonight: http2 off; on nginx, Protocols http/1.1 on Apache, or terminate HTTP/2 at a front door that hard-caps header field count (not just decoded byte size).

Two old tricks, one pinned allocation

Both halves of this exploit have names you may already recognize. HPACK bombs go back to Cory Benfield's CVE-2016-6581 work in 2016: one byte on the wire can expand into a full header allocation when the receiver materializes indexed entries. HTTP/2 Slowloris-style stalls showed up in the same era (CVE-2016-8740, CVE-2016-1546): abuse flow control or continuation handling so the server keeps state without completing the request. Gal Bar Nahum pushed Apache-specific HPACK amplification again last year as CVE-2025-53020 (fixed in 2.4.64).

What changed today is the composition and the bypass paths still sitting in default configs. Calif's team (discovery credited to Quang Luong, cross-server confirmation by Jun Rong and Duc Phan) chains compression amplification with a window stall so memory stays allocated until the server's send timeout gives up. A 70:1 amplifier is annoying; a 70:1 amplifier you cannot release for minutes is an outage.

The writeup also flags a spec blind spot worth internalizing. RFC 7541 §7.3 warns about memory exhaustion, then leans on SETTINGS_HEADER_TABLE_SIZE as the fix. That bounds the dynamic table, not the per-request bookkeeping around indexed references, cookie reassembly, or streams that never complete. Decoded header size limits and header count limits are different knobs; several servers had one without the other.

Where the amplification actually comes from

Indexed reference spam. HPACK lets a sender insert a header once, then refer to it with a single-byte index. Each reference still allocates a fresh copy on the server side when it assembles the request. Servers that only cap total decoded header bytes miss the case where the stored value is nearly empty and the cost is allocator metadata plus per-field structures.

Cookie crumbs as a field-count bypass. RFC 9113 §8.2.3 allows splitting Cookie into multiple header fields ("crumbs"). Apache and Envoy cap header fields but, before the fix, did not count those crumbs against LimitRequestFields. An attacker can multiply crumbs while staying under byte limits.

The cookie merge path is where Apache's patch lands. In mod_http2 2.0.41, Stefan Eissing's change in h2_util.c sets *pwas_added = 1 when merging cookie fragments, so each crumb counts as an add against LimitRequestFields. The changelog line is blunt: "Fix cookie header accounting against LimitRequestFields." Lowering LimitRequestFieldSize still only shrinks per-stream blast radius; the disclosure stresses it does not substitute for counting crumbs.

Envoy's merge behavior appends each crumb into a buffer (high amplification on a fat cookie value). Apache httpd rebuilds the merged cookie string on every crumb, leaving older copies live until stream cleanup, which is why their table shows ~4,000:1 even on an empty cookie value.

Window stall. The client sets the stream's flow-control window to zero, so the server cannot finish sending a response, then sends tiny WINDOW_UPDATE frames to reset send timers without releasing state. That turns "request completed, memory freed" into "memory pinned until timeout," which is the difference between a spike and a sustained hold. The researchers note the nastier operational play may be holding just under OOM and pushing the box into swap rather than killing workers outright.

Patch map and fallbacks

Disclosure timeline from the writeup: nginx got a private report in April and shipped max_headers in 1.29.8 (nginx commit 3656941, default 1000). Apache got May 27 disclosure and same-day fix in standalone mod_http2 2.0.41; trunk has it, but the disclosure says a 2.4.x stable bundle is not out yet. Microsoft IIS, Envoy, and Pingora were notified without a public patch at the initial release; the June 3 update on the Calif post says Envoy shipped mitigations researchers are still testing.

Concrete steps by stack:

  • nginx before 1.29.8: upgrade, or http2 off; on affected server blocks until you can.
  • Apache httpd with mod_http2: pull 2.0.41 from the standalone module release or trunk; interim Protocols http/1.1 disables HTTP/2 entirely. Do not assume LimitRequestFields alone helps until the cookie accounting fix is in.
  • IIS / Pingora / unpatched Envoy: disable HTTP/2 at the edge if you control it, or put a reverse proxy in front that enforces a hard per-request header field cap including cookie crumbs. Partial byte limits alone are the bypass the writeup demonstrates.
  • Last-resort containment: per-worker memory caps (cgroups, ulimit -v, container limits) tight enough that one bombed worker dies and respawns before the host enters swap. The disclosure frames that as better than letting an attacker hold the whole machine at 95% RAM.

Calif published per-server Docker labs and PoC scripts in a linked repo; treat them as owner-only tooling.

Why the disclosure landed now

Maintainers for nginx and Apache already had fix commits public. The oss-security post quotes the researchers: those diffs expose the vectors clearly enough that "any capable AI model can turn those diffs into a working exploit," which is how they extended the same class to IIS, Envoy, and Pingora after the Apache fix was readable. Whether or not you care about the Codex discovery story, the operational trigger is the same: public fix commits shortened the attacker runway, so the coordinated release pushed mitigations to operators who still terminate HTTP/2 on defaults.

If you run HTTP/2 on the origin (not only at a CDN), today is a good day to inventory who counts header fields, who only caps decoded bytes, and whether cookie crumbs participate in that count.

Sources