Skip to content

Angr

Angr is a multi architectural binary analysis framework developed by the team Shellphish from ASU. Angr is a suite of python3 and lets you load binaries and analyze it along the lines of the constraints you add to the symbolic execution state. Basically, angr is capable of performing dynamic symbolic execution and also static analysis on the binary to frame an input to the binary.

Installation

pip3 install angr

Angr for CTF’s

Very often we come across binaries that have numerous checks and conditions which are practically impossible or hard to reverse and these have to be automated somehow and that’s where angr plays an important role. Angr symbolically executes your binary and finds the input required by the binary to achieve a particular state in the binary.

The most common script that comes in handy while playing CTF’s is the find and avoid scripts where the angr solver engine is provided with a find address and an avoid address which is symbolically achieved by the solver engine.

import angr
import claripy
import sys
proj=angr.Project('./jack',load_options={'auto_load_libs':False},main_opts={'base_addr':0x400000})
flag=[claripy.BVS('flag%i'%i,8) for i in range(inp_len))] # here inpp_len refers to the length of the input which we obtain by reversing the binary
Good_address = #address we want to achieve when the required string is given as input
Bad_address =  #address we need to avoid being hit during the symbolic execution
flag_concat=claripy.Concat(*flag + [claripy.BVV("\n")])
state=proj.factory.entry_state(stdin=flag_concat) #the binary is symbolically executed from the binaries entry point
for i in flag: #Adding constraints to the solver to obtain values that are in the printable range
    state.solver.add(i>=32)
    state.solver.add(i<=127)
simgr=proj.factory.simgr(state)
simgr.explore(find=good_address,avoid=bad_address)
if simgr.found:
    simulation=simgr.found[0]
    print(simulation.posix.dumps(sys.stdin.fileno()))
else:
    print("FAILURE")

This is the most basic script which comes in handy for numerous ctf’s but most of the time you have to add more constraints to limit the characters and optimise the solver to solve your problem and reduce the time taken by the solver to find the solution.

Explanation

Creating a project

angr.Project(‘/path/to/file’)

Creates an angr project from where the binary is used for symbolic execution.

Creating Simulation

Project.factory.simgr(init_state)

Here init_state is the entry point set up from the project.

Setting up Entry Point

Init_state = project.factory.entry_state()

It fetches the binary and the entry point to the code to start execution. Here instead of entry_state(), you can start executing the binary from any point by using blank_state(addr = address_to_start)

Creating a Bitvector array to store the input array

flag=[claripy.BVS('flag%i'%i,8) for i in range(inp_len))]

Here we initialise a bitvector of the length that we expect the input to be. We can make an assumption of the length from analysing the binary statically using ghidra or any other tool. This is not necessarily required, you can also brute force the input without this too.

Exploring the Binary

simgr.explore(find=good_address,avoid=bad_address)

Here we explore the binary to hit a particular address in the binary that we want to hit and also avoid a particular address in the process of execution of the binary. This can also be a list of addresses. Finally angr finds an input that satisfies the conditions mentioned by the binary and also the conditions that you add to the simulation manager.

You are also free to add options to angr to reduce the time taken to find the solution, like for example when there is emulation of some sort involved, using options for enabling unicorn can be helpful.

Resources

Angr docs