Contexte de la vulnérabilité
Le driver kernel d’Avast, ciblé par la CVE‑2025‑13032, contient une erreur de double‑fetch sur une structure _UNICODE_STRING fournie par l’utilisateur. Cette faille a été découverte sur Windows 11 à jour et sert de point d’entrée pour un débordement de pool contrôlé. Le correctif introduit dans les versions récentes du noyau Windows, les user‑mode accessors, vérifie chaque accès mémoire depuis le noyau vers l’espace utilisateur, rendant l’exploitation décrite ici non fonctionnelle sur les systèmes patchés.
Mécanisme de double‑fetch et débordement de pool
Le code vulnérable effectue deux lectures du champ Length de la structure fournie. La première lecture sert à allouer un tampon avec ExAllocatePoolWithTag, la seconde à copier les données avec memmove. En modifiant simultanément Length entre les deux lectures, un attaquant peut forcer la copie de plus de données que la taille allouée, créant ainsi un débordement de pool.
if ( ! a1 && unicodestring_user )
{
ProbeForRead ( unicodestring_user , 0x10 , 1 );
ProbeForRead ( unicodestring_user->Buffer , unicodestring_user->Length , 1 );
}
v17 = sub_140071C6C( ( __int64 ) v18 , & v14[ v23 + 13 ] , unicodestring_user );
__int64 __fastcall sub_140071C6C( __int64 a1 , _QWORD * a2 , _UNICODE_STRING * unicodestring_user )
{
PoolWithTag = ExAllocatePoolWithTag( PagedPool , unicodestring_user->Length + 16 , 0x20786E53u );
if ( !PoolWithTag )
return 0xC000009A;
Length = unicodestring_user->Length;
*PoolWithTag = unicodestring_user->Length;
PoolWithTag[1] = Length;
*(_QWORD*)(PoolWithTag + 8) = PoolWithTag + 8;
Buffer = ( char * ) unicodestring_user->Buffer;
if ( Buffer && unicodestring_user->Length )
memmove( ( _OWORD * )PoolWithTag + 1 , Buffer , unicodestring_user->Length );
*a2 = PoolWithTag;
return 0;
}
Le script d’exploitation crée un second thread qui alterne rapidement Length entre une valeur sûre (ex. 0x10) et une valeur malveillante (ex. 0x1000). Le thread principal invoque l’IOCTL vulnérable en boucle jusqu’à ce que le timing corresponde, ce qui se produit de façon fiable après quelques centaines d’itérations.
Construction du primitive lecture/écriture kernel
Le débordement cible le tableau RegBuffers d’un objet IORing. En écrasant les pointeurs du tableau, l’attaquant remplace une entrée par une adresse contrôlée dans le pool paginé. Une fois le tableau corrompu, il utilise une technique de « heap spray » pour placer des structures MDL contenant des adresses kernel prévisibles. L’introspection d’un MDL permet de lire une adresse kernel, fournissant ainsi un pointeur de fuite. Ce pointeur sert à calculer l’emplacement du token de processus, étape indispensable pour la phase suivante.
Escalade de privilèges vers SYSTEM
Avec le primitive lecture/écriture, le code remplace le champ Token du processus courant par celui du processus System. La modification s’effectue via une écriture directe à l’adresse du token, obtenue grâce à la fuite MDL. Après la mise à jour, le processus récupère les privilèges SYSTEM sans déclencher de blue screen, grâce à des correctifs appliqués au code de nettoyage qui restaurent les structures du pool avant la libération.