Skip to content

Instantly share code, notes, and snippets.

@olliencc
Last active July 5, 2024 23:40
Show Gist options
  • Save olliencc/df200e0049fa17036d9f867e024f57ad to your computer and use it in GitHub Desktop.
Save olliencc/df200e0049fa17036d9f867e024f57ad to your computer and use it in GitHub Desktop.
Detect Windows threads which are impersonating
/*
TEB Detect Impersonating Threads for Microsoft Windows
Released as open source by NCC Group Plc - http://www.nccgroup.com/
Developed by Ollie Whitehouse, ollie dot whitehouse at nccgroup dot com
Released under AGPL see LICENSE for more information
*/
#pragma once
#include "stdafx.h"
// Globals
HANDLE hProcess;
TCHAR strErrMsg[1024];
DWORD dwModuleRelocs = 0;
DWORD dwCountError = 0;
DWORD dwCountOK = 0;
DWORD dwOpen = 0;
// Manual import
typedef NTSTATUS(WINAPI* NTQUERYINFOMATIONTHREAD)(HANDLE, LONG, PVOID, ULONG, PULONG);
NTQUERYINFOMATIONTHREAD myNtQueryInformationThread = (NTQUERYINFOMATIONTHREAD)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "NtQueryInformationThread");
// Structures to hold process information
#pragma pack(push, 1)
struct procNfoStuct {
DWORD PID;
TCHAR Name[MAX_PATH];
unsigned long long TotalExecMem = 0;
};
#pragma pack(pop)
procNfoStuct Procs[4098];
DWORD NumOfProcs = 0;
//
// Function : SetDebugPrivilege
// Role : Gets privs for our process
// Notes :
//
BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lPriv)
{
LUID luid;
TOKEN_PRIVILEGES privs;
HANDLE hToken = NULL;
DWORD dwBufLen = 0;
char buf[1024];
ZeroMemory(&luid, sizeof(luid));
if (!LookupPrivilegeValue(NULL, lPriv, &luid)) return false;
privs.PrivilegeCount = 1;
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
memcpy(&privs.Privileges[0].Luid, &luid, sizeof(privs.Privileges[0].Luid));
if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken))
return false;
if (!AdjustTokenPrivileges(hToken, FALSE, &privs,
sizeof(buf), (PTOKEN_PRIVILEGES)buf, &dwBufLen))
return false;
CloseHandle(hProcess);
CloseHandle(hToken);
return true;
}
void AnalyzeTEB(HANDLE hProcess, HANDLE hThread, DWORD dwPID, TCHAR *cProcess, LPCVOID ptrTEB) {
MYTEB myTEB;
SIZE_T szRead=0;
memset(&myTEB, 0x00, sizeof(myTEB));
if (ReadProcessMemory(hProcess, ptrTEB, &myTEB, sizeof(myTEB), &szRead) == TRUE) {
if(szRead != sizeof(myTEB)) fwprintf(stdout, _TEXT("[i] [%d][%s] Size Delta\n"), dwPID, cProcess);
// fwprintf(stdout, _TEXT("[i] [%d][%s] Got TEB %llx\n"), dwPID, cProcess, ptrTEB);
//if(myTEB.InitialThread>0)fwprintf(stdout, _TEXT("[i] [%d][%s] Initial thread: %d\n"), dwPID, cProcess, myTEB.InitialThread);
if (myTEB.CountOfOwnedCriticalSections >0) fwprintf(stdout, _TEXT("[i] [%d][%s] Count of Owned Critical Sections: %d\n"), dwPID, cProcess, myTEB.CountOfOwnedCriticalSections);
if(myTEB.IsImpersonating > 0 ) fwprintf(stdout, _TEXT("[i] [%d][%s] is impersonating\n"), dwPID, cProcess);
}
}
/// <summary>
/// Analyze the process and its memory regions
/// </summary>
/// <param name="dwPID">Process ID</param>
void AnalyzeProc(DWORD dwPID)
{
DWORD dwRet, dwMods;
HANDLE hProcess;
HMODULE hModule[4096];
TCHAR cProcess[MAX_PATH]; // Process name
BOOL bIsWow64 = FALSE;
BOOL bIsWow64Other = FALSE;
DWORD dwRES = 0;
// Get process handle by hook or by crook
hProcess = OpenProcess(PROCESS_ALL_ACCESS | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
if (hProcess == NULL)
{
if (GetLastError() == 5) {
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
if (hProcess == NULL) {
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
if (hProcess == NULL) {
fwprintf(stderr, _TEXT("[!] [%d][UNKNOWN] Failed to OpenProcess - %d\n"), dwPID, GetLastError());
dwCountError++;
return;
}
}
}
else {
fwprintf(stderr, _TEXT("[!] [%d][UNKNOWN] Failed to OpenProcess - %d\n"), dwPID, GetLastError());
dwCountError++;
return;
}
}
// Enumerate the process modules
if (EnumProcessModules(hProcess, hModule, 4096 * sizeof(HMODULE), &dwRet) == FALSE)
{
DWORD dwSz = MAX_PATH;
if (QueryFullProcessImageName(hProcess, 0, cProcess, &dwSz) == TRUE) {
fwprintf(stdout, _TEXT("[i] [%d][%s] not analysed %d\n"), dwPID, cProcess, GetLastError());
dwOpen++;
}
else {
fwprintf(stdout, _TEXT("[i] [%d][%s] not analysed %d\n"), dwPID, _TEXT("UNKNOWN"), GetLastError());
dwOpen++;
}
if (GetLastError() == 299) {
//fprintf(stderr, "64bit process and we're 32bit - sad panda! skipping PID %d\n", dwPID);
}
else {
//fprintf(stderr, "Error in EnumProcessModules(%d),%d\n", dwPID, GetLastError());
}
dwCountError++;
return;
}
dwMods = dwRet / sizeof(HMODULE);
// Get the processes name from the first module returned by the above
GetModuleBaseName(hProcess, hModule[0], cProcess, MAX_PATH);
Procs[NumOfProcs].PID = dwPID;
_tcscpy_s(Procs[NumOfProcs].Name, MAX_PATH, cProcess);
//fwprintf(stdout, _TEXT("[i] [%d][%s] analyzing\n"), dwPID, cProcess);
NumOfProcs++;
//
// Get the
//
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (h != INVALID_HANDLE_VALUE) {
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te)) {
do {
if (te.th32OwnerProcessID == dwPID && GetProcessId(NULL) != dwPID) {
HANDLE hThread = INVALID_HANDLE_VALUE;
hThread = OpenThread(THREAD_QUERY_INFORMATION, false, te.th32ThreadID);
if (hThread != INVALID_HANDLE_VALUE) {
THREAD_BASIC_INFORMATION threadTBI;
NTSTATUS statRes = myNtQueryInformationThread(hThread, (THREADINFOCLASS)(0), &threadTBI, sizeof(threadTBI), NULL);
if (statRes == 0) {
//fwprintf(stdout, _TEXT("[i] [%d][%s] Address of TEB %llx\n"), dwPID, cProcess, threadTBI.TebBaseAddress);
AnalyzeTEB(hProcess, hThread, dwPID,cProcess,threadTBI.TebBaseAddress);
}
else
{
fwprintf(stdout, _TEXT("[!] [%d][%s] Failed to get TBI %d\n"), dwPID, cProcess,(DWORD)statRes);
}
CloseHandle(hThread);
}
}
} while (Thread32Next(h, &te));
}
CloseHandle(h);
}
dwCountOK++;
CloseHandle(hProcess);
}
/// <summary>
/// Enumerate all the processes on the system and
/// pass off to the analysis function
/// </summary>
void EnumerateProcesses()
{
DWORD dwPIDArray[4096], dwRet, dwPIDS, intCount;
NumOfProcs = 0;
// Privs
SetPrivilege(GetCurrentProcess(), SE_DEBUG_NAME);
// Be clean
memset(Procs, 0x00, sizeof(Procs));
//
// Enumerate
//
if (EnumProcesses(dwPIDArray, 4096 * sizeof(DWORD), &dwRet) == 0)
{
DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, strErrMsg, 1023, NULL);
if (dwRet != 0) {
_ftprintf(stderr, TEXT("[!] EnumProcesses() failed - %s"), strErrMsg);
}
else
{
_ftprintf(stderr, TEXT("[!] EnumProcesses() - Error: %d\n"), GetLastError());
}
return;
}
// Total nuber of process IDs
dwPIDS = dwRet / sizeof(DWORD);
//
// Analyze
//
for (intCount = 0; intCount < dwPIDS; intCount++)
{
//fwprintf(stdout, _TEXT("[i] Analyzing PID %d\n"), dwPIDArray[intCount]);
AnalyzeProc(dwPIDArray[intCount]);
}
fwprintf(stdout, _TEXT("[i] Total of %d processes - didn't open %d \n"), dwPIDS, dwOpen);
}
/*
TEB Detect Impersonating Threads for Microsoft Windows
Released as open source by NCC Group Plc - http://www.nccgroup.com/
Developed by Ollie Whitehouse, ollie dot whitehouse at nccgroup dot com
Released under AGPL see LICENSE for more information
*/
#pragma once
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Winternl.h>
#include <Psapi.h>
#include <Aclapi.h>
#include <tlhelp32.h>
#include <wtsapi32.h>
#include <strsafe.h>
#include <winevt.h>
#include <evntprov.h>
//
extern bool bFirstRun;
extern bool bConsole;
extern bool bService;
// https://github.com/edouarda/thread_explorer/blob/master/thread_explorer/thread_explorer.cpp
typedef struct _THREAD_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PTEB TebBaseAddress;
CLIENT_ID ClientId;
ULONG_PTR AffinityMask;
KPRIORITY Priority;
LONG BasePriority;
} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
// http://daaxr.blogspot.com/2016/07/teb-structure-for-windows-10-pro-x64.html
typedef struct _MYNT_TIB
{
EXCEPTION_REGISTRATION_RECORD* ExceptionList;
void* StackBase;
void* StackLimit;
void* SubSystemTib;
union
{
void* FiberData;
unsigned int Version;
};
void* ArbitraryUserPointer;
_MYNT_TIB* Self;
} MYNT_TIB, * PMYNT_TIB;
typedef struct _MYCLIENT_ID
{
void* UniqueProcess;
void* UniqueThread;
} MYCLIENT_ID, * PMYCLIENT_ID;
typedef struct _GDI_TEB_BATCH
{
unsigned __int32 Offset : 31;
unsigned __int32 HasRenderingCommand : 1;
unsigned __int64 HDC;
unsigned int Buffer[310];
} GDI_TEB_BATCH, * PGDI_TEB_BATCH;
typedef const struct _TEB_ACTIVE_FRAME_CONTEXT
{
unsigned int Flags;
const char* FrameName;
} TEB_ACTIVE_FRAME_CONTEXT, * PTEB_ACTIVE_FRAME_CONTEXT;
typedef struct _TEB_ACTIVE_FRAME
{
unsigned int Flags;
_TEB_ACTIVE_FRAME* Previous;
_TEB_ACTIVE_FRAME_CONTEXT* Context;
} TEB_ACTIVE_FRAME, * PTEB_ACTIVE_FRAME;
typedef struct _myTEB
{
MYNT_TIB NtTib;
void* EnvironmentPointer;
CLIENT_ID ClientId;
void* ActiveRpcHandle;
void* ThreadLocalStoragePointer;
PEB* ProcessEnvironmentBlock;
unsigned int LastErrorValue;
unsigned int CountOfOwnedCriticalSections;
void* CsrClientThread;
void* Win32ThreadInfo;
unsigned int User32Reserved[26];
unsigned int UserReserved[5];
void* WOW32Reserved;
unsigned int CurrentLocale;
unsigned int FpSoftwareStatusRegister;
void* ReservedForDebuggerInstrumentation[16];
void* SystemReserved1[38];
int ExceptionCode;
char Padding0[4];
__int64* ActivationContextStackPointer;
unsigned __int64 InstrumentationCallbackSp;
unsigned __int64 InstrumentationCallbackPreviousPc;
unsigned __int64 InstrumentationCallbackPreviousSp;
unsigned int TxFsContext;
char InstrumentationCallbackDisabled;
char Padding1[3];
GDI_TEB_BATCH GdiTebBatch;
CLIENT_ID RealClientId;
void* GdiCachedProcessHandle;
unsigned int GdiClientPID;
unsigned int GdiClientTID;
void* GdiThreadLocalInfo;
unsigned __int64 Win32ClientInfo[62];
void* glDispatchTable[233];
unsigned __int64 glReserved1[29];
void* glReserved2;
void* glSectionInfo;
void* glSection;
void* glTable;
void* glCurrentRC;
void* glContext;
unsigned int LastStatusValue;
char Padding2[4];
UNICODE_STRING StaticUnicodeString;
wchar_t StaticUnicodeBuffer[261];
char Padding3[6];
void* DeallocationStack;
void* TlsSlots[64];
LIST_ENTRY TlsLinks;
void* Vdm;
void* ReservedForNtRpc;
void* DbgSsReserved[2];
unsigned int HardErrorMode;
char Padding4[4];
void* Instrumentation[11];
GUID ActivityId;
void* SubProcessTag;
void* PerflibData;
void* EtwTraceData;
void* WinSockData;
unsigned int GdiBatchCount;
union
{
_PROCESSOR_NUMBER CurrentIdealProcessor;
unsigned int IdealProcessorValue;
struct DUMMYSTRUCTNAME
{
char ReservedPad0;
char ReservedPad1;
char ReservedPad2;
char IdealProcessor;
};
};
unsigned int GuaranteedStackBytes;
char Padding5[4];
void* ReservedForPerf;
void* ReservedForOle;
unsigned int WaitingOnLoaderLock;
char Padding6[4];
void* SavedPriorityState;
unsigned __int64 ReservedForCodeCoverage;
void* ThreadPoolData;
void** TlsExpansionSlots;
void* DeallocationBStore;
void* BStoreLimit;
unsigned int MuiGeneration;
unsigned int IsImpersonating;
void* NlsCache;
void* pShimData;
unsigned __int16 HeapVirtualAffinity;
unsigned __int16 LowFragHeapDataSlot;
char Padding7[4];
void* CurrentTransactionHandle;
TEB_ACTIVE_FRAME* ActiveFrame;
void* FlsData;
void* PreferredLanguages;
void* UserPrefLanguages;
void* MergedPrefLanguages;
unsigned int MuiImpersonation;
union
{
volatile unsigned __int16 CrossTebFlags;
struct DUMMYSTRUCTNAME
{
unsigned __int16 SpareCrossTebBits : 16;
};
};
union
{
unsigned __int16 SameTebFlags;
struct DUMMYSTRUCTNAME
{
unsigned __int16 SafeThunkCall : 1;
unsigned __int16 InDebugPrint : 1;
unsigned __int16 HasFiberData : 1;
unsigned __int16 SkipThreadAttach : 1;
unsigned __int16 WerInShipAssertCode : 1;
unsigned __int16 RanProcessInit : 1;
unsigned __int16 ClonedThread : 1;
unsigned __int16 SuppressDebugMsg : 1;
unsigned __int16 DisableUserStackWalk : 1;
unsigned __int16 RtlExceptionAttached : 1;
unsigned __int16 InitialThread : 1;
unsigned __int16 SessionAware : 1;
unsigned __int16 LoadOwner : 1;
unsigned __int16 LoaderWorker : 1;
unsigned __int16 SpareSameTebBits : 2;
};
};
void* TxnScopeEnterCallback;
void* TxnScopeExitCallback;
void* TxnScopeContext;
unsigned int LockCount;
int WowTebOffset;
void* ResourceRetValue;
void* ReservedForWdf;
unsigned __int64 ReservedForCrt;
GUID EffectiveContainerId;
} MYTEB, * PMYTEB;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment