Solving Root-me Ptrace challenge

You can find the challenge from here. The challenge is to find the password for the elf 32 binary. This is how this looks at a glance.

View post on imgur.com


Let’s fire up GDB and check this out. I’ll break main and run. We can see that arguments to ptrace() function is being pushed on the stack and ptrace is being called.

=> 0x080483fe <+14>: sub esp,0x14
0x08048401 <+17>: mov DWORD PTR [ebp-0xc],0x80c2888
0x08048408 <+24>: push 0x0
0x0804840a <+26>: push 0x1
0x0804840c <+28>: push 0x0
0x0804840e <+30>: push 0x0
0x08048410 <+32>: call 0x8058a70 <ptrace>


Also notice “0x80c2888” address is being moved on to the stack frame as well. If we examine this it’s a string.

(gdb) x/s 0x80c2888
0x80c2888:	 "ksuiealohgy"

Okay, if we continue we get this message. Which means ptrace will detect our debugger and exit.

(gdb) c
Continuing.
Debugger detecté ... Exit
[Inferior 1 (process 2399) exited with code 01]
(gdb)

So ptrace is a little ant-reversing trick in *nix systems. For example we can implement a sample program like this which would print “Debugger Detected” if you run this using a debugger, if you run this normally it will print “Exited Normally” with returning 0.

[code language=”C”]
#include <stdio.h>
#include <sys/ptrace.h>

int main(){

if (ptrace(PTRACE_TRACEME, 0, 1, 0)) {
puts("Debugger Detected");
return 1;
}

puts("Exited Normally");
return 0;
}
[/code]

Now let’s hit a break point near ptrace and step over to the JNS instruction.

0x08048410 <+32>: call 0x8058a70 <ptrace>
0x08048415 <+37>: add esp,0x10
0x08048418 <+40>: test eax,eax
=> 0x0804841a <+42>: jns 0x8048436 <main+70>
0x0804841c <+44>: sub esp,0xc
0x0804841f <+47>: push 0x80c2894
0x08048424 <+52>: call 0x80492d0 <puts>

Once this calls ptrace it will return a negative value 0xFFFFFFFF in EAX.
We can see a Jump short if not sign (JNS) instruction which will jump only if the sign flag is set to 0. If check the flags we can see that SF is being set. We have to unclear this to bypass this checking. If not you can see the message “Debugger detecté … Exit” is being pushed on the stack and puts is being called.

(gdb) x/s 0x80c2894
0x80c2894:	 "Debugger detecté ... Exit"

The sign flag is the 7th bit so 2 ^ 7 = 128 = 0x80. We have to unclear this flag to make the jump. Let’s check the current flags.

(gdb)  i r $eflags
eflags         0x200286	[ PF SF IF ID ]

It seems SF is being set. To unclear this you can negate 0x80 and AND it or simply XOR 0x80.

(gdb) set ($ps)&=~0x80 // or set ($ps)^=0x80
(gdb) i r $eflags
eflags         0x200206	[ PF IF ID ]

This is simple arithmetic, yet to make our lives easier hasherezade has written a nice gdbinit script to manipulate flags. You can download it from here. To negate the flag just type “neg_flag ‘S’” 🙂

(gdb) flags 
 O  D  I  T  S  Z  *  A  *  P  *  C 
[ ][ ][X][ ][X][ ][ ][ ][ ][X][X][ ]
(gdb) neg_flag 'S'
 O  D  I  T  S  Z  *  A  *  P  *  C 
[ ][ ][X][ ][ ][ ][ ][ ][ ][X][X][ ]

Now we can continue to the logic of the application. Next comes the user input where enter the password.

0x08048436 <+70>: sub esp,0xc
0x08048439 <+73>: push 0x80c28b0
0x0804843e <+78>: call 0x80492d0 <puts>
0x08048443 <+83>: add esp,0x10
0x08048446 <+86>: sub esp,0xc
0x08048449 <+89>: push 0x80c28f0
0x0804844e <+94>: call 0x80492d0 <puts>
0x08048453 <+99>: add esp,0x10
0x08048456 <+102>: sub esp,0xc
0x08048459 <+105>: push 0x80c2930
0x0804845e <+110>: call 0x80492d0 <puts>
0x08048463 <+115>: add esp,0x10
0x08048466 <+118>: mov eax,0x80c296e
0x0804846b <+123>: sub esp,0xc
0x0804846e <+126>: push eax
0x0804846f <+127>: call 0x8048f60 <printf>
0x08048474 <+132>: add esp,0x10
0x08048477 <+135>: mov eax,ds:0x80e549c
0x0804847c <+140>: sub esp,0x4
0x0804847f <+143>: push eax
0x08048480 <+144>: push 0x9
0x08048482 <+146>: lea eax,[ebp-0x16]
0x08048485 <+149>: push eax
0x08048486 <+150>: call 0x8048f90 <fgets>
0x0804848b <+155>: add esp,0x10
0x0804848e <+158>: lea eax,ds:0x8048497

