in the name of zero

August 22, 2006

qnix’s qcrk2

before anything else, my recent soundtrip. oldies songs, specifically everly brothers, gin blossoms and “yakap sa dilim by APO hiking society”

so i found time to solve another crackme -qnix’s qcrk2

This is a binary ELF crackme programmed in C for Linux
you should make the programe print CRACKED .

Rules : NO PATCHING .
HINT : BOF

Difficulty: 2 - Needs a little brain (or luck)
Platform: Unix/linux etc.
Language: C/C++

there is anti-ptrace but no environment variable detection e.g. LD_PRELOAD so a ptrace wrapper did all the trick. anyway, onto relevant blocks…

first, the argv[1] handler…

 804850d:       8b 45 0c                mov    eax,DWORD PTR [ebp+12]	; argv[0]
 8048510:       83 c0 04                add    eax,0x4			; argv[1]
 8048513:       83 ec 0c                sub    esp,0xc
 8048516:       ff 30                   push   DWORD PTR [eax]
 8048518:       e8 6f fe ff ff          call   804838c <strlen@plt>	; strlen(argv[1])
 804851d:       83 c4 10                add    esp,0x10
 8048520:       3d ff 03 00 00          cmp    eax,0x3ff		; 1023d
 8048525:       76 3b                   jbe    8048562 <main+0xea>	; copy to buffer1
 ; -- snipped. crackme error/overflow protection block --
 804855d:       e9 1c 01 00 00          jmp    804867e <main+0x206>	; exit(0);

we move now to the second user argument handler

 80485c2:       8b 45 0c                mov    eax,DWORD PTR [ebp+12]	; argv[0]
 80485c5:       83 c0 08                add    eax,0x8			; argv[2]
 80485c8:       83 ec 0c                sub    esp,0xc
 80485cb:       ff 30                   push   DWORD PTR [eax]
 80485cd:       e8 ba fd ff ff          call   804838c <strlen@plt>	; strlen(argv[2])
 80485d2:       83 c4 10                add    esp,0x10
 80485d5:       3d ff 0b 00 00          cmp    eax,0xbff		; 3071d
 80485da:       76 38                   jbe    8048614 
; copy to buffer2

how is argv[2] being copied anyway?

 804862a:       8b 45 0c                mov    eax,DWORD PTR [ebp+12]	; argv[0]
 804862d:       83 c0 08                add    eax,0x8			; argv[2]
 8048630:       ff 30                   push   DWORD PTR [eax]		; varg
 8048632:       68 7d 88 04 08          push   0x804887d		; const char *fmt = ("%s")
 8048637:       68 00 0c 00 00          push   0xc00			; size_t
 804863c:       8d 85 f8 f7 ff ff       lea    eax,[ebp-2056]		;
 8048642:       50                      push   eax			; char *str
 8048643:       e8 74 fd ff ff          call   80483bc <snprintf@plt>	; snprintf()

translated to C, the listing above is approximately equal to snprintf(buff, 3072, “%s”, argv[2]);
sizeof(buf) is more or less 2056 bytes. (as gcc’s stack allocation is a complete mystery to me) but the snprintf() is called with sizeof 3072 bytes. clearly writing past the end of the buffer.

08048686 <crap>:
 8048686:       55                      push   ebp		; save old frame
 8048687:       89 e5                   mov    ebp,esp		; new frame
 8048689:       83 ec 08                sub    esp,0x8
 804868c:       83 ec 08                sub    esp,0x8		; 16 bytes allocation
 804868f:       68 e0 88 04 08          push   0x80488e0	; our dear string
 8048694:       ff 35 84 9a 04 08       push   ds:0x8049a84	; STDOUT@GLIBC
 804869a:       e8 dd fc ff ff          call   804837c <fprintf@plt>
 804869f:       83 c4 10                add    esp,0x10		; unwind stack
 80486a2:       b8 00 00 00 00          mov    eax,0x0		; function return value = success
 80486a7:       c9                      leave  			; restore old frame
 80486a8:       c3                      ret    			; return from function crap()

now, looking thru the listing, there was really no way that function crap() was being called from the crackme itself. no direct cross reference whatsoever.

this is where the author’s hint comes into play: BOF (buffer overflow)

1) first argument is useless.
2) second argument buffer will be filled with the address of the crap() function (0x8048686) so that the return address in the stack will be overwritten with the address of crap().

