angr

Template for Simple Crackme

Credit

Overview

This template builds 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.

For example, this template can be used to solve:

  1. Google CTF 2020 "beginner"

  2. BambooFox CTF 2021 "Flag Checker Revenge"

  3. DiceCTF 2021 "babymix"

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.

Code

#!/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("<bin>", 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_addr> # The address of "call puts" for SUCCESS
avoid_addr = <failure_addr> # 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))