October 03, 2013

CrackMe v2 walkthrough

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

Attaching GDB to a running process

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!

Attaching to a loop process

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.

ELF headers and GDB behavior

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 and my GDB config

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

Bypassing anti-GDB protection

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!

Bypassing the anti-ptrace protection

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.

Encryption

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'
Getting closer to the real password

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.

Decrypting the encrypted password

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.