I recently participated in the MRMCD CTF, which had a challenge called “Once Upon A Time”. The hint for the binary was that it will simply print the flag … but some patience might be required.
Since I am way less binary reverse-engineering ninja than might appear from the scoreboard, I threw the binary into the Snowman decompiler.
Here, I could recognize the following structure quickly:
v4 = 0;
do {
v5 = 1;
while (v5) {
++v5;
}
--v4;
} while (v4 != 77);
fun_640("%d done\n", 0, 64, "%d done\n", 0, 64);
v6 = 0;
do {
v7 = 1;
while (v7) {
++v7;
}
--v6;
} while (v6 != 82);
fun_640("%d done\n", 1, 64, "%d done\n", 1, 64);
[...]
So the challenge hint was technically correct, the inner while
loop would run
until the (int64) v5
would overflow and become 0, while the outer loop would
terminate eventually when v4
was decreased from 2**64 to 77.
At this point, one could have patched the decrements into increments and vice-versa, but that seemed quite tedious.
If you squint closely though, you can notice that the desired values for v4
and v6
correspond to the ASCII characters M
and R
, the usual start of a
flag. During the CTF I just proceeded to manually convert them and concatenated
them, but for the sake of (useless?) automation here’s a one-liner to get the
flag:
$ grep '} while (v' once_upon_a_time.c | cut -d'=' -f2 | cut -d ')' -f1 | python -c 'import sys; chars = sys.stdin.readlines(); print("".join([chr(int(c, 0)) for c in chars]))'
MRMCD{so_sorry_for_the_delay}