Saturday, 29 December 2018

Corebot writeup - 35C3 CTF

The binary given in this challenge is a 32-bit Windows Binary.

The main subroutine of the binary looks like shown below:


It stores the flag encrypted in the resource section in the resource called: "65".

Main functions performed by the binary are:

1. It retrieves the VolumeID with GetVolumeInformationA() API.
2. This is used to calculate a key of length 0x20 bytes.
3. The key is imported using CryptImportKey() and the entire key including the public key structure looks like shown below:


The public key structure looks like shown below:

typedef struct _PUBLICKEYSTRUC {
  BYTE   bType;
  BYTE   bVersion;
  WORD   reserved;
  ALG_ID aiKeyAlg;
} BLOBHEADER, PUBLICKEYSTRUC;

Based on this, we can see:

bType = 0x08 (PLAINTEXTKEYBLOB)
bVersion = 0x02
aiKeyAlg = 0x6610 (AES)
length of the key = 0x20 bytes.

The actual 32 byte key is stored after this.

Decryption of the Flag:

1. Encrypted flag is loaded from the resource called "65".
2. The key imported above using CryptImportKey() will be used to decrypt the flag using CryptDecrypt()

013C1210  |. 813F 33354333  CMP DWORD PTR DS:[EDI],33433533
013C1216  |. 74 0B          JE SHORT corebot.013C1223

If the first DWORD of the decrypted flag is: 0x33433533, then we have found the correct flag.

To solve this challenge, we need to bruteforce the VolumeID. I wrote a few lines of assembly code to bruteforce the VolumeID from 0x0 to 0xffffffff as shown below:

xor eax, eax
inc eax
push eax
call main()

Inside the main() subroutine, we have to NOP out the call to GetVolumeInformationA() and ensure that eax is restored when we return back to above code to continue bruteforcing.

Using this method, we can find the flag as shown below:


Correct value of Volume ID is: 0x25C3
Flag is: 35C3_MalwareAuthorKryptoChef

c0d3inj3cT

PHP Writeup - 35C3 CTF

In this challenge, we are given a PHP file with contents as shown below:


Challenge is running at: nc 35.242.207.13 1

So, we need to craft an input and send it in order to retrieve the flag.

Observations:

1. Our input will be unserialized.
2. There is a Class called "B" with a __destruct() method.
3. The __destruct() method will echo $flag.
4. $flag contains the contents of the file called flag.

We can send the serialized input as shown below to retrieve the flag:


Flag is: 35C3_php_is_fun_php_is_fun

c0d3inj3cT

Permute Writeup - 35C3 CTF

The binary given in this challenge is a 32-bit ELF file.

$ file program
program: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.32, dynamically linked, interpreter \004, stripped

When we execute the binary, it asks us for a flag byte as an input:

$ ./program
Usage: ./program <flag byte>

In the screenshot below, we can see the requirements for the input of the binary:


1. argc == 2
2. strlen(argv[1]) == 1

So, we need to send a single character as an input.

When we execute the binary with a single character input, we notice that the MD5 hash of the binary changes as shown below:

$ md5sum program
817daa25ea4048e31a9703f53ff7c15a  program

$ ./program 3

$ md5sum program
80021d00fc812bbd9ccd3c76bdf0fcd1  program

When we pass 3 as an input to the binary, it changed the original binary itself.

Now, let's analyze further.

1. The binary is using libcapstone library to modify the disassembly of the binary at runtime.
2. The changes are written to a tmp file and the original file is overwritten. This results in the change in MD5 hash.
3. Disassembly of the binary is modified in such a way that the original logic of the program does not change.

We can observe in the screenshots below the similarity as well as the difference in the disassemblies between the original binary and after the binary was passed an input of 3.

Original binary:


Modified Binary:



Now, let's check how the input byte is being processed by the binary.


1. The subroutine, sub_804A1EE returns the value in eax.
2. This value is used to perform an XOR with the input byte as shown below:

LOAD:08049FF9                 call    sub_804A1EE
LOAD:08049FFE                 add     esp, 10h
LOAD:0804A001                 mov     [ebp+var_A], al
LOAD:0804A004                 mov     eax, [ebp+arg_C]
LOAD:0804A007                 add     eax, 4
LOAD:0804A00A                 mov     eax, [eax]
LOAD:0804A00C                 movzx   edx, byte ptr [eax]
LOAD:0804A00F                 movzx   eax, [ebp+var_A]
LOAD:0804A013                 xor     eax, edx
LOAD:0804A015                 mov     [ebp+var_B], al

