Linux/x86 Egghunter Shellcode
By Kunal Pachauri - July 24, 2019
The main goal here is to analyze Egg Hunter shellcode and creating a working demo of it. The Egg hunter should be easily configurable to different shellcode payloads
Why do we need an Egg Hunter?
Ok,Let’s start with little bit of Buffer Overflow background. It occurs when an a program tries to write more data in the buffer than it can actually hold. This leads to overwriting the adjacent memory locations. If the data is specially crafted, the execution can be controlled making the shellcode execute. Sometimes the buffer size is small and it cannot hold the complete shellcode. This is when Egg Hunter shellcode comes to rescue.
What is Egg Hunter?
This Egg Hunter is a technique used during exploit development that can search the entire memory range for one shellcode and redirect flow to it. According to Fuzzy Security.
"An egg hunter is composed of a set of programmatic instructions that are translated to opcode and in that respect it is no different than any other shellcode (this is important because it might also contain badcharacters!!). The purpose of an egg hunter is to search the entire memory range (stack/heap/..) for our final stage shellcode and redirect execution flow to it."
An Egg refers to a unique set of 4 bytes, that are used to mark the start of the original shellcode. These 4 bytes are prepended before the shellcode twice. The reason they are repeated twice is to make sure that the Egg Hunter is finding the correct shellcode and not the Egg Tag itself. However, Egg Hunter refers to the small shellcode that actually goes ahead and searches the whole Virtual Address Space for our egg. Once the egg is found, It checks whether the next four bytes are also the same, if the condition meets it passes on the execution to starting of the original shellcode i.e 8 bytes ahead of egg.
Algorithm of an Egg Hunter
Very simply put, the working of an egg hunter can be seen as:
But unfortunately searching the Virtual Address Space is not that easy. Why?
Quoting these lines from this amazing white-paper by Skape on Egg Hunters:
The danger of searching a process’ VAS for an egg lies in the fact that there tend to be large regions of unallocated memory that would inevitably be encountered along the path when searching for an egg. Dereferencing this unallocated memory leads to Bad Things
So what can we do now? There are three ways described by Skape in his White-paper.
2. access(2) revisited
What is Egg Hunter?
We will go with the third way because it has the smallest shellcode of 30 bytes and its performance is also better.
As we said, it may not be a good idea to dereference an unallocated memory, we will use the sigaction systemcall to determine whether a given address is valid or not before dereferencing it.
The sigaction system call is much like the signal system call, except that it allows much more granular control. Its real purpose is to allow for defining custom actions to be taken on the receipt of a given signal. On this day, however, its purpose will be to allow for the validating of user-mode addresses. The sigaction function is prototyped as follows:
int sigaction(int signum, const struct sigaction *act, structsigaction *oldact);
Based on our previous learning,looking at the function we can understand the following:
1. EAX should contain syscall number for sigaction i.e 67
2. EBX should contain the value of signum
3. ECX should contain value of act which is address of memory having sigaction structure
4. EDX should contain value of oldact which is address of memory having sigaction structure
The goal here will be to use the act structure as the pointer for validating a larger region of memory than a single byte.For reference, sigaction is defined as:
Each of the elements of sigaction struction is 32 bits i.e 4 bytes. Thus, the total size of the structure is 16 bytes. This means that when the verify_area routine is called, it will ensure that there are 16 bytes of contiguous memory at the address supplied for the act structure. Although our egg is of 8 bytes but assuming that any of the meaningful shellcode will be greater than 8 bytes, checking for 16 bytes will work for us.
What else we need to take care of?
Linux uses a virtual memory system where all of the addresses are virtual addresses and not physical addresses. These virtual addresses are converted into physical addresses by the processor. To make this translation easier, virtual and physical memory are divided into pages. Each of these pages is given a unique number; the page frame number. Thus,we need to make sure whenever we hit a EFAULT we are increasing the page number.
Let’s check what is the page size of our memory.
Now to align the page properly based on the pointer value being validated, we need to find a way by which we can increment the address by 4096 to move to the next page.
This can be done by using the Bitwise OR operator. If we bitwise OR the lower 16 bits of EDX Register with 0xfff, it will result in 4095 And then incrementing the register by 1 will lead to increasing 4096 in the address being validated.
Also this approach will help us to prevent NULL's in the register which were a problem if we directly add 4096 to EDX.
There is one more thing that we should know before recreating the shellcode i.e comparing the strings.
We need to compare the Egg String to identify the starting of shellcode. This can be done using scasd opcode. Here is what scasd does:
Compares the byte, word, or double word specified with the memory operand with the value in the AL, AX, or EAX register, and sets the status flags in the EFLAGS register according to the results. The memory operand address is read from either the ES:EDI or the ES:DI registers (depending on the address-size attribute of the instruction, 32 or 16, respectively). The ES segment cannot be overridden with a segment override prefix.
Recreating Egg Hunter in Assembly
Let us now dive deep into creating the Egg Hunter shellcode based on our above analysis.
To verify, let’s take out previous bind shellcode we created, prepend shellcode with the Egg and execute egg hunter as the shellcode from the c program.
Here is how our final C Program looks like.
This is how it looks when it is compiled and executed.
We have finally created an Egg Hunter Shellcode that is easy configurable with any payload. All that needs to be done is to prepend "\x90\x50\x90\x50\x90\x50\x90\x50" before the actual shellcode and use this egg hunter to execute that payload.
Thanks, Hope you had a nice reading. Feel free to point out any mistakes and improvements because it's always a learning!