Junior Hacking Talent - Kid Protocol Writeup

Looking at the source code, we can see the basic processing of each packet

class KidPacket:
    def __init__(self, rawPacket):
        self.parse(rawPacket)

    def parse(self, rawPacket):
        self.contentLength = struct.unpack("<H", rawPacket[0:2])[0]
        data = rawPacket[2:]

        if self.contentLength != len(data):
            raise Exception("Bad content-length")

        try:
            self.body = json.loads(data)
        except:
            raise Exception("Bad format")

def getValue(object, key):
    try:
        return object[key]
    except KeyError:
        raise Exception("Missing '%s' param" % key)


kidPacket = KidPacket(rawPacket)
action = getValue(kidPacket.body, "action")
inputFile = getValue(kidPacket.body, "file")

From this, we can figure out the format for each packet: they all starts with 2 bytes containing the length of the data packed as an unsigned short in little-endian format (<H). The rest of the packet is just JSON data which is decoded and it’s action and file key is read.

The function below decides whether we get to read the flag or not according to the JSON data inside the packet body.

fileList = ["intro.txt", "flag.txt"]

def handle(self, rawPacket):
        try:
            kidPacket = KidPacket(rawPacket)
            action = getValue(kidPacket.body, "action")
            inputFile = getValue(kidPacket.body, "file")

            if action == "read":
                if inputFile in fileList:
                    file = inputFile
                else:
                    file = "intro.txt"

                with open(file, "r") as f:
                    return f.read()

            return "Wrong action"
        except Exception as e:
            return str(e)

Therefore, what we should have inside the body in JSON is:

{"action":"read", "file":"flag.txt"}

Once we have both the data and the format of the packet, creating and sending one should be trivial.

import socket
import struct
import json

s = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
s.connect(("167.71.204.85", 3108))

def parse(body):
    data = json.dumps(body).encode()
    length = len(data)
    content_length = struct.pack("<H", length)
    return content_length + data

body = {"action": "read", "file": "flag.txt"}
packet = parse(body)
s.send(packet)
print(s.recvfrom(4096))

This script should, in theory, send an UDP packet to the server that will give us the flag.

❯ python solve.py
(b' _______          __                       __   .__                \n \\
\\   _____/  |___  _  _____________|  | _|__| ____    ____  \n /   |   \\_/ __ \
\   __\\ \\/ \\/ /  _ \\_  __ \\  |/ /  |/    \\  / ___\\ \n/    |    \\  ___/|
|  \\     (  <_> )  | \\/    <|  |   |  \\/ /_/  >\n\\____|__  /\\___  >__|   \\
/\\_/ \\____/|__|  |__|_ \\__|___|  /\\___  / \n        \\/     \\/
\\/       \\//_____/  \n           ___________.__
\n           \\_   _____/|  | _____     ____                          \n
|    __)  |  | \\__  \\   / ___\\                         \n            |     \\
|  |__/ __ \\_/ /_/  >                        \n            \\___  /   |____(___
_  /\\___  /                         \n                \\/              \\//____
_/                          \n
\n\nCTF{master_networking_fcbfce971dae5a7271deb6ffbc972b16}\n', ('167.71.204.85'
, 3108))