angr-strategies
angr-strategies copied to clipboard
A cheat sheet that summarises "strategies" (or techniques) to use with angr.
angr strategies cheat sheet
This cheat sheet summarises "strategies" (or techniques) to use with angr, seen in various examples of the angr-doc repository.
Hooks
If the program is statically linked:
- (manually) identify libc calls (or calls to functions similar to libc ones; may take a long time to find them)
- add hooks on these calls
If the program calls ptrace():
- add a
ReturnUnconstrained
simulation procedure
Init state
- use angr.options.unicorn
If a program uses syscalls that should not be handled:
- use
BYPASS_UNSUPPORTED_SYSCALL
If some memory or registers bytes are known:
- concatenate a
BVV
and aBVS
(e.g.claripy.BVV(0, 56).concat(claripy.BVS('rdi', 8))
)
Input
If the program takes input from argv
:
- (manually) find the expected length of
argv
- add a non-null constraint on characters
If the program takes printable input:
- add a printable constraint on characters
If the program reads input from a file:
- (manually) find the expected length of the file
- use the filesystem feature of angr
Execution with a known goal path or output
- use
simgr.explore()
- if using
simgr.explore()
several times, usesimgr.unstash()
to move states from thefound
stash to theactive
stash - search flags in the outputs of the states of the
found
stash
"Blind" execution
- periodically trim the
deadended
anderrored
stashes (also useful for memory optimization) ... - ... and when the
active
stash is empty, search for the flag in the outputs of the states in thedeadended
stash (this means searching for the flag in the outputs of the states that have survived the longest time)
See Fish Wang's solution of TUMCTF 2016 - zwiebel.
Execution when searching for memory corruption
- use
project.factory.simgr(save_unconstrained=True)
and symbolically execute the binary until an unconstrained path is reached - use a simulation technique to find paths that lead to a
NULL
program counter
See Audrey Dutcher's demonstration of the GRUB "back to 28" bug.
Execution when searching for a shellcode injection vulnerability
- find a fully symbolic program counter in the
unconstrained
stash - search for a buffer controlled by user input, ie. a buffer which holds symbols whose names contain
file
orstdin.ident
(usestate.solver.get_variables()
andstate.memory.addrs_for_name()
) - add as constraints that the buffer's content must be equal to the shellcode, and the program counter equal to the address of this buffer
See Nick Stephens' solution of Insomnihack Simple AEG.
Optimization
If the program takes too much memory too quickly (e.g. 1 GB/s):
- add a simulation technique that checks for state uniqueness
See Audrey Dutcher's demonstration of the GRUB "back to 28" bug.
If the program takes too much memory over several (or tens) of minutes:
- periodically trim the
deadended
anderrored
stashes (also useful for "blind" execution)
If a function leads to path explosion:
- hook it with a custom SimProcedure
Use of results
If state.solver.eval(expr)
or state.solver.eval_upto(expr)
freezes:
- use
state.solver.eval()
orstate.solver.eval_upto()
several times with broken down expressions
See Yan Shoshitaishvili's solution of Whitehat CTF 2015 - Crypto 400.