This is the ninth part of the Flare-On 6 CTF WriteUp Series.
9 – reloaderd
The challenge reads
This is a simple challenge, enter the password, receive the key. I hear that it caused problems when trying to analyze it with ghidra. Remember that valid flare-on flags will always end with @flare-on.com
From a cursory look, this does look look like a simple challenge. Running the provided PE file reloaderd.exe prompts for a key.
Figure 1: We need a key
Loading the binary in x64dbg we notice two calls from the main function.
Figure 2: Two calls in main
The print_banner just prints the “ReLoaderd” banner. The remainder of the program logic is in the do_it function. The decompiled code of do_it in IDA looks like the following.
Figure 3: The key check logic
The program reads in a key of maximum 33 characters. Next we can see a series of checks on the key. The key length must be 11, key[1] xored with 0x31 must equal 94 and so on. This looks too simple to be the ninth challenge, but anyway let’s calculate the key by reversing the constraints.
key[0] <= 82 ‘R’
key[1] = 94 ^ 0x31 = 111 ‘o’
key[2] = 84 ‘T’
key[3] ^ key[4] == 65
key[5] = even
key[6] = 101 ‘e’
key[7] = 82 ‘R’
key[8] <= 105 ‘i’
key[9] = 110 ‘n’
key[10] = 71 ‘G’
Most of the letters can be figured out statically with the exception of the third, fourth and fifth letter. With a little brute-force, the key comes out to RoT3rHeRinG. Using it as the key, the program does accept it but prints a flag which doesn’t look right.
Figure 4: A fake flag!
Getting to the real key check routine
What we analyzed till now was not the actual flag check code. Let’s load the binary in Ghidra as the challenge description hints.
Figure 5: Ghidra fails to load the binary
No luck either! Ghidra errors out with an exception which was expected. However if we look at the error closely we can find it crashed while trying to process relocations. May be the binary has something to do with relocations. If we use a PE analysis tool like PE Insider we can notice a strange characteristics of the binary. The preferred imagebase is at 0xFFFF0000.
Figure 6: Preferred ImageBase
For regular 32-bit user mode processes any address above 2 GB (0x80000000) is invalid. Here the Image Base specified in the PE header is invalid. In such a case Windows loads the file at the fixed address of 0x10000. This can be used as an anti-debug technique and is well documented.
Since the binary can never be loaded at its preferred image base it has to be relocated. If we check the relocation directory we can find several relocation entries.
Figure 7: The relocation directory
It’s possible through the clever use of relocations the binary is patching itself at run-time. The code we are analyzing statically may not be the code that’s executed. Let’s re-debug the binary right from the entrypoint of the executable.
Figure 8: An indirect function call
A few lines before it is about to call main we notice an indirect function call through the esi register. Stepping in we discover a hidden function not seen previously.
Figure 9: Hidden code!
The presence of cpuid and rdtsc instructions indicate anti-debug code as seen in Figure 10.
Figure 10: Lots of anti-debug
To bypass the anti-debug we need to nullify a few jumps. In general, we need to nop all those jump instructions which jumps (indirectly or directly) to 11523 as shown in Figure 11 & Figure 12.
Figure 11: Jumps to be nopped out
Getting the flag
Down below after bypassing the anti-debug we come across a piece of code which looks to be the place of the key check routine.
Figure 12: Real key check code
The code as shown in Figure 12 reads in an key of max size 14 chars via the fgets function.
Figure 13: Checking the key
The input key is then xored with a buffer of length 0x35 in a cyclic fashion. If our key is correct the buffer must end with the string “@flare-on.com” as in Figure 14.
Figure 14: The correct key would give the flag
Using the obtained information so far, we can write a script to calculate the correct key. We know the buffer after xor must end with “@flare-on.com”. Thus if we xor the encrypted buffer with the above string it should reveal the key.
target=”7A 17 08 34 17 31 3B 25 5B 18 2E 3A 15 56 0E 11 3E 0D 11 3B 24 21 31 06 3C 26 7C 3C 0D 24 16 3A 14 79 01 3A 18 5A 58 73 2E 09 00 16 00 49 22 01 40 08 0A 14 00″.split(‘ ‘)
target = list(map(lambda x:int(x, 16), target))
correct_end = b’@flare-on.com'[::-1]
key = bytearray()
for b in range(len(correct_end)):
key.append(target[-2-b] ^ correct_end[b])
key = key[::-1]
print(‘key is:’, str(key))
Running the script gives us the key 3HeadedMonkey and thus the flag.
Figure 15: The flag
Flag: [email protected]