1
mirror of https://github.com/rapid7/metasploit-payloads synced 2024-11-26 17:41:08 +01:00

Pivot stability, fixes, tidies, etc

This commit is contained in:
OJ 2017-07-18 20:58:23 +10:00
parent 5a04de0780
commit 415665ef59
No known key found for this signature in database
GPG Key ID: D5DC61FB93260597
11 changed files with 165 additions and 34 deletions

View File

@ -12,7 +12,7 @@
#include <stdio.h>
#include <time.h>
#define SAFE_FREE(x) {free(x); x = NULL;}
#define SAFE_FREE(x) if(x){free(x);x=NULL;}
#include <winsock2.h>
#include <windows.h>

View File

@ -23,19 +23,23 @@ DWORD pivot_tree_add_node(PivotNode* parent, PivotNode* node)
{
if (parent->left == NULL)
{
dprintf("[PIVOTTREE] Adding node to left");
parent->left = node;
return ERROR_SUCCESS;
}
dprintf("[PIVOTTREE] Adding node to left subtree");
return pivot_tree_add_node(parent->left, node);
}
if (parent->right == NULL)
{
dprintf("[PIVOTTREE] Adding node to right");
parent->right = node;
return ERROR_SUCCESS;
}
dprintf("[PIVOTTREE] Adding node to right subtree");
return pivot_tree_add_node(parent->right, node);
}
@ -44,7 +48,7 @@ DWORD pivot_tree_add(PivotTree* tree, LPBYTE guid, PivotContext* ctx)
PivotNode* node = (PivotNode*)calloc(1, sizeof(PivotNode));
#ifdef DEBUGTRACE
PUCHAR h = (PUCHAR)&guid[0];
dprintf("[PIVOTTREE] Adding Session GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
dprintf("[PIVOTTREE] Adding GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]);
#endif
@ -74,14 +78,16 @@ PivotNode* pivot_tree_largest_node(PivotNode* node)
return pivot_tree_largest_node(node->right);
}
DWORD pivot_tree_remove_node(PivotNode* parent, LPBYTE guid)
PivotContext* pivot_tree_remove_node(PivotNode* parent, LPBYTE guid)
{
int cmp = memcmp(parent->guid, guid, sizeof(parent->guid));
if (cmp < 0 && parent->left != NULL)
{
dprintf("[PIVOTTREE] Removing from left subtree");
int cmp = memcmp(parent->left->guid, guid, sizeof(parent->guid));
if (cmp == 0)
{
dprintf("[PIVOTTREE] Removing right child");
PivotNode* remove = parent->left;
PivotNode* left = remove->left;
PivotNode* largest = pivot_tree_largest_node(left);
@ -96,8 +102,9 @@ DWORD pivot_tree_remove_node(PivotNode* parent, LPBYTE guid)
parent->left = remove->right;
}
PivotContext* context = remove->ctx;
free(remove);
return ERROR_SUCCESS;
return context;
}
return pivot_tree_remove_node(parent->left, guid);
@ -105,9 +112,11 @@ DWORD pivot_tree_remove_node(PivotNode* parent, LPBYTE guid)
if (cmp > 0 && parent->right != NULL)
{
dprintf("[PIVOTTREE] Removing from right subtree");
int cmp = memcmp(parent->right->guid, guid, sizeof(parent->guid));
if (cmp == 0)
{
dprintf("[PIVOTTREE] Removing right child");
PivotNode* remove = parent->right;
PivotNode* left = remove->left;
PivotNode* largest = pivot_tree_largest_node(left);
@ -122,27 +131,35 @@ DWORD pivot_tree_remove_node(PivotNode* parent, LPBYTE guid)
parent->right = remove->right;
}
PivotContext* context = remove->ctx;
free(remove);
return ERROR_SUCCESS;
return context;
}
return pivot_tree_remove_node(parent->left, guid);
}
return ERROR_SUCCESS;
return NULL;
}
DWORD pivot_tree_remove(PivotTree* tree, LPBYTE guid)
PivotContext* pivot_tree_remove(PivotTree* tree, LPBYTE guid)
{
#ifdef DEBUGTRACE
PUCHAR h = (PUCHAR)&guid[0];
dprintf("[PIVOTTREE] Removing GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]);
#endif
if (tree->head == NULL)
{
return ERROR_SUCCESS;
return NULL;
}
int cmp = memcmp(tree->head->guid, guid, sizeof(tree->head->guid));
if (cmp == 0)
{
dprintf("[PIVOTTREE] Removing head node");
PivotNode* remove = tree->head;
PivotNode* left = tree->head->left;
PivotNode* largest = pivot_tree_largest_node(left);
@ -157,10 +174,12 @@ DWORD pivot_tree_remove(PivotTree* tree, LPBYTE guid)
tree->head = tree->head->right;
}
PivotContext* context = remove->ctx;
free(remove);
return ERROR_SUCCESS;
return context;
}
dprintf("[PIVOTTREE] Removing non-head node");
return pivot_tree_remove_node(tree->head, guid);
}
@ -174,24 +193,27 @@ PivotContext* pivot_tree_find_node(PivotNode* node, LPBYTE guid)
#ifdef DEBUGTRACE
PUCHAR h = (PUCHAR)&guid[0];
dprintf("[PIVOTTREE] Session GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
dprintf("[PIVOTTREE] Saerch GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]);
h = node->guid;
dprintf("[PIVOTTREE] Node GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
dprintf("[PIVOTTREE] Node GUID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15]);
#endif
int cmp = memcmp(guid, node->guid, sizeof(guid));
int cmp = memcmp(node->guid, guid, sizeof(node->guid));
if (cmp == 0)
{
dprintf("[PIVOTTREE] node found");
return node->ctx;
}
if (cmp < 0)
{
dprintf("[PIVOTTREE] Searching left subtree");
return pivot_tree_find_node(node->left, guid);
}
dprintf("[PIVOTTREE] Searching right subtree");
return pivot_tree_find_node(node->right, guid);
}

View File

@ -2,10 +2,12 @@
#define _METERPRETER_PIVOT_TREE
typedef DWORD(*PivotWritePacket)(LPVOID state, LPBYTE rawPacket, DWORD rawPacketLength);
typedef DWORD(*PivotRemove)(LPVOID state);
typedef struct _PivotContext
{
PivotWritePacket packet_write;
PivotRemove remove;
LPVOID state;
} PivotContext;
@ -20,7 +22,7 @@ typedef void(*PivotTreeTraverseCallback)(LPBYTE guid, PivotContext* ctx, LPVOID
PivotTree* pivot_tree_create();
DWORD pivot_tree_add(PivotTree* tree, LPBYTE guid, PivotContext* ctx);
DWORD pivot_tree_remove(PivotTree* tree, LPBYTE guid);
PivotContext* pivot_tree_remove(PivotTree* tree, LPBYTE guid);
PivotContext* pivot_tree_find(PivotTree* tree, LPBYTE guid);
void pivot_tree_traverse(PivotTree* tree, PivotTreeTraverseCallback callback, LPVOID state);
void pivot_tree_destroy(PivotTree* tree);

View File

@ -27,7 +27,8 @@ Remote* remote_allocate()
remote->lock = lock;
remote->enc_ctx = NULL;
remote->pivots = pivot_tree_create();
remote->pivot_sessions = pivot_tree_create();
remote->pivot_listeners = pivot_tree_create();
dprintf("[REMOTE] remote created %p", remote);
return remote;
@ -38,9 +39,14 @@ Remote* remote_allocate()
lock_destroy(lock);
}
if (remote->pivots)
if (remote->pivot_sessions)
{
pivot_tree_destroy(remote->pivots);
pivot_tree_destroy(remote->pivot_sessions);
}
if (remote->pivot_listeners)
{
pivot_tree_destroy(remote->pivot_listeners);
}
if (remote)
@ -59,7 +65,8 @@ Remote* remote_allocate()
VOID remote_deallocate(Remote * remote)
{
free_encryption_context(remote);
pivot_tree_destroy(remote->pivots);
pivot_tree_destroy(remote->pivot_sessions);
pivot_tree_destroy(remote->pivot_listeners);
if (remote->lock)
{

View File

@ -160,7 +160,8 @@ typedef struct _Remote
int sess_expiry_end; ///! Unix timestamp for when the server should shut down.
int sess_start_time; ///! Unix timestamp representing the session startup time.
PivotTree* pivots; ///! Collection of active Meterpreter session pivots.
PivotTree* pivot_sessions; ///! Collection of active Meterpreter session pivots.
PivotTree* pivot_listeners; ///! Collection of active Meterpreter pivot listeners.
PacketEncryptionContext* enc_ctx; ///! Reference to the packet encryption context.
} Remote;

View File

@ -23,6 +23,7 @@ Command customCommands[] =
COMMAND_REQ("core_set_session_guid", request_core_set_session_guid),
COMMAND_REQ("core_set_uuid", request_core_set_uuid),
COMMAND_REQ("core_pivot_add", request_core_pivot_add),
COMMAND_REQ("core_pivot_remove", request_core_pivot_remove),
COMMAND_INLINE_REP("core_patch_url", request_core_patch_url),
COMMAND_TERMINATOR
};

View File

@ -15,4 +15,31 @@ DWORD request_core_pivot_add(Remote* remote, Packet* packet)
{
// Right now we only support named pipe pivotes, so just go straight there
return request_core_pivot_add_named_pipe(remote, packet);
}
/*!
* @brief Remove a pivot point from the current Meterpreter instance.
* @remote Pointer to the \c Remote instance.
* @remote Pointer to the incoming request \c Packet instance.
* @return Indication of error or success.
*/
DWORD request_core_pivot_remove(Remote* remote, Packet* packet)
{
DWORD result = ERROR_NOT_FOUND;
LPBYTE pivotId = packet_get_tlv_value_raw(packet, TLV_TYPE_PIVOT_ID);
if (pivotId != NULL)
{
PivotContext* ctx = pivot_tree_remove(remote->pivot_listeners, pivotId);
if (ctx != NULL)
{
ctx->remove(ctx->state);
free(ctx);
result = ERROR_SUCCESS;
}
}
packet_transmit_empty_response(remote, packet, result);
return result;
}

View File

@ -1,6 +1,7 @@
#ifndef _METERPRETER_SERVER_PIVOT
#define _METERPRETER_SERVER_PIVOT
DWORD request_core_pivot_add(Remote* remote, Packet* packet);
DWORD request_core_pivot_add(Remote* remote, Packet* packet);
DWORD request_core_pivot_remove(Remote* remote, Packet* packet);
#endif

View File

@ -16,6 +16,7 @@ typedef struct _NamedPipeContext
OVERLAPPED write_overlap;
char name[PIPE_NAME_SIZE];
GUID pivot_id;
GUID pivot_session_guid;
Remote* remote;
HANDLE pipe;
BOOL connecting;
@ -30,6 +31,7 @@ typedef struct _NamedPipeContext
} NamedPipeContext;
static DWORD server_notify(Remote* remote, LPVOID entryContext, LPVOID threadContext);
static DWORD server_destroy(HANDLE waitable, LPVOID entryContext, LPVOID threadContext);
typedef BOOL (WINAPI *PAddMandatoryAce)(PACL pAcl, DWORD dwAceRevision, DWORD dwAceFlags, DWORD dwMandatoryPolicy, PSID pLabelSid);
static BOOL WINAPI AddMandatoryAce(PACL pAcl, DWORD dwAceRevision, DWORD dwAceFlags, DWORD dwMandatoryPolicy, PSID pLabelSid)
@ -57,6 +59,38 @@ static BOOL WINAPI AddMandatoryAce(PACL pAcl, DWORD dwAceRevision, DWORD dwAceFl
return TRUE;
}
static DWORD server_destroy(HANDLE waitable, LPVOID entryContext, LPVOID threadContext)
{
NamedPipeContext* ctx = (NamedPipeContext*)entryContext;
if (ctx != NULL)
{
dprintf("[PIVOT] Cleaning up the pipe pivot context");
lock_acquire(ctx->remote->lock);
CloseHandle(ctx->pipe);
CloseHandle(ctx->read_overlap.hEvent);
CloseHandle(ctx->write_overlap.hEvent);
SAFE_FREE(ctx->stage_data);
lock_release(ctx->remote->lock);
dprintf("[PIVOT] Cleaned up the pipe pivot context");
}
return ERROR_SUCCESS;
}
static void terminate_pipe(NamedPipeContext* ctx)
{
if (ctx != NULL)
{
scheduler_signal_waitable(ctx->read_overlap.hEvent, Stop);
}
}
static DWORD remove_listener(LPVOID state)
{
dprintf("[PIVOT] removing named pipe listener");
terminate_pipe((NamedPipeContext*)state);
return ERROR_SUCCESS;
}
static DWORD read_pipe_to_packet(NamedPipeContext* ctx, LPBYTE source, DWORD sourceSize)
{
// Make sure we have the space to handle the incoming packet
@ -315,7 +349,8 @@ DWORD create_pipe_server_instance(NamedPipeContext* ctx)
}
dprintf("[NP-SERVER] Inserting the named pipe schedule entry");
scheduler_insert_waitable(ctx->read_overlap.hEvent, ctx, NULL, server_notify, NULL);
scheduler_insert_waitable(ctx->read_overlap.hEvent, ctx, NULL, server_notify, server_destroy);
//scheduler_insert_waitable(ctx->read_overlap.hEvent, ctx, NULL, server_notify, NULL);
} while (0);
return dwResult;
@ -429,8 +464,23 @@ static DWORD server_notify(Remote* remote, LPVOID entryContext, LPVOID threadCon
else if (dwResult == ERROR_BROKEN_PIPE)
{
dprintf("[NP-SERVER] the client appears to have bailed out, disconnecting...");
// Reset the read event so that our schedular loop witll exit properly
ResetEvent(serverCtx->read_overlap.hEvent);
// TODO: do some clean up of stuf here.
// Prepare the notification packet for dispatching
Packet* notification = packet_create(PACKET_TLV_TYPE_REQUEST, "core_pivot_session_died");
packet_add_tlv_raw(notification, TLV_TYPE_SESSION_GUID, (LPVOID)&serverCtx->pivot_session_guid, sizeof(serverCtx->pivot_session_guid));
// Clean up the pivot context
PivotContext* pivotCtx = pivot_tree_remove(remote->pivot_sessions, (LPBYTE)&serverCtx->pivot_session_guid);
SAFE_FREE(pivotCtx);
// Clean up all the named pipe context stuff.
terminate_pipe(serverCtx);
// Inform MSF of the dead session
packet_transmit(serverCtx->remote, notification, NULL);
return ERROR_BROKEN_PIPE;
}
break;
@ -454,6 +504,7 @@ static DWORD server_notify(Remote* remote, LPVOID entryContext, LPVOID threadCon
nextCtx->remote = serverCtx->remote;
nextCtx->stage_data = serverCtx->stage_data;
nextCtx->stage_data_size = serverCtx->stage_data_size;
memcpy_s(&nextCtx->pivot_id, sizeof(nextCtx->pivot_id), &serverCtx->pivot_id, sizeof(nextCtx->pivot_id));
memcpy_s(&nextCtx->name, PIPE_NAME_SIZE, &serverCtx->name, PIPE_NAME_SIZE);
// create a new pipe for the next connection
@ -466,6 +517,16 @@ static DWORD server_notify(Remote* remote, LPVOID entryContext, LPVOID threadCon
serverCtx->established = TRUE;
// The current listener cotnext in the listeners tree points to the server instance that has now
// become a client instance due to the new connection. Therefore we need to update the pivot tree context
// to point to the new listener on the named pipe.
PivotContext* listenerCtx = pivot_tree_find(remote->pivot_listeners, (LPBYTE)&serverCtx->pivot_id);
if (listenerCtx != NULL)
{
dprintf("[NP-SERVER] Updating the listener context in the pivot tree");
listenerCtx->state = nextCtx;
}
// Time to stage the data
if (serverCtx->stage_data && serverCtx->stage_data_size > 0)
{
@ -475,25 +536,29 @@ static DWORD server_notify(Remote* remote, LPVOID entryContext, LPVOID threadCon
// send the stage
named_pipe_write_raw(serverCtx, serverCtx->stage_data, serverCtx->stage_data_size);
// to "hand over" the stage data, set the existing pointer to NULL so that things don't get freed
// when they shouldn't be.
serverCtx->stage_data = NULL;
}
// We need to generate a new session GUID and inform metasploit of the new session
GUID guid;
CoCreateGuid(&guid);
CoCreateGuid(&serverCtx->pivot_session_guid);
// swizzle the values around so that endianness isn't an issue before casting to a block of bytes
guid.Data1 = htonl(guid.Data1);
guid.Data2 = htons(guid.Data2);
guid.Data3 = htons(guid.Data3);
serverCtx->pivot_session_guid.Data1 = htonl(serverCtx->pivot_session_guid.Data1);
serverCtx->pivot_session_guid.Data2 = htons(serverCtx->pivot_session_guid.Data2);
serverCtx->pivot_session_guid.Data3 = htons(serverCtx->pivot_session_guid.Data3);
Packet* notification = packet_create(PACKET_TLV_TYPE_REQUEST, "core_pivot_session_new");
packet_add_tlv_raw(notification, TLV_TYPE_SESSION_GUID, (LPVOID)&guid, sizeof(guid));
packet_add_tlv_raw(notification, TLV_TYPE_SESSION_GUID, (LPVOID)&serverCtx->pivot_session_guid, sizeof(serverCtx->pivot_session_guid));
packet_add_tlv_raw(notification, TLV_TYPE_PIVOT_ID, (LPVOID)&serverCtx->pivot_id, sizeof(serverCtx->pivot_id));
packet_transmit(serverCtx->remote, notification, NULL);
PivotContext* pivotContext = (PivotContext*)calloc(1, sizeof(PivotContext));
pivotContext->state = serverCtx;
pivotContext->packet_write = named_pipe_write_raw;
pivot_tree_add(serverCtx->remote->pivots, (LPBYTE)&guid, pivotContext);
pivot_tree_add(remote->pivot_sessions, (LPBYTE)&serverCtx->pivot_session_guid, pivotContext);
}
if (bytesProcessed > 0)
@ -602,6 +667,10 @@ DWORD request_core_pivot_add_named_pipe(Remote* remote, Packet* packet)
if (dwResult == ERROR_SUCCESS)
{
dprintf("[NP-SERVER] request_net_named_pipe_server_channel_open. named pipe server %s", namedPipeName);
PivotContext* pivotCtx = (PivotContext*)calloc(1, sizeof(PivotContext));
pivotCtx->state = ctx;
pivotCtx->remove = remove_listener;
pivot_tree_add(remote->pivot_listeners, pivotId, pivotCtx);
}
} while (0);

View File

@ -32,7 +32,7 @@ static DWORD server_pipe_poll(Remote* remote, long timeout)
lock_acquire(remote->lock);
dprintf("[NP DISPATCH] testing for data on the pipe, making sure there's enough for a packet header");
vdprintf("[NP DISPATCH] testing for data on the pipe, making sure there's enough for a packet header");
BOOL ready = PeekNamedPipe(ctx->pipe, NULL, 0, NULL, &bytesAvailable, NULL) && bytesAvailable >= sizeof(PacketHeader);
DWORD result = GetLastError();
@ -40,7 +40,7 @@ static DWORD server_pipe_poll(Remote* remote, long timeout)
if (ready)
{
dprintf("[NP DISPATCH] pipe data found %u bytes", bytesAvailable);
vdprintf("[NP DISPATCH] pipe data found %u bytes", bytesAvailable);
result = ERROR_SUCCESS;
}
else
@ -48,7 +48,7 @@ static DWORD server_pipe_poll(Remote* remote, long timeout)
if (result != ERROR_BROKEN_PIPE)
{
// simulate a wait so that we don't bash the crap out of the CPU?
dprintf("[NP DISPATCH] pipe data not found, sleeping (error %u)", GetLastError());
vdprintf("[NP DISPATCH] pipe data not found, sleeping (error %u)", GetLastError());
Sleep(timeout);
result = ERROR_NO_DATA;
}
@ -175,9 +175,9 @@ static DWORD packet_receive_named_pipe(Remote *remote, Packet **packet)
}
payloadLength = ntohl(header.length) - sizeof(TlvHeader);
vdprintf("[PIPE] Payload length is %d", payloadLength);
dprintf("[PIPE] Payload length is %d", payloadLength);
DWORD packetSize = sizeof(PacketHeader) + payloadLength;
vdprintf("[PIPE] total buffer size for the packet is %d", packetSize);
dprintf("[PIPE] total buffer size for the packet is %d", packetSize);
payloadBytesLeft = payloadLength;
// Allocate the payload
@ -199,6 +199,7 @@ static DWORD packet_receive_named_pipe(Remote *remote, Packet **packet)
// Read the payload
DWORD bytesAvailable = 0;
dprintf("[PIPE] Beginning read loop for a total of %u", payloadBytesLeft);
while (payloadBytesLeft > 0)
{
dprintf("[PIPE] Trying to read %u (0x%x) bytes", payloadBytesLeft, payloadBytesLeft);

View File

@ -475,7 +475,7 @@ static DWORD packet_receive(Remote *remote, Packet **packet)
else
{
dprintf("[TCP] Session GUIDs don't match, looking for a pivot");
PivotContext* pivotCtx = pivot_tree_find(remote->pivots, header.session_guid);
PivotContext* pivotCtx = pivot_tree_find(remote->pivot_sessions, header.session_guid);
if (pivotCtx != NULL)
{
dprintf("[TCP] Pivot found, dispatching packet");