IOLI Crackme Writeup (in-progress)
(NOTE: this post is a work in-progress - I am still working on the set of 10 crackme challenges, and posting my answers as I go!)
Below are my solutions to the IOLI crackme challenges. A crackme is a program that asks the user to enter the correct password. Since these passwords are difficult to guess, the solution often requires the program to be reverse engineered.
This post is on the set of Linux binaries, but there are versions in Windows32 and Pocket PC as well. The crackmes can be downloaded from here. Inspiration for this post comes from koppian adventures.
I encourage readers to try the challenges themselves first, since I will be spoiling the answers.
Level 0
First, run the program.
$ ./crackme0x00
IOLI Crackme Level 0x00
Password: password1
Invalid Password!
Unsurprisingly, the password was incorrect. Let’s examine the printable strings:
$ strings
/lib/ld-linux.so.2
\_\_gmon\_start\_\_
libc.so.6
printf
strcmp
scanf
\_IO\_stdin\_used
\_\_libc\_start\_main
GLIBC\_2.0
PTRh
IOLI Crackme Level 0x00
Password:
250382
Invalid Password!
Password OK :)
...
We can see that the program calls the functions printf, strcmp, and scanf. Some of the strings printed are “IOLI Crackme Level 0x00” and “Password OK :)”. Additionally, there’s the value 250382, which seems out of place amongst the other infomation. Maybe this is the password?
$ ./crackme0x00
IOLI Crackme Level 0x00
Password: 250382
Password OK :)
Level 1
Run the program:
$ ./crackme0x01
IOLI Crackme Level 0x01
Password: 1234
Invalid Password!
I ran strings and ltrace, but didn’t find the password. Next, I loaded the binary in gdb to get a closer look at the main function. Below is the disassembly:
$ gdb -q ./crackme0x01
Reading symbols from ./crackme0x01...
(No debugging symbols found in ./crackme0x01)
(gdb) disass main
Dump of assembler code for function main:
0x080483e4 <+0>: push ebp
0x080483e5 <+1>: mov ebp,esp
0x080483e7 <+3>: sub esp,0x18
0x080483ea <+6>: and esp,0xfffffff0
0x080483ed <+9>: mov eax,0x0
0x080483f2 <+14>: add eax,0xf
0x080483f5 <+17>: add eax,0xf
0x080483f8 <+20>: shr eax,0x4
0x080483fb <+23>: shl eax,0x4
0x080483fe <+26>: sub esp,eax
0x08048400 <+28>: mov DWORD PTR \[esp\],0x8048528
0x08048407 <+35>: call 0x804831c <printf@plt>
0x0804840c <+40>: mov DWORD PTR \[esp\],0x8048541
0x08048413 <+47>: call 0x804831c <printf@plt>
0x08048418 <+52>: lea eax,\[ebp-0x4\]
0x0804841b <+55>: mov DWORD PTR \[esp+0x4\],eax
0x0804841f <+59>: mov DWORD PTR \[esp\],0x804854c
0x08048426 <+66>: call 0x804830c <scanf@plt>
0x0804842b <+71>: cmp DWORD PTR \[ebp-0x4\],0x149a
0x08048432 <+78>: je 0x8048442 <main+94>
0x08048434 <+80>: mov DWORD PTR \[esp\],0x804854f
0x0804843b <+87>: call 0x804831c <printf@plt>
0x08048440 <+92>: jmp 0x804844e <main+106>
0x08048442 <+94>: mov DWORD PTR \[esp\],0x8048562
0x08048449 <+101>: call 0x804831c <printf@plt>
0x0804844e <+106>: mov eax,0x0
0x08048453 <+111>: leave
0x08048454 <+112>: ret
End of assembler dump.
$ gdb -q ./crackme0x01 Reading symbols from ./crackme0x01… (No debugging symbols found in ./crackme0x01) (gdb) disass main Dump of assembler code for function main: 0x080483e4 <+0>: push ebp 0x080483e5 <+1>: mov ebp,esp 0x080483e7 <+3>: sub esp,0x18 0x080483ea <+6>: and esp,0xfffffff0 0x080483ed <+9>: mov eax,0x0 0x080483f2 <+14>: add eax,0xf 0x080483f5 <+17>: add eax,0xf 0x080483f8 <+20>: shr eax,0x4 0x080483fb <+23>: shl eax,0x4 0x080483fe <+26>: sub esp,eax 0x08048400 <+28>: mov DWORD PTR [esp],0x8048528 0x08048407 <+35>: call 0x804831c printf@plt 0x0804840c <+40>: mov DWORD PTR [esp],0x8048541 0x08048413 <+47>: call 0x804831c printf@plt 0x08048418 <+52>: lea eax,[ebp-0x4] 0x0804841b <+55>: mov DWORD PTR [esp+0x4],eax 0x0804841f <+59>: mov DWORD PTR [esp],0x804854c 0x08048426 <+66>: call 0x804830c scanf@plt 0x0804842b <+71>: cmp DWORD PTR [ebp-0x4],0x149a 0x08048432 <+78>: je 0x8048442 <main+94> 0x08048434 <+80>: mov DWORD PTR [esp],0x804854f 0x0804843b <+87>: call 0x804831c printf@plt 0x08048440 <+92>: jmp 0x804844e <main+106> 0x08048442 <+94>: mov DWORD PTR [esp],0x8048562 0x08048449 <+101>: call 0x804831c printf@plt 0x0804844e <+106>: mov eax,0x0 0x08048453 <+111>: leave 0x08048454 <+112>: ret End of assembler dump.
Lines <+0> through <+26> are setting up the stack, and can be ignored for solving the crackmes. The next several lines are printing the "Password: " prompt, and getting our input with scanf.
On line <+71> there is a comparison between the value in register $ebp-0x4 and 0x149a. Since $ebp-0x4 stores the text we entered, perhaps 0x149a stores the password? Let's print the value and check.
(gdb) p 0x149a $1 = 5274 (gdb) r Starting program: /home/kali/Documents/radare2-workshop-2015/IOLI-crackme/bin-linux/crackme0x01 IOLI Crackme Level 0x01 Password: 5274 Password OK :)
Level 2
-------
Same deal.
$ ./crackme0x02 IOLI Crackme Level 0x02 Password: ABCD Invalid Password!
Let's run gdb again (this time I'll only show the relevant info):
(gdb) disass main
setting up stack, print prompt, store user input
… 0x0804842b <+71>: mov DWORD PTR [ebp-0x8],0x5a 0x08048432 <+78>: mov DWORD PTR [ebp-0xc],0x1ec 0x08048439 <+85>: mov edx,DWORD PTR [ebp-0xc] 0x0804843c <+88>: lea eax,[ebp-0x8] 0x0804843f <+91>: add DWORD PTR [eax],edx 0x08048441 <+93>: mov eax,DWORD PTR [ebp-0x8] 0x08048444 <+96>: imul eax,DWORD PTR [ebp-0x8] 0x08048448 <+100>: mov DWORD PTR [ebp-0xc],eax 0x0804844b <+103>: mov eax,DWORD PTR [ebp-0x4] 0x0804844e <+106>: cmp eax,DWORD PTR [ebp-0xc] …
We see that the values 0x5a (90) and 0x1ec (492) are stored in registers $ebp-0x8 and $ebp-0xc respectfully. Then some addition (add) and multiplication (imul) instructions are executed. On line <+106> there is a comparison (cmp) done with the value stored in $ebp-0xc.
Let's display the value with **x/d** (examine as decimal):
(gdb) x/d $ebp-0xc 0xffffd23c: 338724
The value is 338724. Time to see if this is the right password!
$. /crackme0x02 IOLI Crackme Level 0x02 Password: 338724 Password OK :)
Level 3
-------