Skip to main content

Phase 9 (350 pts)

I did not managed to solve this challenge during the competition but a few hints from those on Discord as well as the challenge author had guided me to the solution.

Problem Statement#

Who doesn't <3 arrays?
Author: treap_treap

Ghidra analysis#

undefined4phase9(undefined8 param_1,undefined4 param_2,undefined4 param_3,undefined4 param_4,      undefined4 param_5,undefined4 param_6,undefined4 param_7,undefined4 param_8,char *param_9)
{  char *inp;  code *ptr;  size_t __n;  long lVar1;  undefined8 *puVar2;  undefined8 *puVar3;  undefined8 in_R8;  undefined8 in_R9;  long in_FS_OFFSET;  byte bVar4;  undefined4 extraout_XMM0_Da;  undefined in_stack_fffffffffffffbf8;  undefined4 local_3e8;  int local_3e4;  uint local_3e0;  char *local_3d8;  char *local_3d0;  undefined local_3a8 [48];  undefined8 local_378 [109];  long local_10;
  bVar4 = 0;  local_10 = *(long *)(in_FS_OFFSET + 0x28);  puts("\nWho doesn\'t <3 arrays?");  local_3e8 = 1;  local_3e4 = d;  lVar1 = 0x6c;  puVar2 = &DAT_00102c40;  puVar3 = local_378;  while (lVar1 != 0) {    lVar1 = lVar1 + -1;    *puVar3 = *puVar2;    puVar2 = puVar2 + (ulong)bVar4 * -2 + 1;    puVar3 = puVar3 + (ulong)bVar4 * -2 + 1;  }  *(undefined2 *)puVar3 = *(undefined2 *)puVar2;  inp = (char *)calloc(0x29,1);  getInput(extraout_XMM0_Da,param_2,param_3,param_4,param_5,param_6,param_7,param_8,9,param_9,           &DAT_001028d1,inp,in_R8,in_R9,in_stack_fffffffffffffbf8);  ptr = (code *)valloc(0x362);  local_3e0 = 0;  while (local_3e0 < 0x362) {    if ((local_3e4 == 0) || (*(char *)((long)local_378 + (long)(int)local_3e0) == '\0')) {      local_3e4 = 0;    }    else {      local_3e4 = 1;    }    *(byte *)((long)local_378 + (long)(int)local_3e0) =         *(byte *)((long)local_378 + (long)(int)local_3e0) >> 2 |         *(char *)((long)local_378 + (long)(int)local_3e0) << 6;    ptr[(int)local_3e0] = (code)(*(char *)((long)local_378 + (long)(int)local_3e0) + 5U ^ 0x77);    *(undefined *)((long)local_378 + (long)(int)local_3e0) = 1;    local_3e0 = local_3e0 + 1;  }  mprotect(ptr,0x362,6);  local_3d8 = (char *)(*ptr)(local_3a8,local_3e4,local_3a8,local_3e4);  local_3d0 = inp;  while ((*local_3d8 != '\0' && (*local_3d0 != '\0'))) {    if (*local_3d8 != *local_3d0) {      local_3e8 = 0;      goto LAB_001021ce;    }    local_3d8 = local_3d8 + 1;    local_3d0 = local_3d0 + 1;  }  if (*local_3d8 != '\0') {    local_3e8 = 0;  }LAB_001021ce:  __n = sysconf(0x1e);  memset(ptr,0,__n);  free(ptr);  free(inp);  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {                    /* WARNING: Subroutine does not return */    __stack_chk_fail();  }  return local_3e8;}

Solution#

The solution might be more straightforward than the code seems.

Initial Approach#

During the competition, it can be understood that certain bytes were transformed into certain shellcode which is called later via the CALL $RAX command.

Stepping through the binary in GDB, we stop at the comparison phase and see the supposed answer.

fake flag

Thinking that was the answer, I submitted it and unfortunately the flag was incorrect...

Actual solve#

After the competition, many feedback they had to flip a single bit to get the answer via dynamic analysis. With some other hints, I realised the bit might be a check to see if ptrace() is allowed.

This means that the string to be compared will always be wrong if the binary detects that there is a debugger attached to it.

Using some simple tricks easily found online, we can trick the binary to think that it is not being debugged and reveal the actual flag.

Actual flag

Flag#

DawgCTF{3X3cu71nG_4RrAy5_F0r_fUn}

Closing#

With that, we have come to the end of the binary bomb series. Even though I am not able to complete the entire series during the competition, the post-competition learning is very fruitful.

Cracking the binary bomb