Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
..
Failed to load latest commit information.
200
README.md

README.md

DEFKTHON CTF: Reversing 200

Description:

Segmentation Fault

Write-up

This write-up is made by the HacknamStyle CTF team.

The file is a normal Linux 32-bit ELF binary. Open it up in IDA and we see theres a main function and a lol function. The reverse engineered main function corresponds to the code below. I inlined several comments to explain what the code is trying to accomplish.

int main()
{
  int pipefd[2];
  int pid;
  int userinput;
  char kidid[30];

  // Create pipe for communication between child and parent
  pipe(pipefd);

  pid = fork();
  if ( !pid )
  {
    // ----- Start Child Process -----

    // Send Kid ID to the parent process using the pipe
    puts("\nDamn!! i lost my Kid's ID");
    write(pipefd[1], "42447255344574653276838751", 29);

    // Fake segmentation fault message and exit
    puts("Segmentation Fault");
    exit(0);
  }

  // ----- Start Parent Process -----

  // User must enter pid of the child process ("lost my Kid's ID")
  read(pipefd[0], kidid, 29);
  scanf("%d", &userinput);
  if ( userinput == pid )
  {
    // Did the user place a breakpoint on the lol() function? If so, exit.
    if ( *((char *)lol + 3) == 0xCC ) {
      puts(":D");
      exit(1);
    }

    // Got the kid ID from child process, now derive the key from it
    printf("\nGo for the Key!\n ");
    lol(kidid);
  }

  wait(0);
  return 0;
}

So the child sends an ID to the parent and then exits. The parent does a simple anti-debugging check by seeing if the user placed a breakpoint on the main function. Recall that 0xCC corresponds to an int3 instruction, which is a software breakpoint. A debugger normally places this instruction after the function prologue (hence the +3).

Using the latest IDA version we can remove the userinput == pid if-test using the Edit -> Patch Program -> "Change byte" and "Apply patches to input file". Replacing it with NOPs gives:

call    ___isoc99_scanf
mov     eax, [esp+24h]
cmp     eax, [esp+20h]
nop
nop
mov     eax, offset lol

While we’re at it, let’s also remove the call to scanf. Now we don’t need to worry anymore about giving the correct input. The lol function performs some calculations and ends with the following if-test:

mov     [ebp+var_C], 0
cmp     [ebp+var_C], 1
jnz     short _nope_not_this

mov     eax, offset format ; "%s"
lea     edx, [ebp+var_13]
mov     [esp+4], edx
mov     [esp], eax      ; format
call    _printf
jmp     short _return

_nope_not_this:
mov     eax, offset aNope_not_this_ ; ASCII string "Nope_Not_This_:("
mov     [esp], eax      ; format
call    _printf

In other words, it always jumps to \_nope\_not\_this, because it’s a hardcoded if-test that compares 0 with 1. If we again patch away this if-test using NOPs it will printf some string that was generated by the function. This string turns out to be the solution to the challenge: dignige.

Other write-ups and resources