– First of all, execute the binary ( ‘coz I know that’s not a malware) :
$ ./crackme1 usage: ./crackme1 $ ./crackme1 thegame Bad password! Try again running as root maybe?
– Well, let’s try with gdb !
$ gdb ./crackme1 gdb-peda$ start thegame gdb-peda$ ni // snip // 0x80486c0: add eax,0x4 0x80486c3: mov eax,DWORD PTR [eax] 0x80486c5: mov DWORD PTR [esp],eax => 0x80486c8: call 0x80484a0 <strlen@plt> 0x80486cd: mov DWORD PTR [esp+0x2c],eax 0x80486d1: add DWORD PTR [esp+0x2c],0x1337 0x80486d9: cmp DWORD PTR [esp+0x2c],0x1342 0x80486e1: je 0x80486fc Guessed arguments: arg: 0xffffd6e5 ("thegame")
– That’s very interesting ! This part of assembler code means :
– Call the function strlen
– Compare to 0x1337 (4919) + return(strlen) and 0x1342 (4930)
– Jump if egal
– To sum up, that means if (4919 + strlen(password) != 4930; => exit(). So,
I know that the password MUST BE a 11 bytes length string.
– Let’s have a look with this condition :
gdb-peda$ start aaaaaaaaaaa gdb-peda$ ni // snip => 0x80486e1: je 0x80486fc | 0x80486e3: mov eax,ds:0x804a048 | 0x80486e8: mov DWORD PTR [esp],eax | 0x80486eb: call 0x8048470 <puts@plt> | 0x80486f0: mov DWORD PTR [esp],0x1 |-> 0x80486fc: mov ebx,DWORD PTR ds:0x804a04c // The JUMP is taken, as expected ! \o/ gdb-peda$ ni 0x80485f2: call 0x80484d0 <calloc@plt> // A memory space is allocated // snip 0x804862c: shl ebx,0x2 0x804862f: add edx,ebx 0x8048631: mov edx,DWORD PTR [edx] 0x8048633: xor edx,ecx <== That is very interesting !! 0x8048635: mov DWORD PTR [eax],edx
– Now, I know that my input is XORED !
gdb-peda$ ni 0x8048719: call 0x8048430 <strcmp@plt>
– After that, if my XORed input password equals to the string hardcoded, I get the
flag. If not, I have the following message :
0xffffd480 --> 0x80487ec ("Bad password! Try again running as root maybe?")
– Well, many things are possibles now :
– ltrace the binary (That’s what I did)
– Use ld_preload to load my own strcmp()
– Coffee time (haha)
– Right now, let’s have a look to the binary with ltrace :
$ ltrace ./crackme1 $(python -c 'print "a"*11') __libc_start_main(0x804864a, 2, 0xffec8674, 0x8048740, 0x80487b0 getuid() geteuid() strlen("aaaaaaaaaaa") calloc(1, 12) strcmp("&\016\016\005A\003\016\030A[H", ">_\032;SR05\023\t\036") puts("Bad password! Try again running "...Bad password! Try again running as \ root maybe?) +++ exited (status 0) +++
Ok, as expected, I have the strcmp() display. The first argument is my input
password (aaaaaaaaaaa) XORed; the second argument is the harcoded string.
– Some theorie about XOR :
A ^ B = C, so A ^ C = B
Or, I know my input password, I know my input password XORed. I just need to XOR
them together (I use ipython) to find the key :
In : input = 'a'*11 In : xored = "&\016\016\005A\003\016\030A[H" In : ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(input, xored)) Out: 'Good boy :)'
– Well, « Good boy 🙂 » is the key that’s used to XOR. So, I have to take the second
argument (the hardcoded string) of the strlen() and XOR it with the key !
In : key = "Good boy :)" In : hardcoded = ">_\032;SR05\023\t\036" In : ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(hardcoded, key)) Out: 'y0u_s0_L337'
– Look very good ! It seems to be the expected input password.
Let’s try :
$ ./crackme1 y0u_s0_L337 Good boy 🙂
Flag : y0u_s0_L337
-> crackme1-w32 (thx to @Tishrom for his help)
– This writeup will not be detailed as the first (no time)
We have a function called WindowProc (sub_401579) which is the callback of the
– This function is waiting for WM_KEYDOWN (that means a touch was pushed)
cmp eax, 100h jz short loc_4015A9
– With the different conditions, I reconstructed the expected keystroke :
mov eax, [ebp+arg_8] cmp eax, 4Dh // M jz loc_40179A
– After that, there is a comparison with the variable ‘i’. If it’s bad, it begins
– That means :
Flag : BR34K_MY_K3YL0G
Let’s have a look to the amazing write-up from tlk about the crackme3 :