This assignment explains the usage of egghunting shellcode and the implementation of an egghunting code in ASM. So, what is the fuzz about egghunting? Well, it basicly uses a function which searches for a particular string placed in memory: when the occurence is being found, the next part of code will be executed. When the used shellcode is too large to be used in an initial entry point, for e.g. an buffer overflow, an egghunting function might assist in referring to a larger mapping of memory where the intended shellcode does fit.
Assignment:
- Study about the subject
- Create working demo
- Should be configurable for different payloads
- Second stage once pattern found, different payload usage
First, read up about egghunting in Linux in the paper from Skape:
http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
Three requirements for designing an egghunter code:
– Robustness
– Small
– Fast
Implementations
Multiple implementations exist for Linux to be used for egghunting, but the most common is the SIGSEGV segfaulting handler.
Mainly the “access” and “sigaction” function is discussed in the paper with both pro’s and con’s, whilst SIGACTION being currently the most interesting method. It suffices the three requirements, although in some circumstances it may offer some challenges. The method relies on the EFAULT error return message as a negative value when an address is invalid when searching for a valid address for egghunting.
SCASD
This assembly instruction allows for comparing bytes of momory stored in EDI with the value stored in EAX. The ECX register needs to be set as a counter for a loop to be used by SCASD.
When EDI points to an invalid address, a custom exception handler is triggered and the current Instruction Pointer is updated so it points past the current pointer and moves to the page aligning portion of the egg hunter. This increments EBX by one page and starts the loop again.
When SCASD is finished, the egg hunter jumps into EDI and begins executing the larger payload. Pages in Linux are 4096 in size, but pushing the value directly on the stack generates null-bytes so we have to be creative. XOR’ing/OR’ing/negating are all possible options to mitigate the null-bytes. We’ll be using the OR method to mitigate null-bytes.
Function usage and calls for SIGACTION:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
As we know for taking arguments for a function, the arguments should be placed in the following functions:
- EAX sigaction function
- EBX int signum
- ECX const struct sigaction *act
- EDX struct sigaction *oldact