2 minute read

A write-up of the “Easy Too” reverse engineering challenge from 5Charlie CTF.

Easy Too

Easy Too - Challenge

This one should be easy too! Add flag{…} around the flag you find in there!

Attachments: easy_too - Binary

Easy Too - Solution

We start out by getting an idea of what we’re diving into.

file easy_too
easy_too: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=235f50417e3e316c4bcaa7ad57949a136019865f, not stripped

We can see we’re dealing with an ARM binary, so I break out my go-to dockerized multiarch RE environment: https://github.com/cetaSYN/CTF_Dockerfiles. I’ll be using the revr-multiarch environment.

sudo docker run --rm -it -v"$(pwd):/root/" -w /root/ revr-multiarch
qemu-arm easy_too

We run the binary and are presented with a password prompt.

Enter the password: asdf
You entered: asdf
Access Denied!

I decided to use this opportunity to try out a new tool I learned about recently: angr. Angr has the ability to simulate state and intelligently modify input to find interesting or targeted outputs. In this case I don’t know exactly what output we’re looking for, but I’ll throw a quick auto-solve script at it.

#!/usr/bin/env python3
# solve.py

import angr
p = angr.Project("easy_too")
s = p.factory.entry_state(add_options=angr.options.unicorn)
sm = p.factory.simgr()

  for i
  in sm.deadended

Give it a quick run and…

python3 solve.py
WARNING | 2020-06-25 05:50:28,056 | angr.state_plugins.symbolic_memory | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior.
WARNING | 2020-06-25 05:50:28,056 | angr.state_plugins.symbolic_memory | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2020-06-25 05:50:28,057 | angr.state_plugins.symbolic_memory | 1) setting a value to the initial state
WARNING | 2020-06-25 05:50:28,057 | angr.state_plugins.symbolic_memory | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2020-06-25 05:50:28,057 | angr.state_plugins.symbolic_memory | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY_REGISTERS}, to suppress these messages.
WARNING | 2020-06-25 05:50:28,057 | angr.state_plugins.symbolic_memory | Filling register r10 with 4 unconstrained bytes referenced from 0x10794 (__libc_csu_init+0x0 in easy_too (0x10794))
WARNING | 2020-06-25 05:50:41,093 | angr.engines.successors | Exit state has over 256 possible solutions. Likely unconstrained; skipping. <BV32 Reverse(packet_0_stdin_11_480[255:224])>

[b'\x04\x00\x02\x02\x00\x02M.\x02\x1d\x01\x96\x01\[email protected]\x08\x80\x10\x08\x01\x02\x08\x80\x0f\x01\x02\x80\x01\x80\x04\[email protected]\x80\x08\x08\[email protected]\x08\x08I\xab\x02\x02\x01M\x08g\x08\x10\x02\x10\x02\xa9\[email protected]@\x0e\x80$\x00']

These 6 binary strings are the inputs that angr found causing different end states. We see EasyARM. in the beginning of the last index, which is likely our flag. It is likely that the binary only evaluates up to a certain amount of characters, so the trailing characters are junk that can be discarded. Let’s validate our flag…

Enter the password: EasyARM.
You entered: EasyARM.
Access Granted!

Staring at assembly is great, but wow, angr is a great tool.

Flag: flag{EasyARM.}