Crackme 6: ELF, no ptrace
Link: https://www.root-me.org/en/Challenges/Cracking/ELF-Ptrace (binary)
This program has some protections against debugging, and running it in a debugger fails immediately:
$ /re/crackme/6/ch3.bin
############################################################
## Bienvennue dans ce challenge de cracking ##
############################################################
Password : test
Wrong password.
$ gdb /re/crackme/6/ch3.bin
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
[...]
Reading symbols from /re/crackme/6/ch3.bin...(no debugging symbols found)...done.
(gdb) r
Starting program: /re/crackme/6/ch3.bin
Debugger detecté ... Exit
[Inferior 1 (process 534) exited with code 01]
Disassembling main
in Hopper or gdb
shows a copy of a string into the local variable at ebp
- 0xc. Let’s call it secret
.
mov dword [ebp-0xc], aKsuiealohgy ; "ksuiealohgy"
Then we see that if a call to ptrace
fails, we stop the program:
push 0x0
push 0x1
push 0x0
push 0x0 ; argument "__request" for method ptrace
call ptrace ; ptrace
add esp, 0x10
test eax, eax
jns loc_8048436
An easy way to skip this would be to replace this code with NOPs (no-ops) and the jump from a conditional to an unconditional jump.
Once we’ve bypassed this step, the control flow heads to a block where a password is read using fgets
and stored in a local variable at ebp
- 0x16.
lea eax, dword [ebp-0x16]
push eax ; argument #1 for method _IO_fgets
call _IO_fgets ; _IO_fgets
and we jump into some code right after a label named _notng
:
lea eax, dword [_notng] ; _notng
inc eax
jmp eax
The beginning of notng
is kind of strange, and gets to a HLT
instruction which will stop the program:
08048497 mov eax, 0x8bea558a
0804849c inc ebp
0804849d hlt
But here we’re going to eax
+ 1, so we can re-write the first byte as a NOP
and ask Hopper to re-interpret the rest. Much better! We now have something that makes much more sense. Here’s our first byte, skipped:
_notng:
08048497 nop
And the rest at 0x08048498 is now comparing the two local buffers. dl
successively takes each byte of our input, and al
the expected bytes. Here, if input[0]
!= secret[4]
(’e’), we fail:
08048498 mov dl, byte [ebp-0x16]
0804849b mov eax, dword [ebp-0xc]
0804849e add eax, 0x4
080484a1 mov al, byte [eax]
080484a3 cmp dl, al
080484a5 jne sub_80484e4
Then input[1]
(ebp
- 0x16 + 1) is compared to secret[5]
(‘a’)
080484a7 mov dl, byte [ebp-0x15]
080484aa mov eax, dword [ebp-0xc]
080484ad add eax, 0x5
080484b0 mov al, byte [eax]
080484b2 cmp dl, al
080484b4 jne sub_80484e4
Then input[2]
to secret[1]
(’s’) (notice the inc eax
):
080484b6 mov dl, byte [ebp-0x14]
080484b9 mov eax, dword [ebp-0xc]
080484bc inc eax
080484bd mov al, byte [eax]
080484bf cmp dl, al
080484c1 jne sub_80484e4
And finally input[3]
to secret[10]
(‘y’):
080484c3 mov dl, byte [ebp-0x13]
080484c6 mov eax, dword [ebp-0xc]
080484c9 add eax, 0xa
080484cc mov al, byte [eax]
080484ce cmp dl, al
080484d0 jne sub_80484e4
Let’s try it:
$ /re/crackme/6/ch3.bin
############################################################
## Bienvennue dans ce challenge de cracking ##
############################################################
Password : easy
Good password !!!