Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Assignment #3 for the SLAE exam is to write an egg hunter shellcode.
First, the requirements for assignment #3 were as follows.
Study about the Egg Hunter shellcode Create a working demo of the Egghunter Should be configurable for different payloads
Other than that, let’s jump right in!
I cannot cover egg hunters as well as the original paper or other sources. That said, I wanted to touch on what they are, and what they might be used for.
An egg hunter is a small stager that searches a program’s memory for a series of bytes (the “egg”). Once it finds this egg, it jumps to that location.
This is useful when the space for your first payload is smaller than your final payload. In this case, you can store the desired payload in memory, and then use the egg hunter to search for it. This egg + payload combination could live on the stack, heap, or somewhere else entirely. You do not need to know where, as the egg hunter can search it all.
I also used the following references to brush up on Linux program memory:
I copied my first version from the Safely Searching Process Virtual Address Space white paper. I highly recommend reading through this, as it will do a far better job than me at explaining egg hunters.
Also, for more information on very small egg hunters, I recommend the following paper.
First, I took the shellcode from my Hello World example and added it to my shellcode wrapper. Additionally, I added in the egg hunter code. Finally, I changed the call to the egg hunter shellcode, and used memcpy to put my shellcode somewhere on the stack.
In this case, the egg hunter shellcode will execute, search for my shellcode, and then jump to it.
#include<stdlib.h> #include<stdio.h> #include<string.h> unsigned char egghunter[] = \ "\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8\x90\x50\x90\x50\x89\xcf\xaf\x75\xec\xaf\x75\xe9\xff\xe7"; unsigned char code[] = \ "\x90\x50\x90\x50\x90\x50\x90\x50" // EGG - [NOP, PUSH eax] x4 "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80"; main() { char *buffer; buffer = malloc(strlen(code)); memcpy(buffer, code, strlen(code)); printf("\nEgg Hunter Length: %d\n", strlen(egghunter)); printf("Shellcode Length: %d\n\n", strlen(code)); int (*ret)() = (int(*)())egghunter; ret(); free(buffer); }
If you do not feel like disassembling it, you can find the original egg hunter assembly below.
or cx,0xfff inc ecx push 0x43 pop eax int 0x80 cmp al, 0xf2 je 0x804a040mov eax, 0x50905090 mov edi, ecx scasd jne 0x804a045 scasd jne 0x804a045 jmp edi
With version 1 of my egg hunter complete, it was time to compile and execute it.
doyler@slae:~/slae/_exam/egghunter$ gcc -o shellcode -z execstack -fno-stack-protector shellcode.c doyler@slae:~/slae/_exam/egghunter$ ./shellcode Egg Hunter Length: 30 Shellcode Length: 51 Hello World!
As you can see, everything worked perfectly, and I was left with a 30 byte egg hunter!
While my demo technically meets all 3 requirements, I don’t feel like I’ve done enough yet.
In this case, I’m going to make some changes to the original egg hunter. While I do not think I can improve on the length or speed of the code, I will improve the robustness.
My goal is to create an egg hunter shellcode that still works, uses the sigaction system call, has some integrity checks, and is still under 60 bytes.
As specified by the original author, the sigaction egg hunter has the following con.
While this is a rare occurrence, it is still more likely than the other 2 potential issues.
In this case, adding one more byte shouldn’t make a huge difference.
Next, I want to use a different egg. While this isn’t a major issue, there is the minuscule chance that my egg could exist in a binary that I’m exploiting. For example, the author could just add those instructions as a defense mechanism to defeat copy and paste egg hunters. Additionally, if executed, this egg hunter could cause some minor stack corruption.
With this in mind, I decided to go with the following “egg”:
"\x50\x58\x53\x5B" 50 push eax 58 pop eax 53 push ebx 5b pop ebx
Finally, I also wanted to add an integrity check. For this, I used dijital1’s simple egg hunter checksum routine.
I will also be using a checksum byte in my shellcode. I’ll cover where this comes from in the next section, but I wanted to share the code to calculate it now.
The code below is pretty straight forward, and will calculate the checksum byte for a provided payload. I calculate this by adding all the bytes together, and then performing an AND operation with FF. This will get the last 2 bytes of the total as a “checksum” byte.
While there is a 1/255 potential for collision, it is still better than nothing at all.
payload = "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80" chksum = 0 for byte in bytearray(payload): chksum += byte print "Checksum byte = " + "\\x%0.2X" % (chksum & 0xff)
As you can see, it calculated a checksum byte of 0x66 for my execve payload.
doyler@slae:~/slae/_exam/egghunter$ python egg_hunter_checksum.py Checksum byte = \x66
With those changes in mind, including implementing the checksum similarly to Metasploit, I wound up with the following assembly.
; Filename: egghunter.nasm ; Author: Ray Doyle (@doylersec) ; Website: https://www.doyler.net ; ; Purpose: SLAE Exam Assignment #3 - Egg Hunter Shellcode (Linux/x86) global _start section .text _start: ;cld ; Clear the direction flag, for when it is set page_align: or cx,0xfff ; Perform page alignment page_inc: inc ecx ; Increment address sigaction: push 0x43 ; sys_sigaction pop eax ; Load syscall into EAX int 0x80 ; Execute sigaction() check_mem: cmp al, 0xf2 ; Compare return value to EFAULT jz page_align ; If EFAULT occured, go to the next page (re-align) check_egg: mov eax, 0x5058535b ; Egg value to compare to mov edi, ecx ; Save address pointer in EDI scasd ; Compare EAX (egg) to EDI (first 4 bytes) jnz page_inc ; If no egg, go to the next page scasd ; If first egg found, perform compare to EDI (second 4 bytes) jnz page_inc ; If no second egg, go to next page ; https://www.exploit-db.com/exploits/14873/ ; https://github.com/rapid7/rex-exploitation/blob/master/lib/rex/exploitation/egghunter.rb find_egg: push ecx ; Save the current value of ECX (not included in original) xor ecx, ecx ; Zero out the counter xor eax, eax ; Zero out the accumlator calc_chksum_loop: add al, byte [edi+ecx] ; Add the byte to running total inc ecx ; Increment the counter ; Payload length = 43 (0x2b) cmp cl, 0x2b ; cmp counter to egg_size -- ECX if length >= 256 jnz calc_chksum_loop ;if it's not equal repeat test_ckksum: cmp al, byte [edi+ecx] ; cmp eax with 1 byte checksum pop ecx jnz page_inc ; Increment page to continue searching (failed checksum) execute: jmp edi
I then compiled and linked my new binary.
doyler@slae:~/slae/_exam/egghunter$ ./compile.sh egghunter[+] Assembling with Nasm ... [+] Linking ... [+] Done!
First, I grabbed the shellcode string from my binary.
doyler@slae:~/slae/_exam/egghunter$ objdump -d ./egghunter|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' "\xfc\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8\x5b\x53\x58\x50\x89\xcf\xaf\x75\xec\xaf\x75\xe9\x51\x31\xc9\x31\xc0\x02\x04\x0f\x41\x80\xf9\x2b\x75\xf7\x3a\x04\x0f\x59\x75\xec\xff\xe7"
Next, I modified my original shellcode wrapper. In addition to the new egg hunter, I also put a few mangled versions of my shellcode on the heap. This would show the integrity check and only execute the proper shellcode.
I also added all the different shellcodes to the heap, and printed out the address. This would prove that the egg hunter works just fine, even with ASLR enabled.
#include<stdlib.h> #include<stdio.h> #include<string.h> // EGG - PUSH eax, POP eax, PUSH ebx, POP ebx #define EGG "\x5b\x53\x58\x50" // Egg Hunter w/ integrity checking (51 bytes, 50 without first instruction (cld)) unsigned char egghunter[] = \ "\xfc\x66\x81\xc9\xff\x0f\x41\x6a\x43\x58\xcd\x80\x3c\xf2\x74\xf1\xb8" EGG "\x89\xcf\xaf\x75\xec\xaf\x75\xe9\x51\x31\xc9\x31\xc0\x02\x04\x0f\x41\x80\xf9\x2b\x75\xf7\x3a\x04\x0f\x59\x75\xd5\xff\xe7"; // Proper shellcode with improper checksum byte unsigned char badcode1[] = \ EGG EGG "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80" "\x01"; // Improper shellcode (bytes missing from middle) with proper checksum byte unsigned char badcode2[] = \ EGG EGG "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80" "\x66"; // Hello World shellcode with prepended eggs unsigned char code[] = \ EGG EGG "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80" "\x66"; // Only one egg unsigned char badcode3[] = \ EGG "\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\x52\x52\x6a\x0a\x68\x72\x6c\x64\x21\x68\x6f\x20\x57\x6f\x68\x48\x65\x6c\x6c\x89\xe1\xb2\x0d\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\x90\x90\x90\x90" "\x66"; main() { char *heap; heap = malloc(1000); // Idea from the RCEsecurity post - https://www.rcesecurity.com/2014/08/slae-egg-hunters-linux-x86/ printf("\nMemory location of heap: %p\n", heap); memcpy(heap, badcode1, sizeof(badcode1)); memcpy(heap + sizeof(badcode1), badcode2, sizeof(badcode2)); memcpy(heap + sizeof(badcode1) + sizeof(badcode2), code, sizeof(code)); memcpy(heap + sizeof(badcode1) + sizeof(badcode2) + sizeof(code), badcode3, sizeof(badcode3)); printf("\nEgg Hunter Length: %d\n", strlen(egghunter)); printf("Shellcode Length: %d\n\n", strlen(code)); int (*ret)() = (int(*)())egghunter; ret(); }
Since I was already familiar with how this operated, I didn’t trace the execution in GDB.
That said, as you can see below. the payload works each time. This is in spite of the mangled shellcode(s) being loaded on the heap, as well as ASLR being enabled.
doyler@slae:~/slae/_exam/egghunter$ ./shellcode Memory location of heap: 0x57a19160 Egg Hunter Length: 51 Shellcode Length: 52 Hello World! doyler@slae:~/slae/_exam/egghunter$ ./shellcode Memory location of heap: 0x56f59160 Egg Hunter Length: 51 Shellcode Length: 52 Hello World! doyler@slae:~/slae/_exam/egghunter$ ./shellcode Memory location of heap: 0x581a5160 Egg Hunter Length: 51 Shellcode Length: 52 Hello World!
Also, in the end, I decided to remove my ‘cld’ instruction. I never ran into any scenarios where this was relevant, and it saved me one byte. This brought my shellcode down to the nice round number of 50 bytes.
I learned a ton from this assignment, and it was fairly easy building off of Skape’s original work.
Up next is a custom encoder/decoder, and I am looking forward to that as well.
I’m almost halfway through with this exam, but I’m looking forward to the end!
Finally, you can find the code and updates in my GitHub repository.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert
Student-ID: SLAE-1212
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.