This challenge was solved by @R3x and @d3xt3r during the CTF. Writeup by @R3x
The challenge has two files – an Linux 64 bit executable and a encrypted file.
Reversing the executable
Running the executable we notice that it takes two command line arguments. The first argument is the input file and the second is the output file. We noticed that the contents of the input file are encrypted and stored in the output file.
Running the executable in gdb. We saw that it exits without printing anything. This looks like a possible ptrace implementation. Looking into the main function We don’t see anything standing. This leads to the conclusion that the ptrace might have happened before the main function. This leads to a variety of possibilities and the most obvious one being the .init_array section.
Sections named
.init
,.ctors
,.preinit_array
, and.init_array
are to do with initialization of C/C++ objects, and sections.fini
,.fini_array
, and.dtors
are for tear down.
Taking a look at the .init_array section we saw a lot more functions called than usual. And indeed there was a ptrace function and a ptrace syscall in these functions. So we quickly patched those and we were able to debug the binary.
Now looking into the binary in IDA we see that a lot of functions aren’t properly decompiled. So we do some patching here and there to get the decompilation working properly.
Below is a snapshot of the main function where the input file is actually being modified.
Here we noticed that the input_modification_function actually takes the arguments as two sets of four characters from the input and a constant key.
Again some patching inside the input_modification_function actually gave us a proper decompilation.
Below is a snapshot of the input_modification_function. (here sub_401DE6 is a rotate left function)
Now we just had to reverse the above function and we can get each four blocks of the flag from the given encrypted flag.
So we wrote a script in python to reverse the same. (CTF pressure so the script is obviously messy)
from pwn import * keys = [ "6291bda5", "d40cbbbb", "cdb9f3e5", "edbd5140", "2a716584", "42a476de", "79c7cea9", "48852b0e", "2a53b9c8", "2984790b", "daaed337", "0245815e", "014020ae", "3a84aaa9", "84b1fd24", "2766105f", "1b765e10", "b691adc9", "eb50c850", "264c358b", "32213a84", "387a7378", "1d7a8a61", "883de7f1", "2c3bae3b", "6de14ba2"] ror = lambda val, r_bits, max_bits: \ ((val & (2**max_bits-1)) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) f = open("data.jac2","r") cont = f.read() for ctr in xrange(0, len(cont), 8): v1 = int(p32(int(cont[ctr:ctr+4].encode('hex'), 16)).encode('hex'), 16) v2 = int(p32(int(cont[ctr+4:ctr+8].encode('hex'), 16)).encode('hex'), 16) for i in range(12, 0, -1): v2 = ror((v2 - int(keys[2 * i + 1], 16)) & 0xffffffff, v1 & 0x1f, 32) ^ v1 v1 = ror((v1 - int(keys[2 * i], 16)) & 0xffffffff, v2 & 0x1f, 32) ^ v2 inp1 = (v1 - int(keys[0], 16)) & 0xffffffff inp2 = (v2 - int(keys[1], 16)) & 0xffffffff print hex(inp1).replace('0x','').decode('hex')[::-1], print hex(inp2).replace('0x','').decode('hex')[::-1],
The above script just reversed the algorithm of the input_modification_function. Running it printed out the flag for us.
VolgaCTF{ptr@ce_ant1_r3verse_@ll_in_va1n}
Leave a Reply