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.
Scrolling further down, we can see that majority of URB_INTERRUPT in
packets have either length 64 or 71.
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.
Reading writeups on it, there were many ways to plot the mouse movements into images. Here are some good references on plotting mouse movements:
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()
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")
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}'