Challenge Summary
In this challenge, you are given a simple Linux executable. This program, when run, asks you to enter the flag and tells you if your input is correct (matches the real flag) or not. You must reverse-engineer the flag checker logic, which thankfully, is relatively simple and consists of XORing your input and comparing against a hardcoded array.
Background
Let’s start by downloading the binary.
$ wget "https://metaproblems.com/2340b62ac14172c88fa1f1a3206f2d9f/NothingToC"
$ file NothingToC
NothingToC: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter ..., for GNU/Linux 4.4.0, stripped
Looks like it’s a pretty typical Linux executable. Let’s try running it (note: you’ll usually want to run CTF binaries in containers or virtual machines, but in this case it’s safe to do it on host).
$ chmod +x NothingToC
$ ./NothingToC
=== NOTing To C Flag Checker ===
Ready to check your flag? Let's see what you've got!
Enter the flag to check: blah
Oops! Your flag is 4 characters long, but I'm looking for exactly 28 characters.
Maybe count your characters next time?
That's NOT the flag I'm looking for!
Off the bat, we have some useful info. We are told the flag is 28 characters long, and we can see that this program is in the – fairly typical for CTF reverse engineering challenges – “flag checker” format, where it takes in some user input and tells you if it matches the flag or not. We just need to reverse-engineer that flag checker logic.
Running strings
on it (always a good first step) reveals some text prompts, but no obvious hardcoded flag or anything. Darn.
Let’s pop it into a Binary Reverse Engineering tool. This author used Ghidra, but other popular choices include IDA Free, Binary Ninja, and radare2.
Ghidra
After clicking through the loading process, Ghidra starts off at the entry point of the program. Click the first argument to __libc_start_main
to go to the traditional main()
function.

Once we get there (and rename it to main
):

It looks like there’s a lot of local variables, including some strange hardcoded constants. Thankfully, the decompilation isn’t too lengthy, and there don’t seem to be any function calls other than standard library functions, so this should be relatively straightforward.
At a glance, after the user’s input is taken in, it looks like there’s a bunch of if statements checking various conditions: no fgets()
error, input length == 28, and then… some complicated-looking last thing. If all of those are satisfied, we have the flag, and it congratulates us. I’ll rename some variables and add comments to make it clearer:

Below this section, in the do-while loop, things get a little more complicated. It looks like it’s looping through the input string character by character.

LAB_001011ba
is a section further below where the program will output a randomly chosen failure message and quit, so that’s our “bad”/failure path. We want to not trigger the condition in that if statement.
local_fc
appears to be, at least in Ghidra’s decompilation imagination, a byte array that’s right after user_input
on the stack – so basically, this loop will proceed until it has processed the entire user_input
string (which is of length 28).
Let’s squint and rename these variables a bit. We can also retype them, changing puVar2
to a byte *
, to make the decompilation look a bit nicer.

Much cleaner. Now it’s pretty clear what’s going on – every character of our input gets bitwise NOT applied to it (that’s what the ~
operator is in C) and compared against the corresponding character in other_array
. If they all match, we win; otherwise, FAILURE
.
But what is in other_array
? After a quick Ctrl+F, we find where it’s initialized:

It points to local_138
, and all those values next to it (you can see the numbers in the autogenerated variable names are close to each other) are likely part of the same array of values, but Ghidra did not decompile them as an array.
We could copy out the hex ourselves and manually fix the endianness and such, or, since we know the length of the array, highlight local_138
and hit Ctrl+L to retype it as byte[28]
:

(Make sure to right-click that pesky 200
and convert it to hex to match the rest) From here, I found it easiest to just copy this into a text editor and reduce it down to just the bytes. I used VSCode’s multi-cursors and the Join Lines tool.
b2 9a 8b 9e bc ab b9 84 c8 97 96 ca a0 96 ca a0 91 cf 8b a0 8b 97 cc a0 88 cb 86 82
We can then use Cyberchef to apply the bitwise NOT to each of these bytes, taking care to de-hex them first:

And the flag is revealed.