Описание
AMQP 1.0 symbolic body descriptor prefix collisions bypass validation
Summary
RabbitMQ's optimized AMQP 1.0 server_mode parser recognizes symbolic message
body descriptors by matching a fixed textual prefix but ignores the encoded
symbol length. A remote AMQP 1.0 publisher can therefore submit a described
value whose descriptor merely begins with amqp:data:binary,
amqp:amqp-sequence:list, or amqp:amqp-value:*, and RabbitMQ classifies it
as the corresponding standard body section.
The primary proof uses the well-formed but unknown descriptor
amqp:data:binary@. Structurally inconsistent encodings with truncated or
overlong declared lengths reach the same fast path as long as the fixed prefix
is physically present. These cases should be distinguished: the primary
sample is a valid generic AMQP described value, while some other affected
lengths produce malformed encodings.
The accepted bytes are retained through classic, quorum, and stream queues and
are forwarded unchanged to AMQP 1.0 consumers. RabbitMQ's normal decoder
rejects the unknown descriptor. For a body misclassified as data, RabbitMQ
also performs a full decode during conversion to AMQP 0-9-1 and other
protocols, causing an uncontrolled parser exit in the consumer protocol
process.
This is best characterized as a body-descriptor classification bypass with protocol-specific stored poison-message impact. It does not provide a routing or authorization bypass, and it is not a classic message-smuggling primitive.
Affected revision
Confirmed against:
Confirmed with Erlang/OTP 27.
The affected release range has not been established. This report only claims the revision above.
Affected components
deps/amqp10_common/src/amqp10_binary_parser.erldeps/rabbit/src/mc_amqp.erl- AMQP 1.0 publishing
- AMQP 1.0 normal-mode consuming
- AMQP 1.0 to AMQP 0-9-1 and other cross-protocol conversion paths
Attack requirements
- The attacker can authenticate to RabbitMQ over AMQP 1.0
- The attacker has write permission to a target exchange or queue
- A victim consumes the stored entry using a strict AMQP 1.0 decoder or a protocol path that forces full AMQP body decoding
The attacker does not need control of the RabbitMQ node, the victim process, or the transport between RabbitMQ and the victim.
Root cause
The symbolic body fast paths bind the symbol length to _S but do not compare
it with the fixed descriptor length:
Equivalent vulnerable clauses exist for sym32.
The valid lengths are:
amqp:data:binary: 16amqp:amqp-sequence:list: 23amqp:amqp-value:*: 17
reached_body/2 returns immediately at the section start without consuming or
validating the descriptor or its value:
The affected ranges are therefore:
sym8data: every value except 16sym8sequence: every value except 23sym8value: every value except 17sym32data: every value except 16sym32sequence: every value except 23sym32value: every value except 17
The parser does not require the declared number of symbol bytes to exist. The fixed textual prefix only needs to be physically present in the transfer payload.
Lengths other than the exact registered descriptor length are not semantically equivalent. A longer, fully present symbol can be a well-formed unknown descriptor with a colliding prefix. A shorter or unfulfilled declared length can instead make the generic AMQP value structurally inconsistent.
Parser differential
This well-formed generic AMQP described value:
Decoded fields:
RabbitMQ server_mode interpretation:
Normal parser interpretation:
The normal framing decoder then exits because the well-formed descriptor
<<"amqp:data:binary@">> is not registered as an AMQP message section.
Inbound and storage path
The AMQP reader parses the transfer performative and passes the remaining payload to the session as opaque message bytes.
rabbit_amqp_session:incoming_link_transfer/4 calls:
mc_amqp:init/1 invokes:
The returned body marker is used by msg_body_encoded/3 to retain the
original bare message:
mc_amqp:prepare(store, ...) changes only the record representation. The
bare_and_footer binary is not normalized or reparsed.
AMQP 1.0 delivery similarly appends bare_and_footer unchanged to the
outgoing transfer payload.
Cross-protocol failure
For bodies classified as data, mc_amqp:msg_to_sections/1 performs a full
decode before converting to another protocol:
The colliding descriptor reaches amqp10_framing0:record_for/1, which exits
because it is unknown.
This conversion path is used by AMQP 0-9-1 delivery and by several other
features that convert from mc_amqp. The protocol process can terminate
before delivering the message. With acknowledgement-required consumers, the
entry can be requeued and trigger the same failure after reconnection.
For malformed amqp-sequence and amqp-value classifications, RabbitMQ
usually transports the bytes as opaque AMQP-encoded payload instead of
decoding them during conversion.
Minimal deterministic proof of concept
After building the core broker from the repository root with
make -C deps/rabbit
run:
Expected output:
Security impact
Confirmed:
- RabbitMQ classifies the well-formed unknown descriptor
amqp:data:binary@as a standarddatabody - Classic, quorum, and stream queues preserve the colliding bytes for AMQP 1.0 delivery
- Raw AMQP 1.0 consumers receive those bytes unchanged
- Normal RabbitMQ AMQP 1.0 client sessions terminate while decoding the entry
- AMQP 0-9-1 consumption fails during broker-side conversion on classic and quorum queues
- Acknowledgement-required consumption can repeatedly redeliver the stored poison entry
- The RabbitMQ node remains alive after the demonstrated consumer failures
Routing-sensitive metadata is parsed before the body marker. Bytes following the body marker are not used to select the target exchange, routing key, or authorization decision.
Severity
The issue crosses a tenant boundary when an authorized publisher can place a message into a queue consumed by another principal, but the direct impact is availability of consumer sessions or channels rather than compromise of the RabbitMQ node.
The availability effect is not unique to symbolic prefix collisions:
server_mode also stops before validating the encoded value of a canonical
body descriptor. The prefix-collision defect should therefore be fixed, but
the broader body-validation boundary should be considered at the same time.
The immediate six-clause fix prevents symbolic prefix collisions, but it does not remove the stored poison-message primitive. Numeric body-descriptor fast paths also stop before validating the body value. Ingress body validation or controlled conversion errors are required to address that broader availability boundary.
Recommended remediation
For the immediate defect:
- Require exact symbolic descriptor lengths in all six fast-path clauses
- Use 16 for data, 23 for sequence, and 17 for value
- Fall through to normal parsing when the length is not exact
- Return a controlled AMQP decode error instead of accepting the body marker
For defense in depth:
- Validate the first body descriptor and encoded body value at ingress
- Validate legal message section ordering through the end of the payload
- Catch parser and framing exits at protocol conversion boundaries
- Reject or dead-letter malformed stored entries without terminating consumer protocol processes
Regression tests
Add coverage for:
sym8lengths0, expected minus one, expected plus one, and255sym32lengths0, expected minus one, expected plus one, and16#ffffffff- Well-formed unknown descriptors that extend each registered descriptor prefix
- Structurally inconsistent truncated and unfulfilled overlong symbol encodings
- Data, sequence, and value symbolic descriptors
- Classic, quorum, and stream queue persistence
- AMQP 1.0 normal and raw consumers
- AMQP 0-9-1 conversion with acknowledgement-required consumption
Пакеты
rabbitmq
>= 4.3.0, < 4.3.4
4.3.4
rabbitmq
>= 4.2.0, < 4.2.10
4.2.10
rabbitmq
>= 4.1.0, < 4.1.15
4.1.15
rabbitmq
>= 4.0.0, < 4.0.24
4.0.24