{"id":"EEF-CVE-2026-109584","summary":"HTTP/2 cookie crumb join expands a small HPACK header block into an unbounded allocation in Bandit","details":"## Summary\n\nImproper Handling of Highly Compressed Data (Data Amplification) vulnerability in the HTTP/2 implementation of mtrudel bandit allows an unauthenticated remote attacker to exhaust server memory via a small HPACK-compressed HEADERS block. The block stores one large `cookie` value in the HPACK dynamic table and then references it tens of thousands of times with 1-byte indexed fields. In the reporter's run, a 49 KB block made a server with default settings allocate a 183 MB cookie binary before any application code ran, and every concurrent stream can repeat the cost.\n\n`Bandit.HTTP2.Connection` caps only the compressed block (`max_header_block_size`), and its check of the decoded list against `max_header_list_size` never fires because that setting defaults to `:infinity`. `combine_cookie_crumbs/1` in `lib/bandit/http2/stream.ex` then joins every crumb into one binary.\n\nThis issue affects bandit: from 0.3.3 before 1.14.0.\n\n## Details\n\n**1. Compression**\n\nThe attacker's header block, split across HEADERS and CONTINUATION frames, stays under the 50,000-byte `max_header_block_size`. It inserts one 4,058-byte `cookie` into the 4,096-byte dynamic table, the largest value that fits, then references it about 45,000 times with 1-byte indexed representations. `HPAX.decode` returns a list whose entries all point at the same binary, so decoding itself stays cheap.\n\n**2. Missing decoded limit** (`lib/bandit/http2/connection.ex`, `lib/bandit/http2/settings.ex`)\n\nAfter decoding, the connection compares `header_list_size(headers)` against `local_settings.max_header_list_size`. That setting defaults to `:infinity`, and in Erlang term order every integer sorts below an atom, so the comparison never rejects a list. The limit is enforced only when an operator sets it.\n\n**3. Expansion** (`lib/bandit/http2/stream.ex`)\n\n`read_headers` calls `combine_cookie_crumbs/1`, which joins every crumb with `Enum.map_join/3` into a single binary of about 183 MB before `Plug.call/2` runs. `max_concurrent_streams` also defaults to `:infinity`, so N streams allocate N times as much.\n\n## Proof of concept\n\n1. Against a Bandit server with default HTTP/2 settings, build an HPACK header block with the request pseudo-headers plus a 4,058-byte `cookie` stored in the dynamic table and then referenced about 45,000 times with indexed representations. The block is about 49 KB.\n2. Send it on one stream as a HEADERS frame followed by CONTINUATION frames of at most 16,384 bytes, with `END_HEADERS` on the last.\n3. The request reaches the plug with a `cookie` header of about 183 MB, and VM memory grows by about the same amount. Repeat on further streams to multiply it.\n\n## Impact\n\nAn unauthenticated remote client can make any Bandit server with default HTTP/2 settings allocate a cookie binary thousands of times larger than the header block it sent, before any application code runs. Repeating it across concurrent streams exhausts the BEAM node's memory, taking down every application on the node.\n\n## Workarounds\n\nOn 1.12.2 and later, set a finite decoded header list limit, for example `http_2_options: [default_local_settings: [max_header_list_size: 65_536]]`, which resets the stream before the cookie crumbs are joined. From 0.7.0, disabling HTTP/2 with `http_2_options: [enabled: false]` removes the vulnerable path.","aliases":["CVE-2026-109584","GHSA-7gfj-7gvq-3xqj"],"modified":"2026-10-26T13:56:07.045849500Z","published":"2026-10-26T13:32:22.359Z","database_specific":{"cwe_ids":["CWE-409"],"capec_ids":["CAPEC-491"],"cpe_ids":["cpe:2.3:a:mtrudel:bandit:*:*:*:*:*:*:*:*"]},"references":[{"type":"ADVISORY","url":"https://github.com/mtrudel/bandit/security/advisories/GHSA-7gfj-7gvq-3xqj"},{"type":"WEB","url":"https://cna.erlef.org/cves/CVE-2026-109584.html"},{"type":"WEB","url":"https://github.com/mtrudel/bandit/commit/44bf969cd59851622fb6ee4d16227fbc39a77fb4"},{"type":"FIX","url":"https://github.com/mtrudel/bandit/commit/220b9dfd27f9a21305be2afc75cbe585ddc24e7a"},{"type":"PACKAGE","url":"https://hex.pm/packages/bandit"}],"affected":[{"package":{"name":"bandit","ecosystem":"Hex","purl":"pkg:hex/bandit"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0.3.3"},{"fixed":"1.14.0"}]}],"versions":["0.3.3","0.3.4","0.3.5","0.3.6","0.3.7","0.3.8","0.3.9","0.4.0","0.4.1","0.4.10","0.4.2","0.4.3","0.4.4","0.4.5","0.4.6","0.4.7","0.4.8","0.4.9","0.5.0","0.5.1","0.5.10","0.5.11","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.5.9","0.6.0","0.6.1","0.6.10","0.6.11","0.6.2","0.6.3","0.6.4","0.6.5","0.6.6","0.6.7","0.6.8","0.6.9","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","1.0.0","1.0.0-pre.1","1.0.0-pre.10","1.0.0-pre.11","1.0.0-pre.12","1.0.0-pre.13","1.0.0-pre.14","1.0.0-pre.15","1.0.0-pre.16","1.0.0-pre.17","1.0.0-pre.18","1.0.0-pre.2","1.0.0-pre.3","1.0.0-pre.4","1.0.0-pre.5","1.0.0-pre.6","1.0.0-pre.7","1.0.0-pre.8","1.0.0-pre.9","1.1.0","1.1.1","1.1.2","1.1.3","1.10.0","1.10.1","1.10.2","1.10.3","1.10.4","1.11.0","1.11.1","1.12.0","1.12.1","1.12.2","1.12.3","1.12.4","1.12.5","1.13.0","1.13.1","1.2.0","1.2.1","1.2.2","1.2.3","1.3.0","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.6.0","1.6.1","1.6.10","1.6.11","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.7.0","1.8.0","1.9.0"],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-109584.json"}},{"ranges":[{"type":"GIT","repo":"https://github.com/mtrudel/bandit","events":[{"introduced":"44bf969cd59851622fb6ee4d16227fbc39a77fb4"},{"fixed":"220b9dfd27f9a21305be2afc75cbe585ddc24e7a"}]}],"versions":["1.13.1","1.13.0","1.12.5","1.12.4","1.12.3","1.12.1","1.12.0","1.11.1","1.11.0","1.10.4","1.10.3","1.10.2","1.10.1","1.10.0","1.9.0","1.8.0","1.7.0","1.6.11","1.6.10","1.6.9","1.6.8","1.6.7","1.6.6","1.6.5","1.6.4","1.6.3","1.6.2","1.6.1","1.6.0","1.5.7","1.5.6","1.5.5","1.5.4","1.5.3","1.5.2","1.5.1","1.5.0","1.4.2","1.4.1","1.4.0","1.3.0","1.2.3","1.2.0","1.1.3","1.1.2","1.1.1","1.1.0","1.0.0","1.0.0-pre.18","1.0.0-pre.17","1.0.0-pre.16","1.0.0-pre.15","1.0.0-pre.14","1.0.0-pre.13","1.0.0-pre.12","1.0.0-pre.11","1.0.0-pre.10","1.0.0-pre.9","1.0.0-pre.8","1.0.0-pre.7","1.0.0-pre.6","1.0.0-pre.5","1.0.0-pre.4","1.0.0-pre.3","1.0.0-pre.2","1.0.0-pre.1","0.7.7","0.7.6","0.7.5","0.7.4","0.7.3","0.7.2","0.7.1","0.7.0","0.6.11","0.6.10","0.6.9","0.6.8","0.6.7","0.6.6","0.6.5","0.6.4","0.6.3","0.6.2","0.6.1","0.6.0","0.5.11","0.5.10","0.5.9","0.5.8","0.5.7","0.5.6","0.5.5","0.5.4","0.5.3","0.5.2","0.5.1","0.5.0","0.4.10","0.4.9","0.4.8","0.4.7","0.4.6","0.4.5","0.4.4","0.4.3","0.4.2","0.4.1","0.4.0","0.3.9","0.3.8","0.3.7","0.3.6","0.3.5","0.3.4","0.3.3"],"database_specific":{"source":"https://cna.erlef.org/osv/EEF-CVE-2026-109584.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"}],"credits":[{"name":"Peter Ullrich","type":"REPORTER"},{"name":"Mat Trudel","type":"REMEDIATION_DEVELOPER"},{"name":"Jonatan Männchen / EEF","type":"ANALYST"}]}