« Back to home

PlaidCTF - no_mo_flo writeup

This weekend I joined team NeverTry on PlaidCTF. One of the interesting challenges that I attempted was the "no_mo_flo" reverse engineering exercise.

The description provided was simple:

Can you go with the flow?

To begin with, we load the challenge in IDA Pro. After some parsing, we see the first step is to read 0x20 bytes from STDIN:

Once our input is read, the data is split into 2 buffers, one containing even bytes, and one containing odd bytes:

First, the even byte buffer is parsed. The r8 register is set to "1", and then byte by byte, a number of comparisons are made on the input.

I won't go through them all as there are 23 in total, however the challenge is to pass each check. We know at each stage if we are successful, as we want to complete the 23 checks with the r8 register remaining at "1".

Let's have a look at a sample check:

Here we have a byte being shifted to the left by 1 bit, and then being compared to 0x60, meaning that the input byte should be 0x30, or "0" in ASCII.

Once we are completed, we have the following "even" input text which passes all of the checks:

P T { 0 f 0 _ 0 l k _ h h l _ 0

We know we are on the right track, with the PlaidCTF flag format starting with PCTF{, which matches our partial flag.

Next, the odd bytes are processed. This is a bit more tricky. Before the data is used, a custom signal handler is registered:

Just as above, each byte within the "odd" buffer is processed, however this time, an idiv instruction is used to raise a SIGFPE exception after each comparison, which is intercepted by the registered handler.

The aim of this set of checks is to ensure that the rsi register stays at "1". Let's have a look at a sample check on a byte of the "odd" buffer:

With this check, the byte value has "3" subtracted, and is compared to 0x40, meaning the byte should be 0x43.. or "C" in ASCII.

The r10 register is loaded with a pointer to the next "check" function to complete on the following byte. r11 is used as an indicator to the custom sigaction function as to an additional check which should be completed. Then an idiv throws us into the signal handler.

Reviewing the documentation for the sigaction function, we see that the handler has the following signature:

void (*sa_sigaction)(int, siginfo_t *, void *);

We are interested in the third parameter, which is a pointer to a ucontext_t. Unfortunately this is an opaque structure, however by reversing the handler we can infer what values are being used.

Let's have a look at one of the additional checks:

After some guessing, we can infer that this function is checking the EFLAGS value at the time of the exception, and making a number of comparisons. I've marked the checked EFLAG bits in the above disassembly.

Finally, after "going with the flow" (sorry, couldn't resist ;) and finding the correct set of input bytes to pass all of the checks, we have the following input, which turns out to be our flag:


For some of the other write-ups from team "NeverTry", check out: