SLAE: Assignment 3 of 7

Assignment #3 objectives:
- Study Egg Hunter shellcode
- Create a working demo of an egghunter
- The egg hunter should be configurable for different payloads

Looking on, there are various egghunter shellcodes available. Most come with detailed explanations of how they work. The goal of this assignment is to study the shellcode to figure out how egghunters work. Upon reading generic egghunter shellcodes, the goal of egghunters is simple: Tag large shellcode with an "egg", and use a very small piece of code - the egg hunter - to find the egg and jump to the egg to execute the shellcode. The reason for egg hunting code is that sometimes, there isn't enough space in memory to store a large piece of exploit code: With an egg hunter, you can store shellcode in any part of memory using other techniques than the actual vulnerability in a program - for instance via program parameters - , search for it, then execute it.

I found the following PDF which details how egg hunters work:

We use the following code as a base:

Note: We compile the program and execute it:
Egg hunter shellcode Length:  19
Egg Found!!

00000000  B872800408        mov eax,0x08048072
00000005  BB8F509050        mov ebx,0x5090508f
0000000A  43                inc ebx
0000000B  40                inc eax
0000000C  3918              cmp [eax],ebx
0000000E  75FB              jnz 0xb
00000010  FFE0              jmp eax
00000012  01                db 0x01

Note: I analyze each line in the code:

00000000  B872800408        mov eax,0x08048072
Sets EAX to the base address of Linux programs

00000005  BB8F509050        mov ebx,0x5090508f
Sets EBX to a value that will become the egg in the next instruction

0000000A  43                inc ebx
Sets EBX to 0x50905090 which is the real egg

Note: The egg code, when executed, does nothing damaging to the execution of the program except for stack corruption:

00000000  90                nop
00000001  50                push eax
00000002  90                nop
00000003  50                push eax
Note: If we executed the egg itself, nothing bad would happen except for some stack corruption. If we want a stack-neutral egg, a better option might be to first push eax, then pop it in the next instruction.

0000000B  40                inc eax
Increase EAX by one - this will be jumped to later when searching for the egg

0000000C  3918              cmp [eax],ebx
Compare the egg in EBX, with the code pointed to by EAX

0000000E  75FB              jnz 0xb
If the egg isn't found at EAX, jump to 0000000B to search the next address

00000010  FFE0              jmp eax
If the egg is found, then jump to EAX - which is the egg

00000012  01                db 0x01
This byte is used by the very first instruction to dynamically determine the base memory address at compilation time.

So now that we know how egghunters work we can create our own; let the fun begin!
Now, we need to find a good way to encode our egg value. We can't store the egg directly in our code, because if we do then our egghunter is going to find the egg in the egghunting code instead of the actual shellcode.

There is a nice function SHL and SHR which bit-shifts a value. Effectively a SHL by 1 multiplies the register value by 2, a SHR by 1 divides by 2.

Note: We compile our assembly and grab the shellcode:


Let's try our new egghunter!

Note: We compile our program and test it:

Egg hunter shellcode Length:  21
Egg Found!!

Note: Wow that was easy: Didn't even need GDB and the cat's already in the bag!

We can now use the shellcode from assignment 2 and add our egg hunter:

Note: We compile our program and run it in gdb:

Shellcode Length:  83
Segmentation fault (core dumped)

Note: Upon running the code, we receive a segmentation fault. It seems this cat managed to slip out of the bag somehow. Using gdb, I found that the cause of the segfault is due to the egghunter searching in areas of memory that it had no access to. I thus had to come up with a new egghunter that checks the memory space for read access before trying to read it. I did some research and found the following document: I will use Skape's egg hunter as a basis for my egghunter:

This is what I came up with:

Note: We compile our egghunter assembly code and grab the shellcode bytes:


Note: We compile our new program and test it again:

Egg hunter shellcode Length:  42
Egg Found!!

Note: Oh, yes... Cat back in that bag! Our egghunter shellcode doubled in size, but it is more reliable. If it works, we can look at optimizing this code later. Let's give it a try.

Note: We compile the latest revision of our program:

Shellcode Length:  87
Egghunter Length:  42
Segmentation fault (core dumped)

Note: Hm.. another segfault... not looking good - let's see what's going on