If we look at the disassembly after that can see that the entered the string characters is being compared with different characters with the string we found before “ksuiealohgy”.

=> 0x8048498 <main+168>: mov dl,BYTE PTR [ebp-0x16]
0x804849b <main+171>: mov eax,DWORD PTR [ebp-0xc]
0x804849e <main+174>: add eax,0x4
0x80484a1 <main+177>: mov al,BYTE PTR [eax]
0x80484a3 <main+179>: cmp dl,al
0x80484a5 <main+181>: jne 0x80484e4 <main+244>
0x80484a7 <main+183>: mov dl,BYTE PTR [ebp-0x15]
0x80484aa <main+186>: mov eax,DWORD PTR [ebp-0xc]
0x80484ad <main+189>: add eax,0x5
0x80484b0 <main+192>: mov al,BYTE PTR [eax]
0x80484b2 <main+194>: cmp dl,al
0x80484b4 <main+196>: jne 0x80484e4 <main+244>
0x80484b6 <main+198>: mov dl,BYTE PTR [ebp-0x14]
0x80484b9 <main+201>: mov eax,DWORD PTR [ebp-0xc]
0x80484bc <main+204>: inc eax
0x80484bd <main+205>: mov al,BYTE PTR [eax]
0x80484bf <main+207>: cmp dl,al
0x80484c1 <main+209>: jne 0x80484e4 <main+244>
0x80484c3 <main+211>: mov dl,BYTE PTR [ebp-0x13]
0x80484c6 <main+214>: mov eax,DWORD PTR [ebp-0xc]
0x80484c9 <main+217>: add eax,0xa
0x80484cc <main+220>: mov al,BYTE PTR [eax]
0x80484ce <main+222>: cmp dl,al
0x80484d0 <main+224>: jne 0x80484e4 <main+244>
0x80484d2 <main+226>: sub esp,0xc
0x80484d5 <main+229>: push 0x80c297a
0x80484da <main+234>: call 0x80492d0 <puts>
0x80484df <main+239>: add esp,0x10
0x80484e2 <main+242>: jmp 0x80484f4 <main+260>
0x80484e4 <main+244>: sub esp,0xc
0x80484e7 <main+247>: push 0x80c298e
0x80484ec <main+252>: call 0x80492d0 <puts>

If we keep stepping over you will notice that the dl register will contain the characters from the string we entered and the al register will contain the characters of the real password. Each time the cmp is comparing the 2 characters and if the comparison is true it will return which will set the zero flag. In case cmp fails the JNE instruction will jump to 0x80484e4 which means wrong password.

(gdb) x/3i 0x80484e4
0x80484e4 <main+244>: sub esp,0xc
0x80484e7 <main+247>: push 0x80c298e
0x80484ec <main+252>: call 0x80492d0 <puts>
(gdb) x/s 0x80c298e
0x80c298e: "\nWrong password.\n"

There are 4 comparisons going on which means the password is of length 4 🙂 For easiness I will define a hook to print the 8-bit al register in hex and to set the ZF flag 🙂

(gdb)  define hook-stop
>print/x $al
>set ($ps)|=0x60
>end

I have removed repeating results to save space.

gdb$ ni
$1 = 0x65
0x080484a5 in main ()
$6 = 0x61
0x080484b2 in main ()
gdb$ ni
$12 = 0x73
0x080484bf in main ()
gdb$ ni
$18 = 0x79
0x080484ce in main ()
gdb$ ni
Good password !!!

When we convert to 0x65617379 to ASCII the string is “easy” , so we found the password : )

Patching Ptrace

We can patch ptrace by simply modifying the binary. You can use a hex editor for this. I’ll be using the HT editor. Let’s look at the disassembly where ptrace is being called.

0x08048408 <+24>: push 0x0
0x0804840a <+26>: push 0x1
0x0804840c <+28>: push 0x0
0x0804840e <+30>: push 0x0
0x08048410 <+32>: call 0x8058a70 <ptrace>
0x08048415 <+37>: add esp,0x10
0x08048418 <+40>: test eax,eax
0x0804841a <+42>: jns 0x8048436 <main+70>

To make the test instruction unclear SF flag we eax should contain 0. After that the JNS will jump to the program logic. To do this we can modify the call instruction to a XOR EAX, EAX instruction and fill the rest with NOPs. The opcode for XOR EAX, EAX is 33 C0. If you are using the HT editor ^A will make you easily type the instruction.

View post on imgur.com

Now you can freely use gdb and start solving the challenge. 🙂 Here’s a short video.

For beginners I hope this write-up was useful. Thanks for reading!

7 thoughts on “Solving Root-me Ptrace challenge

  1. What OS you use or what is your gdb? why i use run on gdb it done show me display like you. Don’t show me
    [———————————-registers———————————–]

    [————————————-code————————————-]

    [————————————stack————————————-]
    when i run file.
    please help me. Thank you very much.

  2. What OS you use or what is your gdb? why i use run on gdb it done show me display like you. Don’t show me : registers, code, stack when i run file.
    please help me. Thank you very much.

Leave a Reply to administrastorsCancel reply