Category | Difficulty | Solves | Author |
---|---|---|---|
reverse |
hard |
3 | sudoBash418 |
Description
This one seems a bit trickier than the last.
Good news though: I received word of a new form of black magic called “symbolic execution” - supposedly it can extract flags from even the trickiest of binaries!
Have fun!
Players are given two files to download: flag-checker
and flag-checker-arm64
.
There are also three hints available:
angr
should prove useful.- Your script should not take more than a minute to find the solution.
- If you know C, try testing your script out on a proof-of-concept C binary to better understand what’s happening.
If you don’t know C, you can find practice examples online (with source code) for the same purpose.
Note: I will use the x86-64 flag-checker
binary in this writeup, but my analysis and solution are valid for both binaries.
Analysis
The first few steps are very similar to Rusty Rev 0.
(these steps tend to be common across binary reverse engineering challenges)
In summary:
- We have a 64-bit dynamically-linked executable, including a symbol table.
- Running the executable gives a short error message:
usage: ./flag-checker FLAG
.
If you pass a random variable as the first argument, you’ll get a"Flag is incorrect"
message instead.
Decompiling the binary in Ghidra presents an identical main
function.
After checking CLI arguments, it passes the first arg to verify_flag
, and uses the return code to determine whether to print "Flag is correct"
or "Flag is incorrect"
.
The verify_flag
function contains the logic we’re interested in (I’ve renamed + retyped some variables):
|
|
As with Rusty Rev 0, the first thing this function does is check the length of our input.
In this case, we know the flag must be 0x26
(38) bytes long.
To solve the challenge, we can either continue using static analysis, or we can use a symbolic execution tool called angr to automate the process.
Solution
The purpose of this challenge is to introduce players to symbolic execution, so that’s the route I will discuss.
First, install angr
using pip/poetry/whatever tool you prefer.
If you’re not familiar with angr, I highly recommend checking out their documentation, especially the core concepts section.
Here’s a simplified version of my solve script:
|
|
At a high-level, my script:
- creates a symbolic variable to represent our input:
flag_str
- creates an initial program state, representing the state of the program before it begins execution
- searches for a state that matches our desired outcome: in this case, we check for
"Flag is correct"
in the stdout stream - uses that program state to evaluate our
flag_str
variable into a concrete string (technically three) - prints the evaluated string(s)
This is a basic example of using angr
to solve a reverse engineering challenge.
Rusty Rev 2 ups the ante, requiring some optimization and a better understanding of angr and how symbolic execution works.