Whilst working on angr_ctf in order to properly dive into Angr, there was one exercise which required the use of a symbolic filesystem with SimFile backed by symbolic memory. This particular challenge requires a particular input to be present in the input file and as such act as the password. The filename can be quickly looked up in the binary; the contents however will be made symbolic so we can solve for that.

The provided scaffold was along the lines of:

#!/usr/bin/env python3

import angr
import claripy

project = angr.Project('07_angr_symbolic_file')
initial_state = project.factory.blank_state(addr=0x80488db)

filename = 'INYXFAJA.txt'
symbolic_file_size_bytes = 0x40

symbolic_file_backing_memory = angr.state_plugins.SimSymbolicMemory()
symbolic_file_backing_memory.set_state(initial_state)

password = claripy.BVS('password', symbolic_file_size_bytes * 8)
symbolic_file_backing_memory.store(0 , password)

file_options = 'r'
password_file = angr.storage.SimFile(filename, file_options,
	content=symbolic_file_backing_memory, size=symbolic_file_size_bytes)

symbolic_filesystem = { filename : password_file  }

initial_state.posix.fs = symbolic_filesystem
simulation = project.factory.simgr(initial_state)

While I know this CTF is not too new and is using Python2 still, so far I the tweaks required to get it working with Python 3 were minimal. However the error when running the code above with a fresly installed version of Angr (9.0.5171 at the time of writing) signalled this was a less trivial issue:

AttributeError: module 'angr.state_plugins' has no attribute 'SimSymbolicMemory'

As it turns out, the entire SimSymbolicMemory class was removed without so much as a migration path that’s easily found like what was done for angr 8 and angr 7 before that1. Now, Angr is a rapidly evolving project (that’s a good thing!) so lacking a migration guide, I was hoping to at least find a clue in the changelog. Alas, the changelog at the time of writing doesn’t cover anything beyond angr 8.19.7.25 and repository doesn’t contain anything resembling a changelog.

Obviously I wasn’t the first to run into this particular issue with SimSymbolicMemory and someone filed an issue upstream, but I wanted to provide a working example. If nothing else, it’s a reference for future-me. The referenced issue states that SimSymbolicMemory was removed and instead we can provide the contents, a Claripy bitvector in this case, directly. Also the second argument when instantiating a SimFile class no longer represents a file mode2. Furthermore we can skip the entire symbolic_filesystem construction and instead pass it when setting up the initial state.

Putting everything together the complete solution3 for 07_angr_symbolic_file that works with Angr 9 becomes:

import angr
import claripy

def main(argv):
  project = angr.Project('07_angr_symbolic_file')
  start_address = 0x080488f4

  filename = 'INYXFAJA.txt'
  
  # This is passed to fread() at 0x08048925 as nmemb
  # 0x080488f4      6a40           push 0x40
  symbolic_file_size_bytes = 0x40

  password = claripy.BVS('password', symbolic_file_size_bytes * 8)

  password_file = angr.SimFile(filename,
          content = password,
          size = symbolic_file_size_bytes)

  initial_state = project.factory.blank_state(
          addr = start_address,
          fs = {filename: password_file}
  )

  simulation = project.factory.simgr(initial_state)

  def is_successful(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return b'Good Job.' in stdout_output

  def should_abort(state):
    stdout_output = state.posix.dumps(sys.stdout.fileno())
    return b'Try again.' in stdout_output

  simulation.explore(find=is_successful, avoid=should_abort)

  if simulation.found:
    solution_state = simulation.found[0]

    solution = solution_state.solver.eval(password,cast_to=bytes).decode()
    print(solution)
  else:
    raise Exception('Could not find the solution')

if __name__ == '__main__':
  main()

  1. or maybe there is and I just haven’t been able to find it in angr-doc. ↩︎

  2. the API documention is very much up-to-date! ↩︎

  3. until the next backwards incompatible change without an upgrade path or updated documentation that is. ↩︎