Fuckzing reverse Writeup
EKOPARTY 2016
Overview
This challenge is a bit different than other reversing challenges. Instead of guessing the flag and the program accepting or rejecting it, you send your guess to the server (running the program) and if your input is correct it outputs the flag. The only practical difference here is that we need to generate the correct input locally and send the input to the server to receive the flag.
Binary Investigation
As usual, we run file
on the program to find what we are dealing with.
Throwing the binary into IDA, we are greeted with this graph view.
Ouch.
At the beginning of the program we are asked to input a team name:
Then, the program reads in 300 bytes of our input:
Following this, there are one hundred discrete checks on the user input. All the ones that I looked had did simple bitwise operations such as xor-ing, rolling, etc. For example, the following snippet checks the 0x30th character of the input (buf[0x30] >> 1 & 256 == 0x21
).
If all of these checks pass, the server prints out the flag by calling the get_flag
function from a shared library (this part is in the exact center of the graph view). Since we don’t have access to this library, we are reliant on the server to receive the flag.
If any one of the checks fail, the execution diverges to the second half of the graph view, in a position corresponding to where it failed (i.e. if it failed the first check, it jumps to the first block in the second half). Finally, given any input the program outputs “Goodbye!”
Satisfying the Constraints
While there are no subroutines called in all of this, I didn’t want to spend the whole day reversing and solving each constraint. The presence of afl
(and the name of the challenge) suggests that we can use AFL to fuzz for a satisfying input.
However, the linear nature of the program, lack of loops, clear goal, and precise constraints makes this a suitable job for angr.
For those uninitiated, angr is a symbolic execution engine. This basically means that it can reduce a program to an SMT equations and can calculate inputs to get to parts of the program (even with additional user defined constraints). The lack of looping means we don’t have to deal with path explosion and the precise input constraints yield for simple SMT equations.
Using angr
First, we need to define the addresses of the program where want to start, find, and avoid. You can start wherever you want, but it can require you to do more setup if you don’t start at the beginning. The addresses to avoid are the places where the execution jumped if a check failed – they happened to be regular. Additionally, we need to create the starting state of the program.
Next, we need to add constraints on stdin since we are reading from it for input. If we started execution later in the program, we would have had to constrain the memory where the input was read to. These constraints help to limit angr’s search space and speed up solve time.
Now that we’ve constrained the input, we just need to create an execution path from our addresses and let angr run.
In around thirty seconds, angr was able to find a satisfying input.
Trimming the trailing null bytes, we find that the correct input is ‘@@@(h@@@@f@v@ @@/@vCo@&D@ACHP@@@@@@@@D@@ @X@@@@@B@h@]@@@W@UB@”(@Lq@@@@@,FBtH@?6@@” * k[Q@@@@@@@@@I@@’. When we send this to the server, it sends back ‘Your flag is EKO{}’ with a string of random hex characters inside the braces that is different every time.