On Eggs and Egg-hunters (Linux/x64)

Syscall59
Syscall59
Apr 7 · 6 min read
Photo by Kony Xyzx on Unsplash

Eggs? Hunting? What are we talking about?

In exploit development, an Egg is a full shellcode payload that usually has a nop sled at the beginning. The start of the payload (usually the nop sled) will have a particular signature that we will use to identify where our shellcode is placed in memory. Sometime you can’t know beforehand where is your shellcode going to be allocated in memory. So you can use the egg’s signature (usually the first 8 bytes of the payload) to find the exact place.

An Egg-Hunter is a piece of shellcode that searches for a particular signature in a process’ Virtual Address Space (a.k.a VAS) and jumps to the payload.

This is useful when dealing with a small input buffer. Imagine that we could place a large payload in a non-vulnerable buffer and then use the egg-hunter in a vulnerable buffer to search for the larger payload and execute it flawlessly.

Photo by Claudio Hirschberger on Unsplash

The general use-case would be:

1- We provide the shellcode with the actual payload we want to execute (a reverse shell, for example).

2- This shellcode will be placed in a random section of the VAS.

3- We then exploit a vulnerability and execute the egg-hunter shellcode.

4- The egg-hunter goes through the whole VAS searching for the larger payload. Once it finds the payload it jumps into it.

5- The larger and final payload is executed. Giving us the ability to use a large payload that would be impossible to use otherwise.

In this example, we are using a single egg-hunter but take into consideration this technique could be chained multiple times to execute really large staged payloads.

Anatomy of an egg-hunter

Photo by Specna Arms on Unsplash

An egg-hunter should have the following properties:

1- It should be robust

It should be able to step into invalid memory regions without crashing the application. Therefore, it should somehow manage the dereference of invalid addresses and the exceptions occurred when, for example, it tries to read from a privileged/critical memory region.

2- It should be small

The main purpose of an egg-hunter is to fit where no other payload could. If we could fit our regular shell payload, why would we need this for? So, naturally, size is one of the most important aspects of an egg-hunter.

3- It should be fast

Waiting for several minutes while the egg-hunter searches for the payload would be a pain. This is certainly not the most important property for an egg-hunter, as we (as a general rule) don’t want to detriment robustness or size in order to make it faster.

Anatomy of an egg

The egg can be any payload of which we will take the first 8 bytes as the signature. In general, the first 8 bytes are part of a nop sled (to improve robustness) so we’ll focus on that kind.

Photo by Annie Spratt on Unsplash

1- Size

An egg usually is 8 bytes long. We, of course, can tweak this making it larger or shorter depending on the circumstances.

2- Type

The egg’s signature is usually executable code. This derives from the fact that once the egg-hunter confirms the match it jumps straight into its location and executes the whole egg. Again, that’s the usual case but an egg-hunter that allows non-executable code as a signature can be implemented too.

Implementing an egg-hunter in Linux/x64

Photo by Eamonn Maguire on Unsplash

In Linux, when a syscall encounters an invalid memory address, most of them will return the EFAULT error code into the RAX register. This is crucial, as it will be our way to know we stepped into the wrong place and it will be our key mechanism for making the egg-hunter robust. You can find more info about the error codes here. Let’s confirm this and gather the value of EFAULT in the process. In order to do that we can use this small assembly snippet that calls the access syscall for the address 0x00 which will fail.

After having compiled and linked this we proceed with the test:

So, EFAULT is 0xfffffffffffffff2 (-14)

Now, let’s write our egg-hunter. We need to check every address to see if it’s valid for our process, if not, we can skip the whole page since a page is the minimum allocable unit in memory. In order to get the page size, we can use the getconf command. In my case, I’m using an ubuntu64 VM that yields: 4096

Our egg-hunter will use the access syscall to check if the memory region is valid. If the page is valid then it will look for the egg. If that’s not the case it will jump to the next page.

Photo by Nicolas Thomas on Unsplash

Let’s test this out!

In the following video, I go through the whole process of extracting the shellcode for both the egg-hunter and the payload. Then I use a test skeleton written in C to demonstrate the functionality.

The payload I’m using is a custom password-protected reverse shell I wrote. You can find the source code within this blog post. The payload will connect back to tcp://127.0.0.1:4444 and will give a shell after the password “LETMEIN!” is introduced.

The source code for the egg-hunter tester written in C can be found here.


That’s all folks! Here are some resources that can be useful if you’re struggling to understand how egg-hunters work:



This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
Student ID: SLAE64–1326
Source code can be found
here

syscall59

Shellcode for the masses

Syscall59

Written by

Syscall59

Twitter: @syscall59 | medium.syscall59.com | syscall59@protonmail.com

syscall59

syscall59

Shellcode for the masses

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade