Skip to content

Instantly share code, notes, and snippets.

@Spirotot
Last active November 2, 2017 09:23
Show Gist options
  • Save Spirotot/be621accc8b2f6dc75e5f5eb3e693ec6 to your computer and use it in GitHub Desktop.
Save Spirotot/be621accc8b2f6dc75e5f5eb3e693ec6 to your computer and use it in GitHub Desktop.
Flare-On 2016 Challenge 1 Angr Solve
#!/usr/bin/env python2
import angr
angr.path_group.l.setLevel('DEBUG')
def writefile_hook(state):
print('writefile hook')
# Just return "success"
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
state.regs.eax = 1
def readfile_hook(state):
print('readfile hook')
# Just return "success"
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
state.regs.eax = 1
def buffer_hook(state):
print('buffer hook')
# Store the symbolic address of our symbolic 'user_input' buffer in
# eax, which is where the real 'user_input' buffer would be if the program
# was _actually_ executing.
# http://angr.io/api-doc/simuvex.html?highlight=store#simuvex.storage.memory.SimMemory.store
state.memory.store(state.regs.eax, user_input)
# print state.memory.load(state.regs.eax)
def malloc_hook(state):
print('malloc_hook')
# Just return a concrete value.
# I think I picked this mostly at random, and it worked.
# Thankfully, malloc is only called once in `challenge1.exe`,
# otherwise we might have to do something fancier here.
state.regs.eax = 0xC0000000
# Load the project. Don't load libraries, for multiple reasons:
# * Speed: https://docs.angr.io/docs/speed.html
# * CLE, the symbolic loader, doesn't support
# Windows binaries super well: https://github.com/angr/cle
p = angr.Project('challenge1.exe', load_options={'auto_load_libs': False})
# Need to hook WriteFile, since we're not loading any libraries.
# Even if we were, we wouldn't want to make Angr symbolically execute
# all that anyway...
p.hook(0x401457, writefile_hook, length=6)
# Need to hook ReadFile, since we're not loading any libraries.
# Even if we were, we wouldn't want to make Angr symbolically execute
# all that anyway...
p.hook(0x401473, readfile_hook, length=6)
# Need to hook the location where the pointer to the user's input is
# passed as an argument to the encoding function. Our hook will
# replace whatever is there with a pointer to our symbolic
# 'user_input' buffer.
p.hook(0x401480, buffer_hook, length=6)
# Need to hook malloc, mostly for speed reasons -- without it,
# I'm not sure if this would ever complete.
p.hook(0x401283, malloc_hook, length=5)
# Note: For all hooks, the first argument is the address of the
# instruction to begin the hook at. The second argument is the
# hook function, which gets executed when the address (first
# argument) is hit. The length is the number of bytes to hook.
# I've set it to the length of the instruction at that address.
# I'm frankly not sure what happens if you don't set the length.
# To get the length, I just set the 'number of opcode bytes (graph)'
# option under IDA's General settings to 8. Whe you're in graph mode,
# IDA will display the instruction's bytes. Count them to get the
# length of the instruction.
# Note the starting `addr` -- had to bypass the initialization and loading
# stuff because it caused Angr errors...
initial_state = p.factory.blank_state(addr=0x40143C)
# Create our symbolic user_input buffer, which will use when we make Z3 solve
# for the correct password! The length 0x80 was chosen because I didn't expect
# the password to be super short (i.e. less than 10 chars or something --
# that'd be too easy to brute force), but I also didn't expect it to be soo
# long that someone wouldn't be able to type it in a reasonable amount of time.
user_input = initial_state.se.BVS("user_input", 8 * 0x80)
# Make an initial path from our state...
initial_path = p.factory.path(initial_state)
# Create a path group from that path...
path_group = p.factory.path_group(initial_state)
# Tell Angr where to go, and where to avoid (one is "Correct!", the
# other is "Wrong password").
path_group.explore(find=(0x4014AE,), avoid=(0x4014C7,))
# Once the "Correct!" path is found...
found = path_group.found[0]
# Print the flag!
print('FLAG: {0}'.format(found.state.se.any_str(user_input)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment