# Bleichenbacher's Ghost

## Hey man, computer security is hard.

Disclosure: I didn’t solve this challenge during the CTF.

First, run file to know what we are dealing with

reversing/beginner/solution » file ../a.out
../a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e3a5d8dc3eee0e960c602b9b2207150c91dc9dff, for GNU/Linux 3.2.0, not stripped


Running the executable, we see that it asks for a flag.

reversing/beginner/solution » ./../a.out
Flag: 123
FAILURE


This seems like a classic crack-me, let’s first try with some symbolic execution tools.

## Attempt 1: Using Manticore

Create a pipenv and install manticore using pip install "manticore[native]" and run manticore ./../a.out. It fails because manticore does not recognize some of the instructions. It seems like this binary is making use of some vector instructions. Shit, let’s try with angr.

## Attempt 2: Using Angr

In the same pipenv (or, if you wish, in a new pipenv) install angr using pip install angr. Let’s see if angr can run the binary with this basic script.

import angr

def get_flag():
p = angr.Project("../a.out")
st = p.factory.entry_state()
sm = p.factory.simulation_manager(st)
sm.run()
return "xxx"

if __name__ == "__main__":
flag = get_flag()
print(flag)


It seems like angr can execute this binary. Sweet. Now, let’s open this binary in r2 to get an idea of what this binary does.

Open binary in r2 with r2 ./../a.out, analyze all with aaa, seek to main with s main and try to disassemble it with pdf.

We notice that the binary reads 15 characters, does a strncmp and based on the result, either jumps to FAILURE or jumps to SUCCESS. We want to trigger the SUCCESS case.

First, let’s create a 15-character symbolic string (I adapted this from the example in the docs)

# 15-character symbolic string
flag_chars = [claripy.BVS("flag_%d" % i, 8) for i in range(15)]
# Append a newline at the end of the first input
flag = claripy.Concat(*flag_chars + [claripy.BVV(b"\n")])


Again, following the example in the docs let’s initialize the initial state and add the constraint that the flag characters are printable ASCII characters.

# enable unicorn engine for fast efficient solving
st = p.factory.full_init_state(
args=['./../a.out'],
stdin=flag
)

# constrain the flag characters to be a printable ascii characters.
for k in flag_chars:


As in the basic script, let’s construct a simulation manager with the initial state (with the constraints) and run it.

sm = p.factory.simulation_manager(st)
sm.run()


Yet again, following the example in the docs let’s grab the first final state with SUCCESS in stdout, parse it into a string, and return that.

# Output the first final state with SUCCESS in the stdout
y = []
if b"SUCCESS" in x.posix.dumps(1):
flag_out = x.posix.dumps(0)
# parse it into a string
flag = "".join([chr(flag_out[i]) for i in range(0, len(flag_out))]).strip()
return flag


Running this (you can find the full script here), we get

(solution) reversing/beginner/solution » python sol.py
WARNING | 2020-09-05 22:55:20,499 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.
CTF{S1MDf0rM3!}


Yay!

Ending Notes. In retrospect, this wasn’t that hard, I could’ve done it during the CTF… (kicking myself) I tried to solve this using r2 and paper which was harder. Another approach to solve this that I learnt from looking at writeups was to use Ghidra; here is an excellent writeup that uses Ghidra to solve this challenge.