i verified those things with gdb. (using a ptrace wrapper ‘ptrace.so”)

misha@localhost ~ $ gdb qcrk2
	
niela@gdb $ set env LD_PRELOAD=./ptrace.so
	
niela@gdb $ r tmp `perl -e 'print "a"x3071'`
	
---------------------------
Qcrack v2
By: Qnix
    Qnix[at]bsdmail.org
    www.0x11.org
---------------------------
	
(+) Copying argv[1] into the buffer1 ...        [done] (3)
(+) Copying argv[2] into the buffer2 ...        [done] (3071)
	
Program received signal SIGSEGV, Segmentation fault.
_______________________________________________________________________________
     eax:00000000 ebx:B7F02FF4  ecx:B7F03880  edx:00000010     eflags:00010286
     esi:B7F2AC80 edi:BFC27A84  esp:BFC27A30  ebp:61616161     eip:61616161
     cs:0073  ds:007B  es:007B  fs:0000  gs:0000  ss:007B    o d I t S z a P c
[007B:BFC27A30]———————————————————[stack]
BFC27A60 : 61 61 61 61  61 61 61 61 - 61 61 61 61  61 61 61 61 aaaaaaaaaaaaaaaa
BFC27A50 : 61 61 61 61  61 61 61 61 - 61 61 61 61  61 61 61 61 aaaaaaaaaaaaaaaa
BFC27A40 : 61 61 61 61  61 61 61 61 - 61 61 61 61  61 61 61 61 aaaaaaaaaaaaaaaa
BFC27A30 : 61 61 61 61  61 61 61 61 - 61 61 61 61  61 61 61 61 aaaaaaaaaaaaaaaa
[007B:B7F2AC80]———————————————————[ data]
B7F2AC80 : 00 00 00 00  00 10 00 00 - 0C 06 02 00  EB 7B C2 BF ………….{..
B7F2AC90 : 04 00 00 00  B4 72 F0 B7 - 04 00 00 00  64 00 00 00 …..r……d…
[0073:61616161]———————————————————[ code]
0x61616161:     Error while running hook_stop:
Cannot access memory at address 0x61616161
0x61616161 in ?? ()

clearly, the EIP and EBP are being overwritten with “a” (note the values in bold)

as there is already a solution by cyrex on how to overflow the stack to overwrite the return address, doing repeating that would be reinventing the wheel.

the only objective of this crackme was to make it print the string “cracked”, so i basically extended the ptrace wrapper to set up a SIGSEGV signal handler via attribute ((constructor)) to call crap() using a function pointer. and finally, force the crackme to segfault.

— crack.c –

/* gcc -shared -fPIC -o crack.so crack.c */
	
#include <signal.h>
#include <stdlib.h>
	
#define CRAP_FUNC_ADDR  0x8048686
	
/* call crap() on SIGSEGV */
void handler(int sig)
{
        void (*fp)();
        fp = (void *) CRAP_FUNC_ADDR;
        fp();
        exit(0);
}
	
/* setup a signal handler for the SIGSEGV signal */
void __attribute__ ((constructor)) qcrk_constructor()
{
        signal(SIGSEGV, handler);
}
	
/* we don't even need this anymore */
int ptrace(int a, int b, int c, int d)
{
        return 0;       // always return succesfully!
}

i tried it out on the crackme and it _somehow_ works…

misha@heaven ~ $ gcc -shared -fPIC -o crack.so crack.c
	
misha@heaven ~ $ LD_PRELOAD=./crack.so ./qcrk2 hello `perl -e 'print "a"x3071'`
	
---------------------------
Qcrack v2
By: Qnix
    Qnix[at]bsdmail.org
    www.0x11.org
---------------------------
	
(+) Copying argv[1] into the buffer1 ...        [done] (5)
(+) Copying argv[2] into the buffer2 ...        [done] (3071)
	
        ---[[[ CRACKED ]]]---

it’s also good to reminisce aleph one’s smashing the stack for fun and profit and a former blog entry i wrote, shellcode injection via ptrace.

oh, and lastly, happy birthday daddy!

1 Comment »

The URI to TrackBack this entry is: http://gnurbs.blogsome.com/2006/08/22/qnixs-qcrk2/trackback/

  1. wow! teach me please :)

    Comment by pipitaMaster — August 22, 2006 @ 10:59 am

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Get free blog up and running in minutes with Blogsome | Theme designs available here