Moar Turtles

TJCTF 2018 - Forensic (80 pts).

TJCTF 2018: Moar Turtles

Challenge details

Event Challenge Category Points Solves
TJCTF 2018 Moar Turtles Forensics 80 12 solves

Download: turtle.png - md5: ab0217b81c5bcd235e4e9000c1931354
flag.pcapng - md5: 2cc73f72d11d6f71617452fe4e969cee

Description

Author: Alaska47 > Hey! Check out this cool picture I drew with my XBOX 360 controller…
>Oh yeah, you might want to see this as well.

Hint: I’m a pro fortnite console player so I’ll let you in on a little secret. My controller’s deadzone is 0.2.

TL;DR

We had a PCAP with USB packets comming from XBOX 360 controller. We had to search for a description of leftoverdata, then re-draw with given data.
The hardest point was to think about velocity and find documentation about XBOX 360 controller mapping.

Methology

Extract data

First thing to do with the pcapng file was to extract the USB data and the time associated to each packet. I used the following command to extract data:

tshark -r flag.pcapng -T fields -e frame.time_relative -e usb.capdata > flag.raw

head -n 3 flag.raw

0.000000000	00:14:00:00:00:00:47:07:33:10:85:fe:63:07:00:00:00:00:00:00
0.015999000	00:14:00:00:00:00:fe:05:c7:11:85:fe:fc:06:00:00:00:00:00:00
0.028011000	00:14:00:00:00:00:b5:04:92:12:85:fe:63:07:00:00:00:00:00:00

File: flag.raw - md5: a6a74f0d3d924e62c02d89b1795ab8eb

Identifying bytes

To identify the diffents bytes, i decided first to get the number of possible value for each bytes:

f = open("flag.raw").read().split("\n")  # Array of time - data
f.pop()  # remove empty line

times = []
bytesl = [[] for x in range(20)]  # List of 20 lists

for l in f:  # for each packet
    p1,p2 = l.split("\t")
    times.append(float(p1))  # add time of pack in an array
    sub = p2.split(":")
    for i in range(20):  # add each bytes in a specific array
        bytesl[i].append(sub[i])

for i,l in enumerate(bytesl):
    print(str(i)+" : "+str(len(set(l))))  # Print nuber of values for each bytes position

OUTPUT:

0 : 1
1 : 1
2 : 1
3 : 2
4 : 1
5 : 11
6 : 256
7 : 254
8 : 208
9 : 252
10 : 3
11 : 1
12 : 3
13 : 2
14 : 1
15 : 1
16 : 1
17 : 1
18 : 1
19 : 1

Here we can see that bytes at index 6 to 9 could code the joystic moves.
To confirm our supposition, we’ve been looking for documentation:

We can see that x is coded with a signed int on bytes at index 6 and 7. Y is coded with index 8 and 9.

Scripting

Since we got an image, and the challenge is called Moar Turtles, we decided to redraw a picture from the givent packets.

Parsing packets: - x and y data are not position but movement. It’s important to set a point and change the position of this point progressivly - Draw only when A is pressed - Include velocity (time parameter)

First try we didn’t notice the velocity parameters. We got lots of junk images.

Here is the final script - md5: ba19714088c919a8b1fcca4af9a165a5

# -*- coding:utf-8 -*-
from Tkinter import *
import struct

# tshark -r flag.pcapng -T fields -e frame.time_relative -e usb.capdata > flag.raw

f = open("flag.raw").read().split("\n")  # Array of time - data
f.pop()  # remove empty line

times = []
bytesl = [[] for x in range(20)]
for l in f:  # for each packet
    p1,p2 = l.split("\t")
    times.append(float(p1))  # add time of pack in an array
    sub = p2.split(":")
    for i in range(20):  # add each bytes in a specific array
        bytesl[i].append(sub[i])

fen = Tk()  # Initializing Tkinter Canvas 1200*800
screenWidth = 1200
screeHeight = 800
screen=Canvas(fen, bg="#d0d0d0", height=screeHeight, width=screenWidth)
screen.pack(expand=YES, fill=BOTH)

cursorX = screenWidth/2  # Set cursor at the middle of the screen
cursorY = screeHeight/2  # Set cursor at the middle of the screen

for i in range(len(bytesl[0])-1): # For each packet
    diff = times[i+1]-times[i] # Get time difference between current and next packet

    v1 = bytesl[7][i]  # Get bytes of x
    v2 = bytesl[6][i]  # Get bytes of x
    v3 = bytesl[9][i]  # Get bytes of y
    v4 = bytesl[8][i]  # Get bytes of y

    # Conversion
    # signed 16-bit, little-endian, north/east positive
    # x and y in [-32768;32768]   ==> [-((256**2)/2);(256**2)/2]
    # then divide by 32768 to resize interval in [-1;1]
    x =  float(struct.unpack('<h',chr(int(v2,16))+chr(int(v1,16)))[0])/32768.0
    y =  float(struct.unpack('<h',chr(int(v4,16))+chr(int(v3,16)))[0])/32768.0

    deadzone = 0.2
    velocityWeight = 300
    for t in range(int(velocityWeight*diff)): # Make multiple movement due to velocity
        if abs(x) >= deadzone:  # Check if we're not in deadzone
            cursorX += x
        if abs(y) >= deadzone:  # Check if we're not in deadzone
            cursorY -= y  #  - due to inversion of y (first flag, the image had mirror effect)

        if bytesl[3][i] != "00": # If "A" is pressed (see documentation)
            screen.create_rectangle(cursorX-1,cursorY-1,cursorX+1,cursorY+1,fill="black",outline="black") # Write Current Pixel

fen.mainloop()

Flag

flag.png

tjctf{1_h4Te_cUr1y_bR4c3s}

Zeecka