Tags: engineering reverse 

Rating:

Moonwalk

After seeing the iconic Moonwalk, Covid19 wants to use its spike proteins to do the walk on the lung cells of infected people. Could it succeed in this evil endeavor?

Solution

Finding main

We first try to decompile it using a decompiler (ghidra, hopper dissassembler) and see that the main function is not directly available. This happens because the entry point address is different in the ELF header. Refer for more information on ELF header

In cases like this we have to find main in order to be able to view the decompiled code.

Refer this video by LiveOverflow to get a good understanding on how to find main in binaries

On looking through gdb we see that the entry point address is set to start of .text section. img1

We set a breakpoint on __libc_start_main. This function calls the main function. On reaching the breakpoint we get the address of the actual main function. On examining that address we see instructions that could be similar to a main function too.

img1

Now we patch the ELF header to change the entry point address to the value we found. (0x497) The patched binary. This can be patched using any hex editor. We used https://hexed.it/?hl=en . The values to be changed is understood after reading up about the format of the header from the link mentioned above

Analyzing the code

On decompiling the patched binary we see the main function. After cleaning up some of the code from Ghidra( renaming variables, changing hex to decimal, understanding and replacing with easier logic ). The raw decopiled code and cleaned up code are both attached for reference on how the cleaning was done.

From the final printf call we observe that the flag is actually the input we have to give to the program. printf("\nCorrect :)\nFLAG:\tSUSEC{%s}\n%s%s%s%s\n",input_buff,&DAT_001012e5, &DAT_001012e9,&DAT_001012ed,&DAT_001012f1); Only the correct input passes the checks and reaches the print statement.

The values in local_98 array is set before the code begins. Here we see that first_check is incremented only when count is odd and the first condition is satisfied. To get a better idea about when the first condition is satisfied we need to explore the next part of the code which begins with this condition first_check+4 %16==0 . From this we understand that after the above snippet first_check must be 12. So for all odd values of count the first conditon must hold true. So input_buff[9] would have ascii corresponding to ilocal_98[1] and so on till input_buff[20].

So our flag has: w4lk_wh3n_1t in positions 9 - 20

first_check = 0;
count = 0;
while (count < 24) {
    if ( (local_98[count] == input_buff[(long)first_check + 9]) &&
        (count % 2 ==1) ) {
      first_check = first_check + 1;
    }
    count = count + 1;
  }

  if (input_size != 41) {
    first_check = 1;
  }

This next part contains code for a progress bar which is not relevant to our input analysis.

if ( first_check+4 %16==0 ){
    puts("Wait 20 min please.\nMake your terminal wider to see progress bar.");
    temp_time = time((time_t *)0x0);
    count = 0;
    while (count < 0x192643) {

      //Timer code -- useless
      if (count % 100 == 0) {
        ...
      }

      usleep(1000);
      *(ulong *)(input_buff + 21) = *(ulong *)(input_buff + 33) ^ 0x45a9278d6f0be1c3;
      first_check = first_check * 2;
      count = count + 1;
    }

The progrss bar is followed by an condition check. For the code to progrss to flag all the conditions must hold true. We also note that the flag is 39 characters long since input_buff[39] is assigned '\0' This is basically 9 xor equations to be satisfied by the first 9 characters of our flag. A thing to note is that these 9 equations do not have a unique solution.

input_buff[39] = 0;//'\0';
if (((  ((((input_buff[1] ^ *input_buff) == 0x49) && ((input_buff[2] ^ input_buff[1]) == 0x45)) &&
          ((input_buff[3] ^ input_buff[2]) == 0x2a)) &&
         (((input_buff[4] ^ input_buff[3]) == 0x28 && ((input_buff[5] ^ input_buff[4]) == 0x46))) ) &&
        (((input_buff[6] ^ input_buff[5]) == 0x5d &&
         (((input_buff[7] ^ input_buff[6]) == 0x10 && ((input_buff[8] ^ input_buff[7]) == 0x23)))))) &&
       ((*input_buff ^ input_buff[8]) == 0x26)){
         ....
       }

A python scipt to find all possible combinations in the range of ascii values was used and the output was examined to get a possible match.

count = 0
for i in range(128):
    a0 = i
    a1 = a0 ^ 0x49
    a2 = a1 ^ 0x45
    a3 = a2 ^ 0x2a
    a4 = a3 ^ 0x28
    a5 = a4 ^ 0x46
    a6 = a5 ^ 0x5d
    a7 = a6 ^ 0x10
    a8 = a7 ^ 0x23

    if(a0 ^ a8 == 0x26):
        count = count + 1

    print(chr(a0) + chr(a1) + chr(a2) + chr(a3) + chr(a4) + chr(a5) + chr(a6) + chr(a7) + chr(a8))

After examining the (128 entries) output a possible match was identified.

So our flag has: y0u_w1l|_ in positions 0 - 8

Moving on we observe a 10 character string comparison between 29 to 38 characters of input_buff with values in memory address starting from that of local_a2

        iVar1 = strncmp((char *)(input_buff + 29),&local_a2,10);
        if (iVar1 == 0) {
        ...
        }

But we observe from the intial declaration that local_a2 is a single char variable. However when we look at the initial declaration, we see that 9 other variables were declared right after local_a2. So these variables might occupy the subsequent 10 memory address. These variables are shown below. These variables were assigned constant values at different parts of the code. Note: typedef undefined char is present on decompiling this binary in Ghidra

  char local_a2;// Assumed at address say : addr
  undefined local_a1; // Is located at : addr + 1
  undefined local_a0; // addr + 2
  undefined local_9f;
  undefined local_9e;
  undefined local_9d;
  undefined local_9c;
  undefined local_9b;
  undefined local_9a;
  undefined local_99; // addr +  9

On getting the values we conclude that flag has : _t0_w4lk;) in positions 29-38

The next condition checks a part of input_buff for a value and then subsequently changes it to a new value after xoring with a constant. The final value would then be 0x20c44eba3078d09c ^ 0x45a9278d6f0be1c3 which on converting to ascii and reversing (Since Little Endian is used ) gives the flag characters. Here we know the condition has to be satisfied so we can safely assume the initial value is the value present in the condition.

          if (*(long *)(input_buff + 21) == 0x20c44eba3078d09c) {
            *(ulong *)(input_buff + 21) = *(ulong *)(input_buff + 21) ^ 0x45a9278d6f0be1c3;
            ....
           }

So the flag has : _1s_7ime in positions 21-28

The entire flag is : y0u_w1l|_w4lk_wh3n_1t_1s_7ime_t0_w4lk;)

Original writeup (https://github.com/abinpaul1/CTF-Writeups/blob/master/SuSec%20CTF%202020%20-%20Moonwalk/README.md).