Description:
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
.