rapid7 / metasploit-framework

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

switching processor mode from x86 to x64 gives memory error #15625

Closed C0D3-M4513R closed 3 years ago

C0D3-M4513R commented 3 years ago

Firstly: This is not per-se an issue with meterpreter. The Code I have issues with is in this repo though.

Background: I am writing a rust dll injector library. I want the injector to support injection into x64 and x86. Because I am writing a library, the injector could be x86, or x64.

What I have:

For x86->x64 I found some issues, with the windows api. Primarily, that you cannot simply call CreateToolhelp32Snapshot to get modules, of a x64 process, from a x86 process. Since that didn't work, I have snatched myself some ntdll functions, which can read x64 memory under x64, and am reading/parsing the PEB->LDR->InLoadOrderModuleList->DllBaseAddress. That works too.

What I hit a dead end with, is Creating a thread, which calls LoadLibraryA. I have the x64 function pointer, of the LoadLibraryA function and the parameter for said function, in the scope, of the target process. Now I want to actually execute that function, in the target process, I am injecting to.

I copied the following arrays into my project, as a constant (in rust afaik constants are evaluated on compile time). https://github.com/rapid7/meterpreter/blob/d338f702ce8cb7f4e550f005ececaf5f3cadd2bc/source/common/arch/win/i386/base_inject.c#L15 https://github.com/rapid7/meterpreter/blob/d338f702ce8cb7f4e550f005ececaf5f3cadd2bc/source/common/arch/win/i386/base_inject.c#L22 To be able, to execute those bytes, I create an executable memory-page, with a winapi call, copy the buffer's contents into that memory-page, and execute it.

My Code

some information about my code: asmcompiled::MIGRATE_EXECUTEX64 = migrate_executex64 asmcompiled::MIGRATE_WOWNATIVEX = migrate_wownativex exec_ptr creates an rwx memory page and returns a LPVOID (*mut c_void) My type definitions are as follows: ```rust pub type X64Function=unsafe extern "C" fn(DWORD)->BOOL; pub type EXECUTEX64=unsafe extern "C" fn(X64Function,DWORD)->DWORD; #[derive(Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)] #[repr(packed,C)] pub struct WOW64CONTEXT64 { pub hProcess:ULONG64, pub lpStartAddress:ULONG64, pub lpParameter:ULONG64, pub hThread:ULONG64, } pub const MIGRATE_EXECUTEX64:&[u8] = b"\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00\x58\x83\xC0\x25\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00\x89\x02\xE8\x09\x00\x00\x00\x83\xC4\x14\x5F\x5E\x5D\xC2\x08\x00\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6\x5F\x50\xC7\x44\x24\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24"; pub const MIGRATE_WOWNATIVEX:&[u8] = b"\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00\x48\x83\xC4\x50\x48\x89\xFC\xC3"; ``` My code: ```rust #[no_manle]//Only there for debugging! #[inline(never)]//Only there for debugging! //same with the extern "c" extern "C" fn exec_bytes(proc:u64,entry_point:u64,mem:u64) -> Result<()>{ let exx64ptr = exec_ptr(asmcompiled::MIGRATE_EXECUTEX64.len())?; let wowptr = exec_ptr(asmcompiled::MIGRATE_WOWNATIVEX.len())?; unsafe{std::ptr::copy(asmcompiled::MIGRATE_EXECUTEX64.as_ptr(),exx64ptr as *mut u8,asmcompiled::MIGRATE_EXECUTEX64.len())}; unsafe{std::ptr::copy(asmcompiled::MIGRATE_WOWNATIVEX.as_ptr(),wowptr as *mut u8,asmcompiled::MIGRATE_WOWNATIVEX.len())}; let fnexex64:asmcompiled::EXECUTEX64=unsafe{std::mem::transmute(exx64ptr)}; let fnwow:asmcompiled::X64Function=unsafe{std::mem::transmute(wowptr)}; let mut thread_id:u64=0; let mut par = asmcompiled::WOW64CONTEXT64{ hProcess: proc as u64, lpStartAddress: entry_point, lpParameter: mem as u64, hThread: (&mut thread_id as *mut u64) as u64 }; println!("Injection?"); let test = unsafe {fnexex64(fnwow,&mut par as *mut asmcompiled::WOW64CONTEXT64 as *mut c_void as u32)}; println!("Did we inject? return:{},thread_id:{}",test,thread_id); Ok(()) } ```

My cpu is a AMD Ryzen 5 3600.

Now to the good bits:

Before L54 54_before After L54 54_after Warning, After the line in the previous picture executes. The tid (I'm assuming, that's a thread id) would be my rust execution threads, in the injector. I have three other Threads, with the Start address ntdll.dll!TppWorkerThread. I probably have three LoadLibraryA calls on ntdll.