3. We observe that the result of this XOR operation is equal to 0 only when the correct flag byte is passed as an input.

So, to solve this challenge, we need to do the following:

1. Bruteforce the flag byte till we get the XOR result as 0.
2. Pass the correct flag byte to the binary so that it overwrites itself and we get a new binary.
3. Do step 1 with the new binary.

And we need to continue this process till the binary prints "WIN" which indicates that we have completed processing all the flag bytes.

The flag we get is: 35C3_tempuemr_temupre

c0d3inj3cT

Sunday, 23 December 2018

Friedrich's Christmas Hangover - X-MAS CTF 2018

The binary provided to us in the challenge is an ELF binary however it has an encrypted file header due to which it will not execute. We need to fix the binary header.

So, how do we know that the binary header is encrypted?

We can first run the file command on the binary and we can see that the OS tells us it is a data file and does not recognize it as an ELF file.

$ file chall
chall: data

Next, we check the binary header using hexdump. We notice that the binary header begins with rHAK. We also notice that there are a large number of 0xd bytes in the binary header.

We can now run hexdump on an ELF file and view the first 250 bytes to compare the headers as shown below:


We can quickly confirm that the binary header is encrypted using the byte, 0xd by XOR'ing the first 4 bytes of the file with 0xd and we obtain the bytes {0x7f 0x45 0x4c 0x46} which corresponds to ELF header.

So, to fix the binary, we need to XOR the first 0xc8 bytes of the binary with a single byte key, 0xd.

Once, we have fixed the binary we can run the file command to confirm that it is a 64-bit ELF file.

It also executes properly now.


Let's check the main() subroutine of the binary now:


Quick observations from the main() subroutine:

1. The binary is using the ptrace() anti debugging technique. However, it's important to note that the binary invokes ptrace() two times consecutively. This is done to detect if the return value of ptrace() was patched.
2. Binary expects an input and calculates the length of the input.

Further in the main() subroutine, we observe that the binary is using conditional jump instructions to jump in between the code. This is a common anti disassembly technique. As you can see, IDA did not disassemble the complete code correctly due to this technique:



Let's debug the binary to understand how it processes the input and performs validation.


We can observe from the above screenshot:


1. Calculates the length of the input.
2. Checks if the length is 0x24 bytes.

The screenshot below shows how the input is processed by the binary:


Observations:

1. There are two loops. An outer loop and an inner loop.
2. In the inner loop, it calculates an offset and use it to read a DWORD from an array (at address: 0x601100).
3. Multiplies the DWORD from step 2 with a byte of the input (at address: 0x602560). The result is added to the total sum.

The screenshot below shows how the sum is validated:


At memory address, 0x601060, there is an array of DWORDs. Each time the outer loop is executed, the sum is compared with the corresponding DWORD from the array at address: 0x601060.

Observations:

1. Outer loop executes 0x24 times.
2. Each time the outer loop executes, it will compare the calculated sum with a corresponding DWORD from the array at address:  0x601060.

So, we have 0x24 equations which need to be satisfied.

Now, let's have a look at the array at address: 0x601100 which was used to calculate the offsets in the inner loop.

The interesting thing about this array is that it contains 1296 elements as shown below:

1296 = 36 * 36

The calculation of the sum can be represented as shown below:

def gen(input, outer_counter):

    result = 0
   
    for counter in range(len(input)):
        outer_counter = outer_counter + (outer_counter << 3)
        outer_counter = (outer_counter << 2) + counter
        a = offset[outer_counter]
        b = ord(input[counter])
        result = result + (a * b)
       
    return result

After analysing the above array more, I observed that it is a collection of 36 arrays. Each of those 36 arrays is used to compute a sum which is later compared by the outer loop.

So, we have:

1. 36 Arrays
2. 36 Sum values calculated using the bytes of input (whose length is also 36 bytes)
3. Each value of Sum is compared with the corresponding DWORD from an array consisting of 36 elements.

We can solve this using Z3 now.

Here is the code to solve it: https://gist.github.com/c0d3inj3cT/21384bb9b14373905eda3ba9c8f97164

c0d3inj3cT