--------------------------------------------------------------------------[regs] EAX: 0xFFFFFFEA  EBX: 0x00001004  ECX: 0x12345602  EDX: 0x00001000  o d I t S z a p C
ESI: 0x12345678  EDI: 0x12345678  EBP: 0x12345678  ESP: 0xBFFFF340  EIP: 0x0804A054
CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B  Jump is NOT taken (z!=1)
--------------------------------------------------------------------------[code] => 0x804a054 <egghunter+20>:    je     0x804a044 <egghunter+4>
Note: We can see that EAX is 0xFFFFFFEA
Note: The error code is in two's complement value. It can be decoded as follows into the decimal equivalent error code:
0xFFFFFFFF - 0xFFFFFFEA + 1 = 22

Code 22 means EINVAL. We can look up what that means as follows:

access() shall fail if:
EACCES        The requested access would be denied to the file, or search permission is denied for one
of the directories in the path prefix of pathname.  (See also path_resolution(7).)
ELOOP         Too many symbolic links were encountered in resolving pathname.
ENAMETOOLONG  pathname is too long.
ENOENT        A component of pathname does not exist or is a dangling symbolic link.
ENOTDIR       A component used as a directory in pathname is not, in fact, a directory.
EROFS         Write permission was requested for a file on a read-only file system.

access() may fail if:
EFAULT        pathname points outside your accessible address space.
 EINVAL        mode was incorrectly specified.
EIO           An I/O error occurred.
ENOMEM        Insufficient kernel memory was available.
ETXTBSY       Write access was requested to an executable which is being executed.

Note: We see the following: EINVAL - mode was incorrectly specified.
Upon running the file in gdb and check the arguments to ACCESS, we find ECX, the register that holds the "mode" argument, isn't set properly (ECX is 0x12345602 just before the INT 0x80 instruction). The "man 2 access" command further reveals valid values for "mode" parameter: R_OK, W_OK, and X_OK.  F_OK. Since we're checking for read access to the memory, we need to set R_OK. So how can we find what R_OK maps to?

#define   R_OK  4  /* Test for read permission. */
Note: So ECX should be set to 4 if we want to check R_OK, or 0 if we want to check F_OK. We modify the assembly language and recompile.

Note: We compile our new assembly code:

We replace our shellcode in myegg5 with the fixed shellcode above and recompile myegg5.c

Upon running myegg5.c, we still get a segfault - more gdb is required!

=> 0x804a067 <egghunter+39>:    jmp    edi
gdb$ disassemble 0x0804a084
Dump of assembler code for function shellcode:
0x0804a080 <+0>:    add    DWORD PTR [edx],eax
0x0804a082 <+2>:    add    al,0x8
0x0804a084 <+4>:    xor    eax,eax
0x0804a086 <+6>:    push   eax
0x0804a087 <+7>:    inc    eax
0x0804a088 <+8>:    push   eax
0x0804a089 <+9>:    pop    ebx
0x0804a08a <+10>:    push   eax
0x0804a08b <+11>:    inc    eax
0x0804a08c <+12>:    push   eax
0x0804a08d <+13>:    mov    al,0x66
0x0804a08f <+15>:    mov    ecx,esp
0x0804a091 <+17>:    jg     0x804a094 <shellcode+20>
0x0804a093 <+19>:    add    DWORD PTR [ecx],eax
0x0804a095 <+21>:    jg     0x804a098 <shellcode+24>
0x0804a097 <+23>:    add    DWORD PTR [edx+edx*8],eax
0x0804a09a <+26>:    mov    ax,0xb315

What happened to our shellcode? Did the egg signature change the shellcode?

first we check the actual shellcode is still valid:

The shellcode seems fine. So I check the shell with the egg prepended (the anticipation!..)

The egg does not seem to affect our shellcode. Something else must be changing the shellcode during run-time.
In a light-bulb moment, I realize that we're dynamically changing the IP and port in the code, but we haven't changed our shellcode offsets: What happened was that the egg grew our shellcode by 6 bytes and we need to correct our offsets to match. So if we add 6 to the offsets of the port and IP, all should be good!
And finally, after making the changes, we have a working egg hunter shellcode. GDB once again came to the rescue and gave me the answers I needed.

The final code:

We compile myegg6.c and run it, while our listener is listening on port 1234:

Connection from port 1234 [tcp/*] accepted

Filed under: Exclude from front page SLAE