x86 Assembly Language and Shellcoding on Linux

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.


The goal of this assignment is to understand how egghunters work and to craft a working, customizable PoC code.

How it works

If there isn’t enough space for large shellcode, then egghunters can be used to look for the actual executable payload in memory. The only goal of an egghunter is to scrape through memory, find the repeating pattern that marks the start of the shellcode and execute the newly found shellcode. Egghunters need to be fast, small and robust.

I used skape’s paper on egghunters to craft my own version of the egghunter. I would recommend reading this to everyone who wants to have a basic understanding on what egghunter is and how it does its trick.

Call Graph

Here is the graph call for the egghunter shellcode.

Graph call in Cutter

Assembly code

For the embedded code, I removed most of the inline comments. If you want to check out the full source code with all the comments, feel free to do it on my Github.

The application starts out by XORing the EDX register. EDX register holds the addresses that the application will check for access and for the egg.

    xor edx, edx 

The next step sets the EDX address to end with FFF, which is one less than PAGE_SIZE. Setting it to FFF and incrementing by one means that we move up the address exactly by a PAGE_SIZE’s worth. Why? Virtual memory is reserved in PAGE_SIZE chunks by the OS, so if we can’t access memory in one of these blocks, we don’t need to check each and every address in that block, we can move up to the next memory page and check for the egg much-much faster.

This is also the “outside loop”. The shellcode goes through the memory and checks every 4096 bytes whether it is a valid memory address or not. If it’s not, then we move up by 4096 (set to 0xfff and later increment by one), if it is then we move forward in the code.

    or dx, 0xfff ; 4095, one less than the PAGE_SIZE -

In incr_addr we increment addresses one by one and load the address + 4 bytes into EBX which is used for checking access. To initiate the syscall, I pushed byte 0x21 onto the stack and popped it into EAX. Then based on the result of the syscall, we can decide whether the address we are checking is a valid memory address or not. If it’s not valid, we go back to incr_page, if it is, then we continue.

    inc edx ; next address
    lea ebx, [edx+0x4] ; load edx+4

    push byte +0x21 ; access syscall
    pop eax ; access syscall
    int 0x80 

    cmp al,0xf2 ; 0xf2 - EFAULT aka invalid memory address 
    jz short incr_page ; invalid memory address, we can go a PAGE_SIZE up

Once we are at a valid memory address location we can start checking for our egg. I first moved w00t into EAX in reverse order, then I moved the address into EDI. After that we check for w00t at the EDI register twice. To our convinience scasd increments EDI for us, so we don’t need to increment it manually which saves us valuable bytes in our shellcode. If it only finds one egg or it doesn’t find the egg, we jump back to incr_addr and check the next byte. Otherwise we just jump to the newly found shellcode and execute it.

    mov eax, 0x74303077 ; t00w
    mov edi, edx ; 
    scasd ; search for the first instance of EAX (w00t) in EDI
    jnz short incr_addr
    scasd ; search for the second instance EAX (w00t) in EDI
    jnz short incr_addr
    jmp edi ; execute shellcode

Egghunter in-action

Stack trace displaying some of the syscalls for finding the egg and the execution of the reverse shell. In the terminal below, netcat receives the connection and a proof is displayed that the reverse shell works.

Egghunter in action

Shellcode generator

For this assignment, egghunter.c is written so it’s easily customizeable both for the egghunter or for the reverse TCP shellcode. To modify any of the payloads, you need to convert your values into ASCII hex and replace the specified constants. If you want to use it elsewhere, just copy-paste the shellcode from the source.

Final Thoughts

During this assignment I learned a lot about egghunters. Can’t wait to put what I learned into practice during the OSCE.

Again, all the code is on my Github and if you want to be informed about new posts, just follow me on Twitter at @fuzboxz.