All writeups.

This is a walkthrough for the CrackMe0x00A that was part of the Rensselaer Polytechnic Institute’s Binary Exploitation course in Spring 2015. You can follow along by downloading the challenges here. (mirror 1, mirror 2)

Let’s start by running the binary.

We can tell that this program is checking for a password and that something else would probably happen if we input the correct password. Let’s go for the low hanging fruit – strings.

$ strings ./crackme0x00a
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
__isoc99_scanf
puts
__stack_chk_fail
printf
strcmp
__libc_start_main
GLIBC_2.7
GLIBC_2.4
GLIBC_2.0
PTRh
D$,1
T$,e3
UWVS
[^_]
Enter password: 
Congrats!
Wrong!
;*2$"
g00dJ0B!
GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
crtstuff.c
__CTOR_LIST__
__DTOR_LIST__
__JCR_LIST__
__do_global_dtors_aux
completed.6086
dtor_idx.6088
frame_dummy
__CTOR_END__
__FRAME_END__
__JCR_END__
__do_global_ctors_aux
pass1.c
pass.1685
__init_array_end
_DYNAMIC
__init_array_start
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
strcmp@@GLIBC_2.0
__i686.get_pc_thunk.bx
data_start
printf@@GLIBC_2.0
_edata
_fini
__stack_chk_fail@@GLIBC_2.4
__DTOR_END__
__data_start
puts@@GLIBC_2.0
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_start_main@@GLIBC_2.0
__libc_csu_init
_end
_start
_fp_hw
__bss_start
main
_Jv_RegisterClasses
__isoc99_scanf@@GLIBC_2.7
_init

There are some interesting lines. Let’s try ‘g00dJ0B!’.

And there we have it – the password is ‘g00dJ0B!’. However, that’s an unsatisfying ending. Let’s take a deeper look at what’s actually happening. We can assume that something like the following is happening:

#include <stdio.h>
#include <strings.h>

int main(void) {

    int found = 0;

    while (!found) {

        char buffer[1024];
        scanf("%s", &amp;buffer);

        if (strcmp(buffer, "g00d J0B!") != 0) {
            printf("Wrong!");
        } else {
            printf("Congrats!");
            found = 1;
        }

    }

    return 0;

}

Let’s take a look through this in gdb.

$ gdb ./crackme0x00a
(gdb) disass main
Dump of assembler code for function main:
   0x080484e4 <+0>:	push   ebp
   0x080484e5 <+1>:	mov    ebp,esp
   0x080484e7 <+3>:	and    esp,0xfffffff0
   0x080484ea <+6>:	sub    esp,0x30
   0x080484ed <+9>:	mov    eax,gs:0x14
   0x080484f3 <+15>:	mov    DWORD PTR [esp+0x2c],eax
   0x080484f7 <+19>:	xor    eax,eax
   0x080484f9 <+21>:	mov    eax,0x8048640
   0x080484fe <+26>:	mov    DWORD PTR [esp],eax
   0x08048501 <+29>:	call   0x80483d0 <printf@plt>
   0x08048506 <+34>:	mov    eax,0x8048651
   0x0804850b <+39>:	lea    edx,[esp+0x13]
   0x0804850f <+43>:	mov    DWORD PTR [esp+0x4],edx
   0x08048513 <+47>:	mov    DWORD PTR [esp],eax
   0x08048516 <+50>:	call   0x8048420 <__isoc99_scanf@plt>
   0x0804851b <+55>:	lea    eax,[esp+0x13]
   0x0804851f <+59>:	mov    DWORD PTR [esp+0x4],eax
   0x08048523 <+63>:	mov    DWORD PTR [esp],0x804a024
   0x0804852a <+70>:	call   0x80483c0 <strcmp@plt>
   0x0804852f <+75>:	test   eax,eax
   0x08048531 <+77>:	jne    0x8048554 <main+112>
   0x08048533 <+79>:	mov    DWORD PTR [esp],0x8048654
   0x0804853a <+86>:	call   0x80483f0 <puts@plt>
   0x0804853f <+91>:	nop
   0x08048540 <+92>:	mov    eax,0x0
   0x08048545 <+97>:	mov    edx,DWORD PTR [esp+0x2c]
   0x08048549 <+101>:	xor    edx,DWORD PTR gs:0x14
   0x08048550 <+108>:	je     0x8048567 <main+131>
   0x08048552 <+110>:	jmp    0x8048562 <main+126>
   0x08048554 <+112>:	mov    DWORD PTR [esp],0x804865e
   0x0804855b <+119>:	call   0x80483f0 <puts@plt>
   0x08048560 <+124>:	jmp    0x80484f9 <main+21>
   0x08048562 <+126>:	call   0x80483e0 <__stack_chk_fail@plt>
   0x08048567 <+131>:	leave  
   0x08048568 <+132>:	ret    
End of assembler dump.

Jump statements are a good way to identify branching statements. We have a call to the function ‘strcmp’ at 0x0804852a and a conditional jump to 0x8048554 (at 0x08048531) if the strcmp is not equal – that is, 0x8048554 should handle if a wrong password is input. This is confirmed by 0x0804855b printing something, with a jump back to 0x80484f9 (at 0x08048560) where printf (0x08048501) and scanf (0x08048516) are being called in preparation for another password guess.

The other side of this is if the condition for the jump at 0x08048531 is not fulfilled, the program will continue to print something (0x0804853a) and exit (0x08048550 -> 0x08048567).

So now we know roughly what’s happening, we need to confirm what we are comparing against. Although the assembly does give us a memory address that may be interesting, we can’t really read it.

0x08048523 <+63>: mov DWORD PTR [esp],0x804a024

So to look at the memory address, lets go back to gdb.

$ gdb ./crackme0x00a
(gdb) x/s 0x804a024
0x804a024 <pass.1685>:	"g00dJ0B!"

And voila – by interpreting that memory address as a string, we get ‘g00dJ0B!’.

Radare2 does this really easily. We can just visually inspect the assembly and radare2 sets out everything for us. Input the commands below, and simply scroll down.

$ r2 ./crackme0x00a
[0x08048430]> vpp

And again, red text on the right shows that the password is once again ‘g00dJ0B!’.

If you’re happy with the results of CrackMe0x00A, it’s time to move onto CrackMe0x00B.