Post

N0PSctf 2024 - Writeups

This is a writeup for all forensics challenges from N0PSctf 2024. Initially, I did not want to create a writeup for this CTF because of time constraints. But I still did it anyways as I found the forensics challenges to be very interesting and can be referenced in the future.

HID [Forensics]

Question: what did I write ???

Flag: N0PS{m0Us3_dR4w1Ng}

We are given a pcap file to investigate. Analyzing it, it was obviously a USB device that was captured in the pcap. Looking at the GET DESCRIPTOR Response STRING, we can see that the HID data probably belonged to a USB gaming mouse.

usb1

Scrolling further down, we can see that majority of URB_INTERRUPT in packets have either length 64 or 71.

usb2

So by filtering with usb.src == "3.11.1" && frame.len == 71 and exporting the whole pcap to a JSON file, we can easily get all the HID data. However, at this point I was stuck as I was still new at USB forensics.

usb3

Reading writeups on it, there were many ways to plot the mouse movements into images. Here are some good references on plotting mouse movements:

  1. CrewCTF 2022
  2. 7h4nd5RG0d

This was my method after the CTF.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import itertools
import json
import turtle

def main():
    # Load the packet data from the JSON file
    with open('data.json') as packets_export_file:
        packets = json.load(packets_export_file)

    # Extract the x and y offsets from the packet data
    x_offsets = [int(p["_source"]["layers"]["usbhid.data_tree"]["usbhid.data.axis.x"]) for p in packets]
    y_offsets = [-int(p["_source"]["layers"]["usbhid.data_tree"]["usbhid.data.axis.y"]) for p in packets]

    # Calculate the cumulative positions
    x_positions = list(itertools.accumulate(x_offsets))
    y_positions = list(itertools.accumulate(y_offsets))

    # Determine the range of x and y positions for setting up the screen
    min_x, max_x = min(x_positions), max(x_positions)
    min_y, max_y = min(y_positions), max(y_positions)

    # Set up the turtle screen with dynamic resolution
    screen = turtle.Screen()
    screen.title("Mouse Position Tracker")
    screen.setup(width=max_x - min_x + 100, height=max_y - min_y + 100)
    screen.screensize(canvwidth=max_x - min_x + 100, canvheight=max_y - min_y + 100)
    screen.setworldcoordinates(min_x - 50, min_y - 50, max_x + 50, max_y + 50)
    
    # Create a turtle to draw the positions
    pen = turtle.Turtle()
    pen.penup()
    pen.goto(x_positions[0], y_positions[0])
    pen.pendown()

    # Draw the mouse positions
    for x, y in zip(x_positions, y_positions):
        pen.goto(x, y)

    # Keep the window open until clicked
    screen.mainloop()

main()

usb4

Another cool method from @hex01e where the flag can be easily seen in the canvas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from PIL import Image
import json

image = Image.new('RGB', (1350, 300))

j = json.load(open("exported.json"))
x,y= 280,180
for p in j:
    t = p["_source"]["layers"]["usbhid.data_tree"]
    if t["usbhid.data.report_id"] == "0x01":
        x += int(t["usbhid.data.axis.x"])
        y += int(t["usbhid.data.axis.y"])
        if p["_source"]["layers"]["usbhid.data"][3:5] == "01":
            image.putpixel((x,y),255)
image.save("flag.png","PNG")

flag

ZipZip [Forensics]

Question: zipzipzipzipzipzip

Flag: N0PS{z1p_z1p_z1p_z1p}

We are given a zip file to investigate. Inside the zip file was a file called 4ad9edde81b5526dcd95747a96a90583 which was useless. Given some hints from the author, it seems that another hidden file was embedded within the zip file. However, I could not finish this CTF on time, but a writeup from 7h4nd5RG0d pretty much sums it all up. Basically, we can just extract the hidden file’s data after its file name and deflate it with a simple script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import zlib

# Hex string of the compressed data
compressed_hex_str = "F3330808AEAE322C8847C2B500"

# Convert the hex string to bytes
compressed_bytes = bytes.fromhex(compressed_hex_str)

# Decompress the data using raw DEFLATE compression
try:
    uncompressed_bytes = zlib.decompress(compressed_bytes, -zlib.MAX_WBITS)
    print(uncompressed_bytes)
except zlib.error as e:
    print(f"Decompression error: {e}")
1
2
└─$ python deflate.py 
b'N0PS{z1p_z1p_z1p_z1p}'
This post is licensed under CC BY 4.0 by the author.