Right after publishing first crackme walkthrough , I have received the second one – more complex crackme from Geyslan .
It took me much more time to solve it.
The binary you can get here crack.me_.32.new_
As usually crackme asks for the password
[arno@centos ~]$ ./crack.me.32.new
Please tell me my password: testpwd
No! No! No! No! Try again.
Attempt to debug it – fails
[arno@centos ~]$ gdb -q ./crack.me.32.new
"/home/arno/crack.me.32.new": not in executable format: File format not recognized
Because ELF header is corrupted
[arno@centos ~]$ file ./crack.me.32.new
./crack.me.32.new: ELF 32-bit LSB executable, Intel 80386, (SYSV), dynamically linked (uses shared libs), corrupted section header size
[arno@centos ~]$ readelf -h crack.me.32.new
readelf: Error: Unable to seek to 0xffffffff for section headers
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0xffffffff
Entry point address: 0x8048500
Start of program headers: 52 (bytes into file)
Start of section headers: 4294967295 (bytes into file)
Flags: 0x0
Size of this header: 65535 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 65535 (bytes)
Number of section headers: 65535
Section header string table index: 65535
readelf: Error: Unable to seek to 0xffffffff for section headers
In the next attempt I have tried attaching GDB to a running process, obviously crackme had protection against that.
[arno@centos ~]$ ps -ef|grep crack
arno 1487 1209 0 19:42 pts/0 00:00:00 ./crack.me.32.new
[arno@centos ~]$ gdb -q -p 1487
Attaching to process 1487
ptrace: Operation not permitted.
Then I have tried to fake ptrace function, but it didn’t work. Obviously this crackme is a way improved than the first one.
[arno@centos ~]$ cat ptrace.c
int ptrace(int i, int j, int k, int l)
{
printf("fake ptrace called\n");
}
[arno@centos ~]$ gcc -shared -o ptrace.so ptrace.c
[arno@centos ~]$ strace -e ptrace -E LD_PRELOAD=/home/arno/ptrace.so ./crack.me.32.new
ptrace(PTRACE_TRACEME, 0, 0x1, 0) = -1 EPERM (Operation not permitted)
Tracing is not allowed... Bye!
The main idea here is to attach GDB to a process at the very first instruction of its execution – just right before anti-ptrace & anti-GDB functions will start. Knowing the entry point address 0x8048500 and proper offset, I could manage to loop the process.
[arno@centos ~]$ readelf -l ./crack.me.32.new
readelf: Error: Unable to seek to 0xffffffff for section headers
readelf: Error: Unable to seek to 0xffffffff for section headers
Elf file type is EXEC (Executable file)
Entry point 0x8048500
There are 9 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00b38 0x00b38 R E 0x1000
LOAD 0x000f04 0x08049f04 0x08049f04 0x00144 0x00188 RW 0x1000
DYNAMIC 0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW 0x4
NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x00099c 0x0804899c 0x0804899c 0x00054 0x00054 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x000f04 0x08049f04 0x08049f04 0x000fc 0x000fc R 0x1
To calculate the right offset, you need to subtract PhysAddr of LOAD (0x08048000) from the Entry point – 0x8048500 hex(0x8048500-0x08048000) = ‘0x500’ which is 1280 in decimal
Now it is time to find a good instruction to replace it with “jmp short $” – which is “EB FE” raw instruction in hex. $ – means current line. So basically “jmp short $” – makes a loop wherever it is executed.
Looking at the first set of instructions from the program’s entry point.
[arno@centos ~]$ cat crack.me.32.new |ndisasm -b32 -o 0x8048500 -e1280 - |head -14
08048500 31ED xor ebp,ebp
08048502 5E pop esi
08048503 89E1 mov ecx,esp
08048505 83E4F0 and esp,byte -0x10
08048508 50 push eax
08048509 54 push esp
0804850A 52 push edx
0804850B 68C0880408 push dword 0x80488c0
08048510 6850880408 push dword 0x8048850
08048515 51 push ecx
08048516 56 push esi
08048517 6869870408 push dword 0x8048769
0804851C E8AFFFFFFF call dword 0x80484d0
08048521 F4 hlt
I have picked “89 E1” at the address 08048503 to substitute with the EB FE (jmp short $).
[arno@centos ~]$ hexdump -C crack.me.32.new |grep --color '89 e1'
00000500 31 ed 5e 89 e1 83 e4 f0 50 54 52 68 c0 88 04 08 |1.^.....PTRh....|
# Changing 89E1 to EBFE with sed
[arno@centos ~]$ sed 's/\x89\xe1/\xeb\xfe/g' crack.me.32.new > crack.me.32.new.loop
[arno@centos ~]$ hexdump -C crack.me.32.new.loop |grep 00000500
00000500 31 ed 5e eb fe 83 e4 f0 50 54 52 68 c0 88 04 08 |1.^.....PTRh....|
# Before
[arno@centos ~]$ cat crack.me.32.new |ndisasm -b32 -o 0x8048500 -e1280 - |head -3
08048500 31ED xor ebp,ebp
08048502 5E pop esi
08048503 89E1 mov ecx,esp
# After the substitution
[arno@centos ~]$ cat crack.me.32.new.loop |ndisasm -b32 -o 0x8048500 -e1280 - |head -3
08048500 31ED xor ebp,ebp
08048502 5E pop esi
08048503 EBFE jmp short 0x8048503
Replaced. Now time to run and attach to it with the GDB.
[arno@centos ~]$ chmod +x ./crack.me.32.new.loop
[arno@centos ~]$ ./crack.me.32.new.loop
[arno@centos ~]$ ps -ef|grep crack.m[e]
arno 1813 1193 81 05:36 pts/0 00:00:39 ./crack.me.32.new.loop
[arno@centos ~]$ gdb -q -p 1813
Attaching to process 1813
"/home/arno/crack.me.32.new.loop": not in executable format: File format not recognized
(gdb) disassemble /r $eip-3, +31
Dump of assembler code from 0x8048500 to 0x804851f:
0x08048500: 31 ed xor ebp,ebp
0x08048502: 5e pop esi
=> 0x08048503: eb fe jmp 0x8048503
0x08048505: 83 e4 f0 and esp,0xfffffff0
0x08048508: 50 push eax
0x08048509: 54 push esp
0x0804850a: 52 push edx
0x0804850b: 68 c0 88 04 08 push 0x80488c0
0x08048510: 68 50 88 04 08 push 0x8048850
0x08048515: 51 push ecx
0x08048516: 56 push esi
0x08048517: 68 69 87 04 08 push 0x8048769
0x0804851c: e8 af ff ff ff call 0x80484d0
End of assembler dump.
Ok perfect, I have managed attaching the GDB to a running process and can see that it stuck at loop function I have hardcoded in binary.
Let’s now set the instruction back as it was before and continue.
Reverting the substitution which was done before, back to original instructions: “EB FE” (jmp short $) to “89 E1” (mov ecx,esp).
(gdb) print /x $pc
$1 = 0x8048503
(gdb) set $i = $pc
(gdb) set *(unsigned char*)$i++ = 0x89
(gdb) set *(unsigned char*)$i++ = 0xe1
(gdb) disassemble /r $eip-3, +31
Dump of assembler code from 0x8048500 to 0x804851f:
0x08048500: 31 ed xor ebp,ebp
0x08048502: 5e pop esi
=> 0x08048503: 89 e1 mov ecx,esp
0x08048505: 83 e4 f0 and esp,0xfffffff0
0x08048508: 50 push eax
0x08048509: 54 push esp
0x0804850a: 52 push edx
0x0804850b: 68 c0 88 04 08 push 0x80488c0
0x08048510: 68 50 88 04 08 push 0x8048850
0x08048515: 51 push ecx
0x08048516: 56 push esi
0x08048517: 68 69 87 04 08 push 0x8048769
0x0804851c: e8 af ff ff ff call 0x80484d0
End of assembler dump.
Okay, seems all is ready to continue with the GDB, however, the main problem was that there is no any information about the functions, the variables.
(gdb) bt
#0 0x00b3ba90 in ?? ()
#1 0x00b2db9a in ?? ()
#2 0x00b2e0da in ?? ()
#3 0x00b32a05 in ?? ()
#4 0x00b38c90 in ?? ()
#5 0x080488a2 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) info functions
All defined functions:
(gdb) info variables
All defined variables:
Without seeing the library calls description, functions -> it makes the program much harder and longer for debugging.
As you could see, attaching to a loop process didn’t help much. I have got a tip from the crackme author, that GDB easily takes a program with 0 ELF headers. Here is C code of elfzeroheaders.c program
// elfzeroheaders.c
// 2013 April
#include <elf.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys stat.h="None">
#include <sys types.h="None">
Elf32_Ehdr *ehead;
int main(int t, char **c)
{
int bin;
int bytes;
if (t < 2) {
printf("usage: %s \n", *c);
goto quit;
}
if ((bin = open(c[1], O_RDWR)) == -1)
goto quit;
ehead = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));
bytes = read(bin, ehead, sizeof(Elf32_Ehdr));
printf("read: %d bytes from %s\n", bytes, c[1]);
/* just a magic number check */
if (ehead->e_ident[0] != 0x7f && ehead->e_ident[1] != 0x45
&& ehead->e_ident[2] != 0x4c && ehead->e_ident[3] != 0x46) {
printf("not an ELF file. quitting…\n");
goto clean;
}
printf("e_shoff: %d\n", ehead->e_shoff);
printf("e_phentsize: %d\n", ehead->e_shentsize);
printf("e_shstrndx: %d\n", ehead->e_shnum);
printf("e_shoff: %d\n", ehead->e_shstrndx);
ehead->e_shoff = \ // Start of section headers
ehead->e_shentsize = \ // Size of section headers
ehead->e_shnum = \ // Number of section headers
ehead->e_shstrndx = 0; // Section header string table index
if (lseek(bin, 0, SEEK_SET) == -1) {
printf("error : ");
printf("%s\n", strerror(errno));
goto clean;
}
bytes = write(bin, ehead, sizeof(Elf32_Ehdr));
printf("wrote: %d bytes to %s\n", bytes, c[1]);
clean:
free(ehead);
close(bin);
quit:
return 0;
}
Compile and run it
[arno@centos ~]$ gcc -o elfzeroheaders elfzeroheaders.c
[arno@centos ~]$ ./elfzeroheaders crack.me.32.new
read: 52 bytes from crack.me.32.new
e_shoff: -1
e_phentsize: 65535
e_shstrndx: 65535
e_shoff: 65535
wrote: 52 bytes to crack.me.32.new
Check the headers now
[arno@centos ~]$ readelf -h crack.me.32.new |grep -w 0
ABI Version: 0
Start of section headers: 0 (bytes into file)
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
And now GDB takes it!
[arno@centos ~]$ gdb -q ./crack.me.32.new
Reading symbols from /home/arno/crack.me.32.new...
warning: no loadable sections found in added symbol-file /home/arno/crack.me.32.new
(no debugging symbols found)...done.
(gdb) run
I'm sorry GDB! You are not allowed!
However, I was blocked by the anti-GDB protection.
Right before telling you how I bypassed the anti-GDB protection, let me briefly tell you about great extension PEDA I have discovered in meanwhile.
PEDA is Python Exploit Development Assistance for GDB. You can get it here http://ropshell.com/peda/
To not go deep into discussion about the PEDA here, you can briefly read what it can do here Pimp my gdb with PEDA and see PEDA’s presentation
Here is my GDB config for PEDA.
[arno@centos ~]$ cat .gdbinit
source ~/peda/peda.py
set disassembly-flavor intel
set disable-randomization
define hook-stop
#disassemble $eip, +25
#context stack
#context reg
context code
end
I will show you three methods of bypasssing anti-GDB protection. The first one is to close additional file descriptors (fd later) that GDB opens while debugging the program. Most of anti-GDB protections are checking amount of opened fd’s.
[arno@centos ~]$ gdb -q ./crack.me.32.new
Reading symbols from /home/arno/crack.me.32.new...
warning: no loadable sections found in added symbol-file /home/arno/crack.me.32.new
(no debugging symbols found)...done.
gdb-peda$ shell readelf -h ./crack.me.32.new |grep Entry
Entry point address: 0x8048500
gdb-peda$ b *0x8048500
Breakpoint 1 at 0x8048500
gdb-peda$ r
[-------------------------------------code-------------------------------------]
0x80484f0: jmp DWORD PTR ds:0x804a030
0x80484f6: push 0x48
0x80484fb: jmp 0x8048450
=> 0x8048500: xor ebp,ebp
0x8048502: pop esi
0x8048503: mov ecx,esp
0x8048505: and esp,0xfffffff0
0x8048508: push eax
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048500 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.7.i686
gdb-peda$ b __libc_start_main
Breakpoint 2 at 0x147c06
gdb-peda$ c
[-------------------------------------code-------------------------------------]
0x147c03 <__libc_start_main+3>: push edi
0x147c04 <__libc_start_main+4>: push esi
0x147c05 <__libc_start_main+5>: push ebx
=> 0x147c06 <__libc_start_main+6>: call 0x147b1f <__i686.get_pc_thunk.bx>
0x147c0b <__libc_start_main+11>: add ebx,0x17b3e9
0x147c11 <__libc_start_main+17>: sub esp,0x6c
0x147c14 <__libc_start_main+20>: mov esi,DWORD PTR [ebp+0x14]
0x147c17 <__libc_start_main+23>: mov eax,DWORD PTR [ebp+0x1c]
Guessed arguments:
arg[0]: 0x12efc4 --> 0x1eefc
arg[1]: 0x1
arg[2]: 0x8048500 (xor ebp,ebp)
arg[3]: 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x00147c06 in __libc_start_main () from /lib/libc.so.6
gdb-peda$ procinfo
exe = /home/arno/crack.me.32.new
fd[0] -> /dev/pts/0
fd[1] -> /dev/pts/0
fd[2] -> /dev/pts/0
fd[3] -> pipe:[170492]
fd[4] -> pipe:[170492]
fd[5] -> pipe:[170493]
fd[6] -> pipe:[170493]
pid = 1726
ppid = 1721
uid = [500, 500, 500, 500]
gid = [500, 500, 500, 500]
gdb-peda$ call close(3)
$1 = 0x0
gdb-peda$ call close(4)
$2 = 0x0
gdb-peda$ call close(5)
$3 = 0x0
gdb-peda$ procinfo
exe = /home/arno/crack.me.32.new
fd[0] -> /dev/pts/0
fd[1] -> /dev/pts/0
fd[2] -> /dev/pts/0
fd[6] -> pipe:[170493]
pid = 1726
ppid = 1721
uid = [500, 500, 500, 500]
gid = [500, 500, 500, 500]
gdb-peda$ c
Tracing is not allowed... Bye!
Program exited with code 01.
Warning: not running or target is remote
Voila! I have just bypassed anti-GDB protection, but still see the ptrace one.
Right before showing you how I bypassed the anti-ptrace protection, I’m going to show the 2nd method of bypassing the anti-GDB one. The second one is to change amount of opened fd’s to a lower number than protection mechanism checks!
[arno@centos ~]$ gdb -q ./crack.me.32.new
Reading symbols from /home/arno/crack.me.32.new...
warning: no loadable sections found in added symbol-file /home/arno/crack.me.32.new
(no debugging symbols found)...done.
gdb-peda$ b *0x8048500
Breakpoint 1 at 0x8048500
gdb-peda$ r
[-------------------------------------code-------------------------------------]
0x80484f0: jmp DWORD PTR ds:0x804a030
0x80484f6: push 0x48
0x80484fb: jmp 0x8048450
=> 0x8048500: xor ebp,ebp
0x8048502: pop esi
0x8048503: mov ecx,esp
0x8048505: and esp,0xfffffff0
0x8048508: push eax
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x08048500 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.7.i686
gdb-peda$ b fileno
Breakpoint 2 at 0x199513
gdb-peda$ c
[-------------------------------------code-------------------------------------]
0x19950f: nop
0x199510 <fileno_unlocked>: push ebp
0x199511 <fileno_unlocked+1>: mov ebp,esp
=> 0x199513 <fileno_unlocked+3>: mov eax,DWORD PTR [ebp+0x8]
0x199516 <fileno_unlocked+6>: call 0x2551f8 <__i686.get_pc_thunk.cx>
0x19951b <fileno_unlocked+11>: add ecx,0x129ad9
0x199521 <fileno_unlocked+17>: mov edx,DWORD PTR [eax]
0x199523 <fileno_unlocked+19>: and dh,0x20
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 2, 0x00199513 in fileno_unlocked () from /lib/libc.so.6
gdb-peda$ bt
#0 0x00199513 in fileno_unlocked () from /lib/libc.so.6
#1 0x08048614 in ?? ()
#2 0x080488a2 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
gdb-peda$ finish
[-------------------------------------code-------------------------------------]
0x8048609: mov eax,DWORD PTR [ebp-0xc]
0x804860c: mov DWORD PTR [esp],eax
0x804860f: call 0x80484f0
=> 0x8048614: cmp eax,0x5
0x8048617: jle 0x8048631
0x8048619: mov DWORD PTR [esp],0x80488f0
0x8048620: call 0x80484a0
0x8048625: mov DWORD PTR [esp],0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048614 in ?? ()
gdb-peda$ print /x $eax
$1 = 0x7
gdb-peda$ set $eax=0x4
gdb-peda$ print /x $eax
$2 = 0x4
gdb-peda$ c
Tracing is not allowed... Bye!
Program exited with code 01.
Warning: not running or target is remote
gdb-peda$
The third method is to bypass the call to the anti-GDB function itself and continue they way I want.
[arno@centos ~]$ gdb -q ./crack.me.32.new
Reading symbols from /home/arno/crack.me.32.new...
warning: no loadable sections found in added symbol-file /home/arno/crack.me.32.new
(no debugging symbols found)...done.
gdb-peda$ b *0x804860f
Breakpoint 1 at 0x804860f
gdb-peda$ r
[-------------------------------------code-------------------------------------]
0x8048606: mov DWORD PTR [ebp-0xc],eax
0x8048609: mov eax,DWORD PTR [ebp-0xc]
0x804860c: mov DWORD PTR [esp],eax
=> 0x804860f: call 0x80484f0
0x8048614: cmp eax,0x5
0x8048617: jle 0x8048631
0x8048619: mov DWORD PTR [esp],0x80488f0
0x8048620: call 0x80484a0
Guessed arguments:
arg[0]: 0x804b008 --> 0xfbad2488
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x0804860f in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.7.i686
gdb-peda$ print /x $pc
$1 = 0x804860f
gdb-peda$ set $pc=0x8048631
gdb-peda$ context code
[-------------------------------------code-------------------------------------]
0x8048620: call 0x80484a0
0x8048625: mov DWORD PTR [esp],0x1
0x804862c: call 0x80484c0
=> 0x8048631: mov eax,DWORD PTR [ebp-0xc]
0x8048634: mov DWORD PTR [esp],eax
0x8048637: call 0x8048470
0x804863c: pusha
0x804863d: jmp 0x8048641
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
gdb-peda$ c
Tracing is not allowed... Bye!
Next run, I was looking for where the anti-ptrace protection is and how could I bypass it. So right after bypassing the anti-GDB protection, I run into many stepi’s, while watching functions called, the backtrace, trying to understand what is going on.
While going through on and on, I have managed to figure out couple of further calls are exactly parts of the anti-ptrace protection
0x8048683: mov DWORD PTR [esp],0x8048914
=> 0x804868a: call 0x80484a0 0x804868f: mov DWORD PTR [esp],0x1
0x8048696: call 0x80484c0 and quits program
0x804869b: leave
0x804869c: ret
So basically what I have decided is to try skipping both calls.
gdb -q ./crack.me.32.new
b *0x8048500
r
b fileno
c
finish
set $eax=0x4
gdb-peda$ b *0x804868a
Breakpoint 3 at 0x804868a
gdb-peda$ c
[-------------------------------------code-------------------------------------]
0x804867f: test eax,eax
0x8048681: jns 0x804869b
0x8048683: mov DWORD PTR [esp],0x8048914
=> 0x804868a: call 0x80484a0
0x804868f: mov DWORD PTR [esp],0x1
0x8048696: call 0x80484c0
0x804869b: leave
0x804869c: ret
Guessed arguments:
arg[0]: 0x8048914 (push esp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 3, 0x0804868a in ?? ()
gdb-peda$ set $pc=0x804869b
gdb-peda$ c
Please tell me my password:
Great! Finally I have broken both protections and going to find the real password.
Now I have set of commands that helps to get through quicker
gdb -q ./crack.me.32.new
b *0x8048500
r
b fileno
c
finish
set $eax=0x4
b *0x804868a
c
set $pc=0x804869b
si
Then going through many steps (si’s), I have found out that “fgets” call starts here => 0x804879e: call 0x8048490
gdb-peda$ bt
#0 0x00191dbf in fgets () from /lib/libc.so.6
#1 0x080487c0 in ?? ()
#2 0x00147ce6 in __libc_start_main () from /lib/libc.so.6
#3 0x08048521 in ?? ()
While continuing, I could notice that crackme runs some counter that repeats at the 0x804870a address, so it always compares increasing $EDX value that starts from 0, with 0×8 (8 decimal), and if it is less or equal to 8, it continues to increase the counter. In fact that was C function “for (i = 0; i < 9; i++)”.
0x191ec7 <fgets+311>: pop ebp
=> 0x191ec8 <fgets+312>: ret
...
...
...
=> 0x804870a: cmp DWORD PTR [ebp-0x8],0x8
0x804870e: jle 0x80486e5
0x8048710: add esp,0x14
0x8048713: pop ebx
0x8048714: pop ebp
So I set the breakpointer at 0x804870a address, redefined the hook-stop and continued watching the registers while counter was increasing. The password I used is “testing”.
gdb-peda$ b *0x804870a
Breakpoint 4 at 0x804870a
gdb-peda$ c
Please tell me my password: testing
[-------------------------------------code-------------------------------------]
0x80486ff: call 0x804869d
0x8048704: mov BYTE PTR [ebx],al
0x8048706: add DWORD PTR [ebp-0x8],0x1
=> 0x804870a: cmp DWORD PTR [ebp-0x8],0x8
0x804870e: jle 0x80486e5
0x8048710: add esp,0x14
0x8048713: pop ebx
0x8048714: pop ebp
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 4, 0x0804870a in ?? ()
gdb-peda$ define hook-stop
>context reg
>context code
>end
gdb-peda$ c
[----------------------------------registers-----------------------------------]
EAX: 0xf4
EBX: 0xbffff691 --> 0x747365f4
ECX: 0x0
EDX: 0x0
ESI: 0x0
EDI: 0x0
EBP: 0xbffff678 --> 0xbffff6a8 --> 0xbffff728 --> 0x0
ESP: 0xbffff660 --> 0x74 ('t')
EIP: 0x804870a (cmp DWORD PTR [ebp-0x8],0x8)
EFLAGS: 0x200202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
[-------------------------------------code-------------------------------------]
0x80486ff: call 0x804869d
0x8048704: mov BYTE PTR [ebx],al
0x8048706: add DWORD PTR [ebp-0x8],0x1
=> 0x804870a: cmp DWORD PTR [ebp-0x8],0x8
0x804870e: jle 0x80486e5
0x8048710: add esp,0x14
0x8048713: pop ebx
0x8048714: pop ebp
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 4, 0x0804870a in ?? ()
I will strip unnecessary lines to keep only the important ones
gdb-peda$ c
EAX: 0xf5
EDX: 0x1
ESP: 0xbffff660 --> 0x65 ('e')
gdb-peda$ c
EAX: 0xf3
EDX: 0x2
ESP: 0xbffff660 --> 0x73 ('s')
gdb-peda$ c
EAX: 0xf4
EDX: 0x3
ESP: 0xbffff660 --> 0x74 ('t')
gdb-peda$ c
EAX: 0xf9
EDX: 0x4
ESP: 0xbffff660 --> 0x69 ('i')
gdb-peda$ c
EAX: 0xfe
EDX: 0x5
ESP: 0xbffff660 --> 0x6e ('n')
gdb-peda$ c
EAX: 0xf7
EDX: 0x6
ESP: 0xbffff660 --> 0x67 ('g')
gdb-peda$ c
EAX: 0x9a
EDX: 0x7
ESP: 0xbffff660 --> 0xa ('\n')
So here is what the crackme did to a “testing” word that I enteret as a password
t 0x74 -> 0xf4
e 0x65 -> 0xf5
s 0x73 -> 0xf3
t 0x74 -> 0xf4
i 0x69 -> 0xf9
n 0x6e -> 0xfe
g 0x67 -> 0xf7
\n 0xa -> 0x9a
Trying to reverse the operation
>>> hex(0xf4-0x74) = '0x80'
>>> hex(0xf5-0x65) = '0x90'
>>> hex(0xf3-0x73) = '0x80'
>>> hex(0xf4-0x74) = '0x80'
>>> hex(0xf9-0x69) = '0x90'
>>> hex(0xfe-0x6e) = '0x90'
>>> hex(0xf7-0x67) = '0x90'
>>> hex(0x9a-0xa) = '0x90'
It was not clear at the beginning what operation exactly was applied, however after awhile I have found that it was operator OR.
>>> hex(0x90|0x74)= '0xf4'
>>> hex(0x90|0x65)= '0xf5'
>>> hex(0x90|0x73)= '0xf3'
>>> hex(0x90|0x74)= '0xf4'
>>> hex(0x90|0x69)= '0xf9'
>>> hex(0x90|0x6e)= '0xfe'
>>> hex(0x90|0x67)= '0xf7'
>>> hex(0x90|0xa)= '0x9a'
Continuing with stepi’s I have noticed following interesting address
=> 0x80487cc: mov DWORD PTR [esp+0x4],0x804a03c
0x80487d4: lea eax,[esp+0x11]
0x80487d8: mov DWORD PTR [esp],eax
0x80487db: call 0x8048716
gdb-peda$ x/3xw 0x804a03c
0x804a03c: 0xf4f1f8f7 0xfcb3f8f1 0x000000fc
In fact this was the address (0x804a03c) of encrypted real password, however I was 100% sure only while continuing
gdb-peda$ stepi
[----------------------------------registers-----------------------------------]
EAX: 0xf7
EBX: 0x2c2ff4 --> 0x191d7c (<_L_unlock_251+3>: push eax)
ECX: 0x0
EDX: 0xf4
ESI: 0x0
EDI: 0x0
EBP: 0xbffff678 --> 0xbffff6a8 --> 0xbffff728 --> 0x0
ESP: 0xbffff678 --> 0xbffff6a8 --> 0xbffff728 --> 0x0
EIP: 0x8048743 (cmp dl,al)
EFLAGS: 0x200286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
[-------------------------------------code-------------------------------------]
0x804873a: movzx edx,BYTE PTR [eax]
0x804873d: mov eax,DWORD PTR [ebp+0xc]
0x8048740: movzx eax,BYTE PTR [eax]
=> 0x8048743: cmp dl,al
0x8048745: je 0x804871b
0x8048747: mov eax,DWORD PTR [ebp+0x8]
0x804874a: movzx eax,BYTE PTR [eax]
0x804874d: test al,al
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048743 in ?? ()
You can see above $EDX is the first encrypted byte of the password “testing” and $EAX is the 1st byte of the encrypted real password. Setting the breakpointer on 0x8048743 and continuing, but also not forgetting to set $edx=$eax each time before continue, otherwise crackme will exit saying that the password is incorrect.
gdb-peda$ b *0x8048743
Breakpoint 6 at 0x8048743
gdb-peda$ set $edx=$eax
gdb-peda$ c
Doing so, I collected encrypted password which is 0xf7,0xf1,0xf4,0xf1,0xf8,0xb3,0xfc,0xfc.
I used python to XOR the password with 0×90.
>>> key=[0xf7,0xf8,0xf1,0xf4,0xf1,0xf8,0xb3,0xfc,0xfc]
>>> keydecr=[0,0,0,0,0,0,0,0,0]
>>>
>>>
>>> for i in range(len(key)):
... keydecr[i]=key[i] ^ 0x90
...
>>> print ''.join(str('%0.2X' % n).decode("hex") for n in keydecr)
ghadah#ll
[arno@centos ~]$ ./crack.me.32.new
Please tell me my password: ghadah#ll
The password is correct!
Congratulations!!!
Interesting part is that this crackme has in fact 3 working passwords. Second one is made of encrypted key XOR 0×80: wxqtqx3|l
And the 3rd one is the very real password (initial one) is whatah3ll.
This happened due to OR operator used in this crackme.