01 Angr Avoid¶
One down! Time to look at 01_angr_avoid
.
Analyzing the Binary¶
As before, we are going to start with the binary in a decompiler. I had trouble opening this up in any decompiler as the file was just too large. I finally got it open in both Cutter and Ghidra by tweaking a setting. I changed the Max Instructions per Function
setting to something larger. You can find this by going to Edit > Tool Options > Decompiler
In Binary Ninja I just forced the analyze function.
As with the first one, we are looking for an eight character input.
It then loops the input and calls complex_function
on each character.
It then compares the output using a bunch of if
statements. A lot of them result in the avoid_me
function being called.
If we are able to navigate the branches we can arrive at a maybe_good
function. As shown it does a couple of more checks and then will print Good Job.
If successful.
I think this does a great job describing the need for an automated way to reverse binaries. There is no way I am going through all of those branches to find the winning password. Luckily I don't have to!
Building the Script¶
The script starts out as before, by defining the initial state and then creating a simulation manager.
path_to_binary = "./01_angr_avoid"
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state(
add_options={
angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
},
)
simulation = project.factory.simgr(initial_state)
This time, we want the simulation manager to end any paths that reach the avoid_me
function. This is important to keep the number of paths from exploding. Anytime the simulation manager reaches this point, we want it to discard the path.
In addition, we want to tell the simulation manager how to tell if its found the "good" function.
We set these and then let it go forth and pillage.
print_good_address = 0x080485E5
will_not_succeed_address = 0x080485AB
simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
That is it. On to the next one.
Final Script¶
import sys
import angr
import pwn
def run_binary(solution, path_to_binary):
if type(solution) == str:
solution = bytes(solution, "utf-8")
print(f"[+] Solution found: {solution.decode()}")
print(" [|] Running binary")
elf = pwn.ELF(path_to_binary, checksec=False)
pty = pwn.process.PTY
io = elf.process(stdin=pty, stdout=pty, level="warn")
io.recvuntil(b":")
io.sendline(solution)
output = io.recvline().decode().splitlines()[0].strip()
print(f" [+] Output: {output}")
def main():
path_to_binary = "./01_angr_avoid"
project = angr.Project(path_to_binary)
initial_state = project.factory.entry_state(
add_options={
angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY,
angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS,
},
)
simulation = project.factory.simgr(initial_state)
print_good_address = 0x080485E5
will_not_succeed_address = 0x080485AB
simulation.explore(find=print_good_address, avoid=will_not_succeed_address)
if simulation.found:
solution_state = simulation.found[0]
solution = solution_state.posix.dumps(sys.stdin.fileno()).decode()
run_binary(solution, path_to_binary)
else:
raise Exception("Could not find the solution")
if __name__ == "__main__":
main()