SLAE32 - Assignment 3
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: https://www.pentesteracademy.com/course?id=3
Student ID: PA-15072
All associated code can be found here: https://github.com/pAP3R/public/tree/master/SLAE32/assignments
There's a lot of information out there on the web about egghunters, and some really good papers too (e.g. Skape). If you're totally unfamiliar and just want the tldr, here's the gist of it:
Let's say you are testing some sort of server application, maybe it's an FTP app. The application has a couple different commands that accept some input, but when you send certain data to one of the commands, the application crashes in a way that allows you to control the flow of execution. "Hooray!" you might be thinking, "Now I can send my payload!"
To your dismay, after sending payload after payload you find you can only fit a measly 40 bytes in memory through this vulnerable command, womp womp. Earlier while fuzzing, you noticed you could send arbitrarily large amounts of bytes in the other commands though, then send your payload and take control of execution. There's a high probability the other thousand \x41's you sent right before invoking the crash are hanging out in memory, though... but where? Will that even matter?
This is where an egghunter comes in. An egghunter acts as a small piece of code which searches through arbitrary memory locations for a specific, unique string we define, known as an egg. Once it locates the egg, a good egghunter will check the bytes following the intial egg for another egg. If it doesn't find the egg twice, it continues to search memory, looking for the two eggs in a row. Assuming we prepend our shellcode with our egg, if the egghunter locates two eggs, which again, are expected to be unique values, then it returns the location it found.
Now, since our shellcode is prepended with the two unique eggs, the egghunter has found our shellcode in memory, and we're free to jump to it and begin execution.
There are a few caveats though, as always. Some memory just isn't readable, meaning our application will error out, crash, whatever, if we try to read from it. Since the goal of our egghunter is to read the memory we need ways to handle those errors, meaning we acquire some sort of indicator that the memory isn't readable, then skip over those locations to memory that IS readable.
We do this in a few parts:
I created two egghunters for this assignment, one for both of the above. Here's my access() egghunter:
Student ID: PA-15072
All associated code can be found here: https://github.com/pAP3R/public/tree/master/SLAE32/assignments
Egghunters! Woooo!
I love egghunters, I think they're super cool.
There's a lot of information out there on the web about egghunters, and some really good papers too (e.g. Skape). If you're totally unfamiliar and just want the tldr, here's the gist of it:
Let's say you are testing some sort of server application, maybe it's an FTP app. The application has a couple different commands that accept some input, but when you send certain data to one of the commands, the application crashes in a way that allows you to control the flow of execution. "Hooray!" you might be thinking, "Now I can send my payload!"
To your dismay, after sending payload after payload you find you can only fit a measly 40 bytes in memory through this vulnerable command, womp womp. Earlier while fuzzing, you noticed you could send arbitrarily large amounts of bytes in the other commands though, then send your payload and take control of execution. There's a high probability the other thousand \x41's you sent right before invoking the crash are hanging out in memory, though... but where? Will that even matter?
This is where an egghunter comes in. An egghunter acts as a small piece of code which searches through arbitrary memory locations for a specific, unique string we define, known as an egg. Once it locates the egg, a good egghunter will check the bytes following the intial egg for another egg. If it doesn't find the egg twice, it continues to search memory, looking for the two eggs in a row. Assuming we prepend our shellcode with our egg, if the egghunter locates two eggs, which again, are expected to be unique values, then it returns the location it found.
Now, since our shellcode is prepended with the two unique eggs, the egghunter has found our shellcode in memory, and we're free to jump to it and begin execution.
There are a few caveats though, as always. Some memory just isn't readable, meaning our application will error out, crash, whatever, if we try to read from it. Since the goal of our egghunter is to read the memory we need ways to handle those errors, meaning we acquire some sort of indicator that the memory isn't readable, then skip over those locations to memory that IS readable.
We do this in a few parts:
- Check for a bad memory location
- Increase our 'Page' size to skip that section
- Back to step 1
I created two egghunters for this assignment, one for both of the above. Here's my access() egghunter:
global _start section .text _start: ; zero edx for kicks xor edx, edx fix_page: ; This is a hacky fix for page sizes ; 0xfff is 4095, so we inc edx later on to avoid nulls ; page fix is only called if we need some gud gud memory or dx, 0xfff
egghunter: inc edx ; int access(const char *pathname, int mode) ; Give ebx an arbitrary address within the page lea ebx, [edx + 4] ; push, pop, syscall for access() push dword 0x21 pop eax int 0x80 ; compare access() return for errors ; fix the page and get to gud gud memory ; but only if we got bad memory (f2 = EFAULT) :[ cmp al, 0xf2 je fix_page ; if not, load our egg into eax mov eax, 0x41414141 ; load the memory to scan into edi mov edi, edx ; Scan it, jump to egghunter if no match scasd jnz egghunter ; Do that again, just to be sure scasd jnz egghunter ; If we got here, it's valid! jmp ediAnd that's the egghunter. It's 35 bytes, but it's mine! That's it for assignment three.
Comments
Post a Comment