warning

OJ commented 3 years ago

@C0D3-M4513R the code as it currently stands works in Metepreter, so I'm not clear on what you're asking here.

Are you asking for help with implementing this feature in your own project?

C0D3-M4513R commented 3 years ago

Oops. should have clarified that. I am searching for ANY way, to inject a thread, into a x64 process, from a x86 process, with the restriction being, that the start address has to be 64 bits. This is my attempt, at implementing, the way meterpreter does things, in my rust project.

Ideally I'd want something, similar to NtWow64QueryInformationProcess64, but for creating threads. Since that doesn't exist, I searched for other ways. Compiling Assembly in stable rust is fairly difficult. (Without going into the whole compiling x64 and x86 in the same binary thing.) Therefore, I simply copied an exising, compiled solution, which does, only what I want.

This issue should be for fixing my implementation, of calling the meterpreter machinecode. If you have something else, that would work for this case, I'd happy to accept anything, and close/put this issue on hold, in favor of that.

C0D3-M4513R commented 3 years ago

After debugging, I found, that the far jumps, simply don't do, as they should. https://github.com/rapid7/metasploit-framework/blob/a1eef6a2c194284fe5e90be602eaa6417db51651/external/source/shellcode/windows/x86/src/migrate/executex64.asm#L54 (That says jmp fword ptr [edx] in my ide) The contents, of edx point, to a dword, with the address of native_x64. The execution ends somewhere entirely different (In some switch statement inside RtlIpv6AddressToStringA in x86 ntdll. ).

I can set the EIP manually to native_x64, and it executes the code, it is supposed to, but probably then not in x64 mode, correct? Therefore, running the code in windows/x64/src/migrate/remotethread.asm doesn't work at all.

timwr commented 3 years ago

I believe this is called heavens gate: https://medium.com/@fsx30/hooking-heavens-gate-a-wow64-hooking-technique-5235e1aeed73 http://rce.co/knockin-on-heavens-gate-dynamic-processor-mode-switching/

timwr commented 3 years ago

If you're using this array: https://github.com/rapid7/meterpreter/blob/d338f702ce8cb7f4e550f005ececaf5f3cadd2bc/source/common/arch/win/i386/base_inject.c#L15 You might be missing this change: https://github.com/rapid7/metasploit-framework/commit/f17b28930dd926b93915a115f1117825f4c594db Try this array: https://github.com/rapid7/metasploit-payloads/blob/45e98c85a3dc2b55d8e907a87c0555a89e3a1aa3/c/meterpreter/source/metsrv/base_inject.c#L8?

C0D3-M4513R commented 3 years ago

Does anyone read my messages? I was using the wrong array, yes, but the difference only starts, AFTER the jump, that is troubling me. The issue, is still the same. My after performing the jump in go_all_native, the code still jumps into ntdll.dll code (which then tries to return, and that throws an exeption). If you looked, at the change (in the assembly), and where the change is, it should have been obvious. @timwr

C0D3-M4513R commented 3 years ago

Ok, so I tried another heaven's gate implementation. https://github.com/JustasMasiulis/wow64pp/blob/4573048c41657cf66555a87a736720ab8712cbdd/include/wow64pp.hpp#L693 Here it fails, in line 699, on byte 0xCB, which is a retf. This also lands me at a retn 28h in x86 ntdll.dll code. I also tried, compiling the assembly myself, to replace the jmp, with something else. Everything I tried, had the same problem, of resulting in a memory read or write error.

C0D3-M4513R commented 3 years ago

I have now scrapped the idea, of even thinking about taking any arguments, or performing any system calls. I just want to change the processor mode, from x86, to x64. I wrote this assembly, which I execute:

;minimal example
[BITS 32]
start:
    int3;catch debugger

    call x64 ;try x64 mode
[BITS 64]
    nop;have a next address 
    call x86
[BITS 32]
    nop
    retn
x64:
    push 0x33
    retf
        ;here I get an error: (address of retf): The instruction at (address of retf) referenced memory at 0xFFFFFFFFFFFFFFFF. The memory could not be read (exc.code c0000005, tid 53056)
x86:
[BITS 64]
    push 0x23
    retf
OJ commented 3 years ago

Does anyone read my messages?

Clearly yes, otherwise nobody would be trying to help. I would recommend not getting annoyed or snarky, especially given you've opened up a Github issue on MSF, marked it as a bug hence implying there's in issue in MSF, then done nothing but ask for help implementing features in your own software.

This is not an issue in Metasploit, and hence this issue is being closed and further comments restricted to contributors only. If you want to discuss this further feel free to jump on the Metasploit slack channel as you might find people open to helping you in there. For future reference, please use Github issues only when there are legitimate issues with Metasploit. Thank you.