noxCTF – Crakaker


“I did something awful with the flag. Would you be able to understand how to fix it?”

We are going to dive into the internals of this executable, to thoroughly understand the program and generate the input required for the flag.
At the end I’ll also attach source code(s) of the solution and my interpretation of the program.

Initial analysis of the executable

Before we push the executable into IDA to start reversing it, we should probably know what we are dealing with; we can of course open it up in either IDA or IDA64 and continue on from there but I would throw it into DIE to understand what I am dealing with.


From here, we can understand that we are dealing with a PE+(64bit) that was compiled using MinGW GCC, and so we can prepare ourselves to the compiler wrappers, boilerplate and etc.

IDA, and the real main

Pushing the executable into IDA64, first thing I would do is go through the exports to better understand where the executable actually starts executing code besides the entry-point, if such regions exist.
We do this to ensure we are fully aware of what the executable is going to do, because we want to trace these kind of operations because they can be used to affect the main program, this can be done using a variety of ways; for example:
* Abusing TLS functions
* Abusing CRT startup
* Abusing IAT
* etc.


In the Export Address Table, our IDA has picked up that TLS functions are available and that could be used to execute code before our start function via ntdll!LdrpCallInitRoutine, this is always a good idea to analyze these functions but in this case going through the functions quickly they are just GCC critical state initializers/stub.

Moving on to start, we come across a small start function, entering the first function call we can see the regular __security_init_cookie so we can quickly rename that function to it’s proper name and move forward.




From initial overview of the executable, we are able to notice that it’s not obfuscated or packed in anyway (DIE helps with this as well) so we can freely use the HexRays Decompiler to assist us in quick reviews to find the important stuff.

Inside the next call, we reach quite a big block of code so we should be careful to spot any tricks the author is attempting to use, from my knowledge of GCC and assuming this didn’t have any post-modifications done we can rule out majority of the code by using signatures to detect known GCC generated code.
I will try to use guessing and known code-snippets to attempt and detect most of the functionality of this function.


My best guest this is part of the heavy stackguard protection of GCC, however I might be wrong, this is only for ruling out which parts of the code I actually require.


This is some basic CRT startup, some SEH setting up and etc, we can safely ignore it however keep it mind there might be hidden stuff in the CRT we might need to check.


Command Line parsing, this would get the string after the argv[0] for example if the commandline is Crakaker.exe nop we will get a pointer to nop in g_cmdline


Normally, the values that are being passed into the main function would be argc and argv and there would be a sort of program cleanup as the compilers add, from the exit we can see that result is the exit code so we can assume that the function the exit code is coming from is our main!

Understanding the main

Approaching the main function, we can see a lot of magic values being used so I’ll rename the properly to attend to them later if they are important.


Looking over at input_size, the maximum size is 16 and later we can see var_230 being turned into a number.. I would assume sub_407C4F transforms our input to a number somehow.

Let’s approach this function step-by-step, the compiler tries to not interfere with the main function, so this would be the author’s code we will start with sub_407E50 firstly.


So from first impression, this looks like an initializer of some sort, these are quite common to initialize stuff like global variables or memory/functions to be used by the program so it should be rather safe however… looking at sub_407DE0 points us to a sort of initializer that calls functions that are in the .text section, that’s quite odd.


Let’s take one of the functions, and try to understand what this does.



Oh.. Yup, that’s some anti-debugging.

Okay, so we can rename all of the stuff in sub_407E50 appropriately, the bypass of this is rather simple, either we enable g_debug_check_passed while running our debugger or modifying nt!_PEB->BeingDebugged


Moving on to sub_407B40, we are able to see some sort of structure creation with copying from a global.


Looking at unk_40B060 we can see that it’s just a table that goes from 0x00 to 0xFF, I would assume this is some sort of key system and that unk_40B060 is a key table of some sorts, arg_8 is being added to the table in a circular-motion I’ll assume it’s some sort of seed for the table while arg_10 being it’s size, let’s rename it properly and create a structure.


Some fields are unknown, also the key in the end is being copied to key_dirty my guess it needs the original key around.


So now our main got a bit clearer, and we can see that sub_407C4F the function that transforms our v7 to a number with atoi is being passed the key we just spawned, that means the input together with the key gets to be a number.


Alright, so we can see that indeed key_dirty is being used and changed in the end, and var_C is being used as some sort of offset for the key while var_5 is being used as the next swapping value and in the end a1 gets the result. I’ll assume this is some sort of decryption method from our key and that a1 is a target, I’ll rename the fields properly.


Alright, so we can update some of the stuff in our main


Looking at sub_406A43, it’ll print “try again!” so for simplicity I’ll name sub_406A43 as print_fail and sub_4016AD as print_flag, and so we will move on to print_flag.


At first, it might seem overwhelming but we can see that these operations are mostly on the constant values at the very start of the main which are here to generate something into the var_90 field.


At the end, we can see that it’s indeed our flag printing and that our generated integer from the key is used as an allocation size, which is the flag size in the end, and the var_90 is some sort of a string that is being used against the integer and our original input.


Another sort of initialization, let’s look into it.


