CVE-2026-113431
Unbounded decoded HTTP/2 header-list size in cowlib cow_http2_machine enables HPACK amplification memory exhaustion
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.7
Unbounded decoded HTTP/2 header-list size in cowlib cow_http2_machine enables HPACK amplification memory exhaustion
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
- Record
CVE-2026-113431- CVSS 4.0
- 8.7 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-06
EUVDEUVD-2026-118102
8.7
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
- Record
EUVD-2026-118102- CVSS 4.0
- 8.7 High
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 - Updated
- 2026-11-06
GitHub AdvisoriesGHSA-9jxc-cwfw-gpg8
8.7
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an...
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
- Record
GHSA-9jxc-cwfw-gpg8- CVSS 4.0
- 8.7 High
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-770
- Updated
- 2026-11-06
NVD
8.7
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
- Record
CVE-2026-113431- CVSS 4.0
- 8.7 High
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-770
- Updated
- 2026-11-06
OSV
8.7
Unbounded decoded HTTP/2 header-list size in cowlib cow_http2_machine enables HPACK amplification memory exhaustion
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
- Record
CVE-2026-113431- CVSS 4.0
- 8.7 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-08
OSVEEF-CVE-2026-113431
8.7
Unbounded decoded HTTP/2 header-list size in cowlib cow_http2_machine enables HPACK amplification memory exhaustion
Summary
Allocation of Resources Without Limits or Throttling vulnerability in ninenines cowlib allows an unauthenticated remote peer to exhaust the memory of an HTTP/2 endpoint via HPACK-indexed header fields.
cow_http2_machine:headers_decode/4 in src/cow_http2_machine.erl decodes every HEADERS block with cow_hpack:decode/3. It bounds the compressed block size and the number of fields, but not the size of the decoded header list that RFC 9113 section 6.5.2 places under SETTINGS_MAX_HEADER_LIST_SIZE. A peer stores one header value of up to the dynamic table size (4096 bytes by default) and then references it with one-byte indexed fields, so about 100 bytes on the wire decode to a header list of about 390 KB. A consumer that copies the decoded values, as Cowboy does when cowboy_http2:headers_to_map/2 joins repeated cookie fields into one binary, allocates that amount for every stream. The total across streams and connections is not bounded.
This issue affects cowlib: from 2.7.0 onward.
Details
1. The decoder bounds everything but the decoded size. cow_http2_machine:headers_decode/4 passes each complete header block to cow_hpack:decode/3. Two limits apply: max_fragmented_header_block_size (32768 bytes by default) on the compressed block, and max_headers (100 by default, added in cowlib 2.17.0) on the number of fields. max_header_list_size appears in the module's option list with a default of infinity, next to a @todo comment, and is neither advertised in SETTINGS nor enforced on the decoded list, which is what RFC 9113 section 6.5.2 defines it for.
2. One-byte fields expand to kilobytes. The dynamic table holds entries of up to max_decode_table_size (4096 bytes by default). The peer sends one literal field with incremental indexing carrying a 4000-byte value, which becomes table entry 62. Every later field is the single byte 0xBE, an indexed representation of that entry. A 110-byte block that carries four pseudo-headers and 96 such references decodes to a 100-field list whose values total 384,000 bytes. Before 2.17.0 no field count applied, so a 32 KB block referenced the entry about 32,000 times.
3. The allocation is realised by the consumer. The list that cow_hpack returns is cheap, about 2 KB of heap, because every reference shares the one table binary. Cowboy's cowboy_http2:headers_to_map/2 joins repeated fields into a single new binary ("; " for cookie, ", " for other names), so each stream yields a 384,190-byte cookie value that lives in the request process for the life of the stream. Cowboy's HTTP/1.1 limits max_header_value_length and max_headers do not apply to HTTP/2. With the default max_concurrent_streams of 100 a connection holds about 38 MB, and only the Ranch connection limit bounds the total. Gun hands the decoded list to the owner process without copying the values, so the client side sees a small list per response and no amplification.
4. Measurement. On cowboy 2.20.0 with cowlib 2.21.0 on Erlang/OTP 29, one h2c connection on loopback sent 15,943 bytes of HEADERS for 100 streams. Binary memory grew by 38.4 MB and every handler received a 384,190-byte cookie header. A control with the same 100 streams and no indexed fields grew by 0.
Proof of concept
- Start a Cowboy clear listener with default protocol options and a handler that takes a few seconds to reply, so streams stay open.
- Open one TCP connection, send the HTTP/2 client preface and an empty SETTINGS frame.
- On stream 1 send a HEADERS frame with
END_HEADERSandEND_STREAMwhose block contains:method,:scheme,:pathand:authority, then one literalcookiefield with incremental indexing carrying a 4000-byte value, then 95 indexed fields0xBEreferencing it. - On streams 3 through 199 send the same frame with the four pseudo-headers followed by 96 indexed fields
0xBE. Each block is 110 bytes. - Watch
erlang:memory(binary)on the server. It grows…
- Record
EEF-CVE-2026-113431- CVSS 4.0
- 8.7 High
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 - Weakness
- CWE-770
- Updated
- 2026-11-06
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
cowlibHex |
2.7.0 and later, plus listed versions 2.10.02.10.12.11.02.12.02.12.1 Show 17 moreShow less2.13.02.14.02.15.02.16.02.16.12.17.02.17.12.18.02.19.02.20.02.21.02.7.12.7.22.7.32.8.02.9.02.9.1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| references | OSV | |
| references | OSV | |
| published | EUVD | |
| published | EUVD, NVD | |
| aliases, references | GitHub Advisories | |
| references, severity | CVE List | |
| aliases, description, products, published, references, severity | EUVD | |
| first indexed | OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-113431 is a high vulnerability (CVSS 8.7) in Ninenines Cowlib, published November 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.42%, the 36th percentile. No source names a fixed version.
References
cna.erlef.org/cves/CVE-2026-113431.html
osv.dev/vulnerability/EEF-CVE-2026-113431
github.com/ninenines/cowlib/commit/b461b119e78e4e09bb28b186b09da7ed4a86a0dd
github.com/ninenines/cowlib/commit/a3b15e29b3067644bfd580246c87f7af7a90b3b8
nvd.nist.gov/vuln/detail/CVE-2026-113431
github.com/advisories/GHSA-9jxc-cwfw-gpg8
Left out here: the rest of 1 source description. The JSON record has them.