So what exactly is an “egghunter”? Well recently I embarked upon the
OSCE course
from Offensive Security and learned just
that… Although I have yet to pass the exam :(
Anyhow, essentially what an “egghunter” does is search through memory and
locate a marker. Once this marker has been found it jumps to the next
instruction which is generally a malicous payload that was preceded with
the marker. So for example:
0xdeadbeef w00tw00t
0xxxxxxxxx malicious
0xxxxxxxxx code
0xxxxxxxxx here
...snip...
The egghunter code would search all of the memory within the process until it
came upon 0xdeadbeef. Once it looked at the value at that address, it would
find the marker bytes that it was looking for and proceed to execute the
instruction at the memory address immediately following the marker.
Strategy
So how will we demonstrate such a feat? Well, we will attempt to:
Create a C program that will create a decent sized uninitialized buffer of memory.
Copy some marked shellcode into the buffer at run time within our main function. This will simulate a piece of marked shellcode landing during a bufferoverflow.
Write our egghunter code in assembly and extract the bytes as we have been doing from our previous exam problems.
Cast the egghunter bytes to a function pointer and execute
If all goes well our egghunter should find the shellcode we placed in the buffer and execute it.
I’m glad I did a little research on writing egghunters before I
started writing one because I learned that my naive implementation
wouldn’t have worked. My initial thought was to just load up an
address in memory and to start comparing bytes looking for my
marker. As I was reminded this would have ended up resulting in
a SIGSEGV. All virtual memory from lowest to highest addresses
is NOT accessible to a program and attempts to access it will result
in a SIGSEGV exception. This should have been obvious to me as segfaults
when playing around with bufferoverflows occur because memory that
has not been allocated by/for the program is attempting to be accessed.
Due to the kernel reserving memory, ASLR memory randomization leaving
un-allocated chunks of memory between the memory segments, as well
as un-allocated memory between the stack and heap to allow both room
to grow, there are quite a few places where an egghunter that simply
just tries to dereference every memory address would fail.
According to skape egghunter paper
one way that we can work around this is by utilizing system calls.
Since system calls work in Kernel Mode. Attempts at accessing memory
that is not accessible to our process will not segfault but will
raise a more friendly exception instead. The egghunters that are
presented in the skape paper leverage this technique. We will utilize
the access(2) revisited egghunter in our demonstration.
Cool. We have some egghunter code, lets setup an example program to
test it out. We will first compile and dump the bytes of our egghunter:
./compile egghunter
We will leverage our reverse shell from our previous
tutorial.
Create our c program demo
If we compile our egghunterdemo.c using gcc -g egghunterdemo.c -o egghunterdemo,
start a netcat listener on port 4444 (as our shellcode is a reverse shell) using
netcat -nlvp 4444 and then execute our egghunter using ./egghunterdemo we
will see that we indeed get a connection back from our shellcode~
To prove that there is nothing up our sleves, lets hook GDB and inspect where
everything is in memory.
Right before we execute our egghunter the memory is laid out as such:
Egghunter
Shellcode Buffer
Wee can see that our egghunter code and our buffer are in fact in two
separate places within memory. We add a breakpoint at the beginning of
our egghunter code:
We place another breakpoint at our jmp edi instruction.
If this instruction is hit and executes it means that our
egghunter has found our marker and that it is about to
execute the code right after our marker. As we can see
in the gdb output our jmp edi does in fact get hit.
We allow the program to continue and we end up with our
reverse shell connection.