From a quick overview, it’s just an initialization of an ASCII table, we can see that it then uses this as an ascii map.
Treating a1 as a character_map here, we rule out input_key_var because it’s used as storage and it contains some rules for output_key_var such as it must be output_key_var & 3 == 0 and the character_map is the actual thing that builds the return value.

I’ll assume that function generates the bytes for the flag, preparing print_flag by the information we got.


Continuing to sub_407AC4


Seems like a start for an unpacking, let’s continue on to sub_4079D8


Looking at the three first functions, I quickly rename them based on their appearance.




There’s nothing too special about them, and they are quick to understand.

Focusing onto sub_4077FA, it looks like some sort of unpacking based on the value we get from the ascii map (which comes from our flag bytes that got generated from the character_map).



Based on calculating the magic_index_value we can generate the following map of how the character gets unpacked:

[0] (x << 3) | (x >> 2)
[1] (x << 6) | (x << 1) | (x >> 4)
[2] (x << 4) | (x >> 1)
[3] (x << 7) | (x << 2) | (x >> 3)
[4] (x << 5) | (x << 0)

Research Summary

So we just went all of that, we understood most of the program thoroughly, what did we learn?

magic_value_0 = 117 * (argc / 2)
magic_value_1 = argc / 12.0;

magic_buffer_1 = aaaaabgfdgdgdsdf
magic_buffer_2 = aaaaaagfsdgsdgdsfgsdfg
character_map is generated by magic_value_* and magic_buffer_*

magic_formula_0 = (3 - 5 * a1 % 8)
magic_formula_1 = 5 * a1 / 8
character_to_ascii_map = accepted: A-Z 2-7

g_table_key goes from 0x00 to 0xFF
g_seed is [0x4B, 0x42, 0xB7, 0x14, 0xD8, 0xAF, 0xA2, 0x4E, 0x1D, 0x86, 0x46, 0xE0, 0xD3, 0xC3, 0x1A, 0xE0]

argc > 1
argv[1] must be at most 16 in length.
magic_buffer_* size is affected by argc.
output_key & 3 == 0

unpacking affects 5 flag characters at once and uses 8 of the flag_bytes at once.
Translate table:
[0] (x << 3) | (x >> 2)
[1] (x << 6) | (x << 1) | (x >> 4)
[2] (x << 4) | (x >> 1)
[3] (x << 7) | (x << 2) | (x >> 3)
[4] (x << 5) | (x << 0)

And after all of that, we also notice that our input only affects how many bytes we allocate for the string..
So looking at that, we only need to figure out how to properly generate the amount of bytes we want, that is generated by the key decryption and the proper amount of argc we need for the magic_buffer_* sizes.

Building the solving script.

So first of all, we are going to control the output of the key decryption, luckily if we look at the function we can see it’s just a XOR between our input to the key value, so we can just use the decryption as the encryption.
We just need to use the same functionality as the one we saw in the program and input what we want to except being used in the program.
Once we do that, we need to pick a size, remember that we need to have output_key &amp; 3 == 0 satisfied to actually work so let’s pick 128 which fits that nicely and is big enough for a flag, in any case we can just make it bigger.
So our script would look like this:

import subprocess
import copy

__KEY_TABLE__ = [x for x in range(0x100)]
__KEY_SEED__ = [0x4B, 0x42, 0xB7, 0x14, 0xD8, 0xAF, 0xA2, 0x4E, 0x1D, 0x86, 0x46, 0xE0, 0xD3, 0xC3, 0x1A, 0xE0]
__MAX_INT8__ = 0xFF
__TARGET_PROGRAM__ = './Crakaker.exe'
__MALLOC_SIZE__ = b'128'

class key_t:
    def __init__(self):
        self._key = []
        self._key_dirty = []
        self._key_offset = 1
        self._key_value = 0

    def spawn(self, seed):
        self._key = copy.copy(__KEY_TABLE__)
        offset = 0

        for i in range(len(self._key)):
            offset += self._key[i] + seed[i % len(seed)]
            offset &= __MAX_INT8__
            swap_value = self._key[i]
            self._key[i] = self._key[offset]
            self._key[offset] = swap_value

        self._key_dirty = copy.copy(self._key)

    def decrypt(self, input):
        if len(input) == 0 or len(input) > 0x10:
            raise Exception('out of bounds')

        offset = self._key_offset
        value = self._key_value
        output = [0] * len(input)
        for i in range(self._key_offset, len(input) + offset):
            value += self._key_dirty[i]
            value &= __MAX_INT8__
            swap_value = self._key_dirty[i]
            self._key_dirty[i] = self._key_dirty[value]
            self._key_dirty[value] = swap_value
            output[i - offset] = self._key_dirty[(self._key_dirty[i] + self._key_dirty[value])] ^ input[i - offset]

        self._key_offset = i
        self._key_value = value

        return output

def main():
    key = key_t()
    value = bytes([c for c in key.decrypt(__MALLOC_SIZE__)])
    proc = subprocess.Popen([__TARGET_PROGRAM__, value], stdout=subprocess.PIPE)
    out, err = proc.communicate()

if __name__ == "__main__":

So we might need to adjust argc, but let’s just try to execute that as it is- and we get nice job: noxCTF{Move_Bitch_Get_Out_Da_Way}
Great, so the argc does not need to be touched, and we are done!