angr Template

What is it

This template finds the flag character by character automatically. It works if the flag is passed to a series of functions for validation check. For example, the program contains hundreds of functions that check whether the password that the user gives is valid or not.

This template can be used to solve:

  • Google CTF 2020 "beginner"

  • BambooFox CTF 2021 "Flag Checker Revenge"

  • DiceCTF 2021 "babymix"

  • ... and many others

How to Use

  1. Examine the binary manual and guess the flag length. Usually the clue would be the strlen function call or the upper bound of some for loop.

  2. Upon success or failure, the binary may execute the call puts instruction and prints a string. Find the address of call puts for the successful cases and the address of call puts for failed cases. They should be filled in as the find_addr and avoid_addr respectively.

  3. Run the script and wait patiently. It usually takes 5+ mins to complete.

Template

Change the lines containing < and >:

#!/usr/bin/env python3
import angr
import claripy

FLAG_LEN = <len>
STDIN_FD = 0

base_addr = 0x100000 # To match addresses to Ghidra

proj = angr.Project("<binary_name>", main_opts={'base_addr': base_addr}) 

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(FLAG_LEN)]
flag = claripy.Concat( *flag_chars + [claripy.BVV(b'\n')]) # Add \n for scanf() to accept the input

state = proj.factory.full_init_state(
        args=['<bin>'],
        add_options=angr.options.unicorn,
        stdin=flag,
)

# Add constraints that all characters are printable
for k in flag_chars:
    state.solver.add(k >= ord('!'))
    state.solver.add(k <= ord('~'))

simgr = proj.factory.simulation_manager(state)
find_addr  = <success_address> # The address of "call puts" for SUCCESS
avoid_addr = <failure_address> # The address of "call puts" for FAILURE
simgr.explore(find=find_addr, avoid=avoid_addr)

if (len(simgr.found) > 0):
    for found in simgr.found:
        print(found.posix.dumps(STDIN_FD))

Reference

Last updated