Tuesday, 6 February 2018

Codegate CTF 2018 Preliminary - Boom

Even though I didn't solve any challenge during the CTF, Codegate was fun. There were a variety of RE challenges this time. Half of the day I tried the challenge Easy Serial. I understood that it was a haskell program. I haven't used it before and debugging in GDB did not make much sense as haskell programs do not use the conventional stack. I read about haskell program and it's stack structure then left it midway to look at the challenge Boom. I guess this challenge can be solved in many ways. I approached it in the following way:

Boom was written in rust. I tried running the program.


From the output it is evident that there are 5 stages in this challenge.

Let's figure out what is the first one. Inside the main::main function there is a function called main::func1 which looks interesting. The first input and check happens in that function itself.
We can see an if statement and a string compare in it. Our input is compared with the string "Know what? It's a new day~". If it is correct then main::func13 checks if the file /tmp/files/1 is present or not. If that is present then only we move on to the second check.
The second check happens in the function main::func2. First of all it checks if the input has 7 characters. In this case, the characters are space separated integers which will become clear with little debugging.

This is a predefined array in the program:
f7*zq5$ase0t6ui#^yd2owgb_n8pu4!k&vc@lrj19mx3h

The input is 7 integers which after few calculations must match with the position of characters of the string pand0ra in the above array. With a little trial and error along with one of my teammates, I was able to find the correct input as 57 14 23 92 50 7 14. If it is correct then the check for file /tmp/files/6 happens. If that is present then we move on to the next check.

The third input is also a string of integers i.e. 25 space separated integer values. Put a breakpoint in GDB at the address of _ZN4main5func717h1a0f9200721da090E+2791 and we can observe that the value at rdx (our input, one character at a time) is compared with QWORD PTR [rax+rsi*8]. To get all the values at that memory location, I wrote a small GDB script.
Running the script at that address gives the correct input and that is:
17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9

Now make the file named 10 at the same location as other files. So now we have passed the first 3 checks. Moving onto the 4th check. 

The 4th input is just an integer. The most important check for it happens in the function _ZN4main6func1217h2c7664a1999a9c74E. The integer should be such that the return value of the function must be 0x6b.

I was too lazy to reverse the function, so I wrote a GDB script to bruteforce the correct input. The correct input is 188. Also we need to have a file named 13 at the location /tmp/files.
The final input is a bit confusing because many values are possible (that might be the reason why they hosted the challenge on a server). We need to enter 4 indexes as input. I gave the input 1 1 1 1 and it worked.

The complete output of the binary:

This is just a brief summary of how to solve the challenge. There are many checks and exception handling in the program. I hope giving these inputs on the remote server will print the flag, can't confirm as the server is down after the CTF.

Wednesday, 5 April 2017

Starblind - Teaser CONFidence CTF 2017

This was a 200 points reverse engineering challenge.
Challenge Description:

The light of the star is blinding.

Clicking on the above link, it redirects to another website. It was actually very surprising to get a link to a website as a reversing challenge. When we view the source code, we can find a javascript link which is base64 encoded. After decoding, we get the following file Starblind.html.

The input to be supplied is 27 characters long. After encoding it using  CalcSHA4, it is compared with the following string:
983bb35ed0a800fcc85d12806df9225364713be578ba67f65bc508b77f0c54878eda18a5eed50bac705bdc7db205623221e8ffe330483955a22216960754a122

I guessed that the correct input must be the flag. Actually, some junk values were being added to our input to make its length 64. Then is was encoded by two functions xor and perm. Each function was being called alternatively. The xor function had an array of length 64 as argument and perm had an array of length 512 as argument. Then many operations were being carried out on our input with the help of those arguments. In total both of those functions were being called alternatively for a total of 512 times.

The functions were being called in the following order:
perm()
xor()
perm()
xor()

What we have to do is this:
reverse_perm()
xor()
reverse_perm()
xor()

Now lets see what the perm function actually does:
        let perm = function(imm) {
            let n = new Uint8Array(64);
            for (let i = 0; i < 512; i++) {
                const dst_bit = i % 8;
                const dst_byte = i / 8 | 0;
                const sign = Math.sgn(imm[i]);
                const idx = sign ? -imm[i] : imm[i];
                const src_bit = idx % 8;
                const src_byte = idx / 8 | 0;
                let b = (r[src_byte] >> src_bit) & 1;
                if (sign) {
                    b ^= 1;
                }
                n[dst_byte] |= b << dst_bit;
            }
            r = n;
        };
What this function is actually doing is,
n[dst_byte][dst_bit]=r[src_byte][src_bit]
The function does this for each bit. So the total no. of iterations will be 64 X 8 times i.e 512.
Finally the value of n is saved to r. Then xor function is called with the integer array of length 64 as argument. Then r is xor ' ed with that array and result is stored in r. The same things are repeated again and again for a total of 512 times.

Now what I did is decode the encoded string given in the program to find out the flag. For that I wrote the following script:

Finally the script prints out the following flag:

DrgnS{Humank1ndEmpire0fAbh}