Nuit Du Hack CTF 2017 Quals Matriochka Write up

This was a reverse engineering challenge that consisted of 4 parts. I’ll just be describing part 2 over here. If I manage to solve part 3, I’ll update that too.

Part 1 was really simple. All you need to do is to run an ltrace and you’ll see that argv[1] is being reversed and then compared with a string “Tr4laLa!!!”.

So once you pass step1, the binary then goes on to decrypt and print out the step2 binary.

Step2.bin is relatively trickier. Taking a quick look at the CFG might suggest that this is another job for angr. But that’s where the challenge slaps you across your face.

The first few BBL’s are verifying whether the argv[0] is “./step2.bin”. After those checks, it goes on to check if  length of argv[1] is 33 argv[1][0] is “W”. That gives us the length and first character of our flag. Next it goes on to do some messed up operations.

This is the part where it gets tricky.Screenshot from 2017-04-05 14-32-22

At the time of execution of the first instruction, rax contains a pointer to a copy of our argument stored in the heap via an strdup call. Also, a copy of rax is saved in r12. So rbx contains the pointer to the byte at offset 1 of our input.

It then calls these functions, sigfillset and sigaction. After a bit of googling, I found out the reason behind the usage of sigfillset and sigaction.

Usually, when you wish to add signal handlers for multiple signals, there might arise a scenario where two signals are caught. The first signal causes the appropriate signal handler to be invoked. Now if the second signal is caught while the signal handler of the first signal was being executed, that causes trouble. Hence, the method that is usually followed to avoid this scenario is to block all signals if a signal handler is being executed. Sigaction decides which all signals to block based on a data structure called set. Sigfillset basically fills this set with all signals. So a combination of these two simply blocks all new signals if a signal handler is under execution.

Now that that’s clear, lets get back to the code. The signal handler is assigned to the signal identified by 5 which is the SIGTRAP signal. There are two signal handlers in this code ie inc_byte and dec_byte. The signal handler assigned is dependent on the value of the LSB of the byte at rbx-1.

So what happens here is that, after the signal handler is set for SIGTRAP, the program executes an int 3 instruction. The int 3 instruction basically generates the SIGTRAP signal. This causes the signal handler to be invoked. The signal handler in turn does either an inc [rax] or dec [rax] and returns.

This process is repeated until a counter , initialised to 0, becomes equal to [rbx-1].

So basically, inp[1] becomes inp[1]-inp[0] or inp[1]+inp[0] depending upon the LSB of inp[0].

This whole process is then repeated until all the characters of the input have been changed this way.

What happens after this is a basic xoring and comparing with some fixed values in the bss segment. If our input passes all those checks, we get the “Well done” string printed out along the the step3.bin.

So, the first thing I did was to use angr to find out the values that could pass the final xoring and comparing blocks. And that angr did very well.

Now I had a string which was the result of the increment/decrement loops. Keep in mind that we already know the first byte of the flag. So what’s left is to whip up a simple script that can reverse the functionality of this loop.

And after that’s done. We’re left with the flag and the next challenge.

Anyway, here’s the script.

import angr


def get_constant():
    p = angr.Project("./step2.bin", load_options={'auto_load_libs': False})
    s = p.factory.blank_state(addr=0x4008DB)
    val = s.se.BVS('val', 33*8)
    s.memory.store(0x6335b0, val)
    s.regs.r12 = s.se.BVV(0x6335b0, 64)
    pg = p.factory.path_group(s)
    pg.explore(find=0x400906, avoid=0x4009BC)
    for pp in pg.found:
        return pp.state.se.any_str(val)


def get_arg(out):
    result = "W"
    for x in xrange(1, len(out)):
        c = out[x-1]
        d = out[x]
        flag = ord(c) & 1
        if flag == 1:
            result += chr((ord(d)+ord(c)) % 256)
        else:
            result += chr((ord(d)-ord(c)) % 256)
    return result


if __name__ == "__main__":
    out = get_constant()
    arg = get_arg("W"+out[1:])
    print arg

Flag : When_i_grow_up,_I_will_be_a_flag