Category | Difficulty | Solves | Author |
---|---|---|---|
reverse |
hard |
1 | sudoBash418 |
Description
Looks like they’ve gotten smarter: this time the flag is locked behind a hash function! You might need the dark art of optimization for this one.
I’m sure you’ll figure it out. Can’t promise you won’t be cursed in the process though :P
Players are given two files to download: flag-checker
and flag-checker-arm64
.
There are also three hints available:
- This challenge can be solved similarly to Rusty Rev 1 - but you’ll want to optimize your script.
- Don’t forget the flag format:
- starts with
clubeh{
- ends with
}
- contains only printable ASCII characters
- starts with
- A well-optimized script should be able to get the flag in under 3 minutes.
You may have better luck with the x86-64 binary compared to the arm64 one.
Note: using the x86-64 binary should work fine on any device, and will likely be easier to work with than the ARM64 binary.
Analysis
This challenge is very similar to Rusty Rev 1.
I’ll be assuming you’ve already read that writeup to understand the premise of this challenge.
The main difference is the flag-checking logic, which is significantly more complicated.
As before, basic static analysis will reveal the flag length: 0x34
(52) bytes.
Solution
This solution also uses angr
, whose documentation you can find here.
The section on optimizing symbolic execution is especially relevant here, along with this cheat sheet.
Here’s a cleaned-up version of my solve script:
|
|
This script has a few changes:
- Updated flag length.
- The beginning of the flag is constrained to
clubeh{
. - Each byte of the flag is constrained to certain ASCII character ranges. These ranges exclude uppercase letters and most punctuation marks.
Using the full range of ASCII printable characters (0x20
to0x7e
) works too, it’s just slower. - The
Threading
technique is enabled, which can help when angr spends a lot of time in native dependencies (like z3).
A few notes on optimization:
- Further tightening the character constraints actually increased the execution time in my testing.
- Using
pypy
often helps, but when I tried it here, it quickly used over 4GB of RAM and OOMed. - Profiling shows that 80% of time is spent in z3 - and most of that is spent copying z3 solver states.
This is likely why adding more constraints slows down execution: the solver states became larger and thus took longer to clone.
This script takes about 30 seconds to finish on my desktop, with peak memory usage around 560MB (Ryzen 5600X, CPython 3.11.1, angr 9.2.37).