
CVE-2025-7771: Lettura/scrittura arbitraria di memoria fisica e porte I/O tramite il driver ThrottleStop
CVE-2025-7771: Lettura/scrittura arbitraria di memoria fisica e porte I/O tramite il driver ThrottleStop
ThrottleStop è una piccola applicazione legittima utilizzata per monitorare e correggere il throttling della CPU. Dispone di un driver che facilita queste attività e per la maggior parte non effettua alcun tipo di validazione dell'input.
| IOCTL | Vulnerabilità |
|---|---|
| 0x80006498 | Lettura arbitraria di memoria |
| 0x8000649C | Scrittura arbitraria di memoria |
| 0x80006430 | Lettura arbitraria di porte |
| 0x80006434 | Scrittura arbitraria di porte |
Sembra esserci anche lettura e scrittura arbitraria di MSR ma non sono riuscito a farle funzionare.
Sentiti libero di indagare tu stesso.
La lettura MSR si trova a 0x80006448 e la scrittura a 0x8000644C.
Se sei riuscito a farla funzionare, invia una issue descrivendo la procedura.
Tutte le chiamate IOCTL menzionate di seguito sono implementate nel file Exploit.c.
L'IOCTL 0x80006498 mappa qualsiasi indirizzo fisico fornito nello spazio kernel usando MmMapIoSpace e legge 1, 2, 4 o 8 byte da esso. I dati vengono poi copiati nello spazio utente.
Per attivare questo IOCTL, usa DeviceIoControl con l'indirizzo fisico come input e la dimensione del buffer di output come dimensione di lettura (1, 2, 4 o 8).
Codice semplificato per questo IOCTL nel driver:
case 0x80006498: {
size_t readSize = inputBuffer->Size; // must be 1,2,4,8
PHYSICAL_ADDRESS physAddr = inputBuffer->PhysAddr;
void* mappedAddr = MmMapIoSpace(physAddr, readSize, MmNonCached);
if (mappedAddr) {
if (readSize == 1)
*outputBuffer = *(uint8_t*)mappedAddr;
else if (readSize == 2)
*(uint16_t*)outputBuffer = *(uint16_t*)mappedAddr;
else if (readSize == 4)
*(uint32_t*)outputBuffer = *(uint32_t*)mappedAddr;
else if (readSize == 8)
*(uint64_t*)outputBuffer = *(uint64_t*)mappedAddr;
MmUnmapIoSpace(mappedAddr, readSize);
}
}
Simile alla lettura, l'IOCTL 0x8000649C mappa l'indirizzo fisico fornito nello spazio kernel usando MmMapIoSpace e legge 1, 2, 4 o 8 byte da esso.
Per attivare questo IOCTL, usa DeviceIoControl con il seguente layout del buffer di input:
struct {
ULONGLONG PhysicalAddress; // 8 bytes
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
QWORD Value64;
};
};
La dimensione del buffer di input sarà 8 + (1, 2, 4 o 8) a seconda della dimensione di scrittura.
Il buffer di output e la sua dimensione non vengono utilizzati.
Codice semplificato per questo IOCTL nel driver:
case 0x8000649C: {
size_t writeSize = inputBuffer->Size; // must be 1,2,4,8
PHYSICAL_ADDRESS physAddr = inputBuffer->PhysAddr;
void* mappedAddr = MmMapIoSpace(physAddr, writeSize, MmNonCached);
if (mappedAddr) {
if (writeSize == 1)
*(uint8_t*)mappedAddr = inputBuffer->Value8;
else if (writeSize == 2)
*(uint16_t*)mappedAddr = inputBuffer->Value16;
else if (writeSize == 4)
*(uint32_t*)mappedAddr = inputBuffer->Value32;
else if (writeSize == 8)
*(uint64_t*)mappedAddr = inputBuffer->Value64;
MmUnmapIoSpace(mappedAddr, writeSize);
}
}
L'IOCTL 0x80006430 consente a un utente di specificare una porta IO e leggere da essa direttamente usando __inbyte, __inword o __indword.
Il buffer di input è un USHORT e la dimensione del buffer di input dovrebbe essere sizeof(USHORT).
Il buffer di output conterrà il risultato (1, 2 o 4 byte a seconda della dimensione richiesta) e la dimensione di output può essere 1, 2 o 4.
Codice semplificato per questo IOCTL nel driver:
case 0x80006430: {
uint16_t port = inputBuffer->PortNumber;
uint8_t size = inputBuffer->AccessSize; // 1, 2, or 4
if (size == 1)
*outputBuffer = __inbyte(port);
else if (size == 2)
*(uint16_t*)outputBuffer = __inword(port);
else if (size == 4)
*(uint32_t*)outputBuffer = __indword(port);
}
Simile alla lettura di porte, l'IOCTL 0x80006434 consente a un utente di specificare una porta IO e scrivere valori arbitrari su di essa con __outbyte, __outword o __outdword.
Il layout del buffer di input è:
struct {
USHORT PortNumber;
BYTE Padding[2]; // alignment
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
};
};
La dimensione del buffer di input dovrebbe essere sizeof(USHORT) + sizeof(Value) (+ padding). Il buffer di output e la sua dimensione non vengono utilizzati.
Codice semplificato per questo IOCTL nel driver:
case 0x80006434: {
uint16_t port = inputBuffer->PortNumber;
uint8_t size = inputBuffer->AccessSize; // 1, 2, or 4
if (size == 1)
__outbyte(port, inputBuffer->Value8);
else if (size == 2)
__outword(port, inputBuffer->Value16);
else if (size == 4)
__outdword(port, inputBuffer->Value32);
}
Questa traduzione è possibile sfruttando Superfetch, un modulo kernel legittimo che espone la traduzione da indirizzo virtuale a fisico tramite API non documentate. Il codice per questo è implementato in vtop.c
Questo progetto può essere compilato usando Meson. Legge il campo UniqueProcessId risolvendo la struttura EPROCESS del processo System usando PsInitialSystemProcess e verifica se è 4 (implementato in Main.c e le funzioni per trovare l'EPROCESS di uno specifico PID sono implementate in EProcess.c). Inoltre, chiede all'utente se desidera forzare il riavvio del sistema. Questo è possibile scrivendo il valore 0x0E sulla porta 0xCF9.
Attenzione: Tutto è stato testato su Windows 11 build 24H2. Gli offset nella struttura EPROCESS potrebbero essere diversi sul tuo sistema.
Compila il progetto con
meson setup build
Verrà creata la directory build. Ora compila il progetto con
meson compile -C build
Il driver è disponibile nella directory Drivers del repository. I driver ThrottleStop possono essere estratti direttamente dal programma ThrottleStop.exe tramite Resource Hacker. Troverai 2 driver - x64 e x86. DriverObject->MajorFunction[14] contiene la funzione di dispatch dove sono stati implementati tutti gli IOCTL.
Crea e avvia il driver usando:
sc create ThrottleStop binPath="<Path>" type=kernel
sc start ThrottleStop
https://github.com/jonomango/superfetch
Questo driver non è ancora stato aggiunto alla block list dei driver di Microsoft né a loldrivers.