Everything-is-wrong
Writing a debugger to side-channel out comparisons with RAX values
Problem overview

When running the problem, we're presented with a map of the world and an input prompt. The binary takes our input and checks it, and then prints out that we entered the incorrect flag.

I opened the binary in Binary Ninja and saw a function called Main_validateFlag_info that looked like the below image, which immediately made me want to see if I could side-channel an answer instead of trying to reverse Haskell.

My normal side-channel tools utilize qemu usermode emulation, but I noticed that the number of instructions executed for the same input varied. Below I'm sending in just the letter "A" and seeing a different number of instructions executed every time. This told me that I might be able to use an instruction counting side-channel but it might be unstable.
I wanted to know the flag length, and can usually just add "A"'s to start getting more instructions, but again I wasn't seeing constant (only variable) instruction increases.
Knowing how unstable instruction counting was looking I wanted to find an alternate side-channel that would give me a clearer picture of how many successful instructions or calls were executed.
Here is where the RAX==0 side-channel comes in
Checking for RAX == 0
RAX is the return or result register for amd64. Most return status codes use 0 to indicate that they were successful. The thinking here is that if we count the number of times RAX is equal to 0, we can access a side-channel indicting general program success without worrying about instructions or threads/timers messing up our side-channel!
I started writing a tracing utility a little while to hook up to another process use PTRACE and hunt for call instructions and dump arguments. I decided to add a little bit more to this tracer and added register comparisons. The premise of the utility is two fold:
fork process from tracer and set PTRACE_TRACEME, then execve the program
PTRACE_SINGLESTEP the process, PTRACE_GETREGS then count and compare regs.RAX == 0
You can access the call tracer here: https://github.com/ChrisTheCoolHut/call_trace and after compiling should be able to run it with:
Getting the flag length
Once the tracer was printing out the RAX comparison values I was able to start printing out the different number of "A"s to see what all the lengths resulted in. I use the python2 -c trick to print bytes from a bash variable i. I looped it and grepped for that.
I actually learned at this point that almost EVERY register provided a side-channel and showed a large increase in registers equal to 0:
At this point we have multiple sources showing a jump in registers == 0 at input length 22 so I knew that flag length must be 22 characters long.
How to build a side-channel solve
The next step is to find out how the problem is checking those input values. Usually these problems compare each value left to right against the hard-coded flag value, but sometimes they'll check certain pieces of the input first.
I needed to see if it started by checking the first character against the flag. The organizers shared that the flag format was shctf{} so we know the first character MUST be s. So we can run our tracer again with all "A"s and with all "A"s, but an "s" at the beginning:
At this point I can tell that we're getting our input checked byte by byte, and each successful character drastically increases the number of times RAX is equal to 0.
The next step is start toward writing a solver is to wrap these calls and check the tracer outputs. I always start my solve scripts with a check method that sends a given input into the tool and comes back with the expected output:
Next we need to be able to modify inputs to our guesses, the snippet below takes in a guess, position, and character and substitutes it in.
Once both those two steps are done, all that's left it to try every letter for each position and check each number paired with RAX==0 counts to see which one has the greatest number of RAX==0 counts.
Since the majority of time spent processing here is during the call_trace, I'm using python threading to start a thread per character and have them join once we have every value
The script will take a while, but here is what it looks like running:
Flag
Instruction Counting
I talked with the challenge author and learned the instruction counting side-channel was stable enough to get both the flag length and compare input values. ( Here is his solve https://github.com/FITSEC/spaceheroes_ctf_23/blob/main/RE/Everything-is-wrong/solve.py)
Once I learned that I tried running Instruction Stomp, and it was able to find that flag too!
https://github.com/ChrisTheCoolHut/Instruction-Stomp
Solve script
Last updated
Was this helpful?