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:
- http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/UsbInfo
- http://euc.jp/periphs/xbox-controller.ja.html
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
tjctf{1_h4Te_cUr1y_bR4c3s}