Hâte de vous éteindre – DoS distant utilisant Wininit.exe
Contributions éditoriales et additionnelles de Tricia Howard
Synthèse
Stiv Kupchik, chercheur chez Akamai, a trouvé une nouvelle vulnérabilité de déni de service (DoS) dans Wininit.exe de Microsoft, CVE-2022-44707, obtenu un score CVSS de 6,5.
La vulnérabilité a été révélée de manière responsable à MSRC en août et a été corrigée dans le cadre du Patch Tuesday de décembre 2022.
La vulnérabilité exploite le mécanisme de mise en cache RPCsur lequel nous avons fait des recherches approfondies. Nous fournissons une démonstration de cette attaque dans notre boîte à outils RPC.
En exploitant cette vulnérabilité, un pirate peut contourner une vérification de sécurité grâce au mécanisme de mise en cache et interagir avec le mécanisme d'arrêt sur un ordinateur Windows distant. Il devient alors capable d'initier et de stopper des arrêts.
Cette vulnérabilité affecte toutes les versions non corrigées de Windows, à partir de Windows 8/Server 2012, ce qui renforce l'importance de la mise en œuvre rapide de correctifs.
Introduction
Dans notre article précédent, La dure réalité du cache, nous avons annoncé la future révélation de quelques vulnérabilités RPC supplémentaires que nous avons trouvées à l'aide de notre boîte à outils RPCet le temps est enfin venu de vous les présenter. Aujourd'hui, dans la liste des manigances exploitant RPC, nous analysons Wininit.
Wininit est un processus Windows critique qui joue un rôle crucial dans le démarrage et l'arrêt du système d'exploitation Windows (à tel point que s'il s'arrête, l'ensemble du système s'arrête). À ce titre, il expose plusieurs interfaces RPC qui mettent en œuvre la fonctionnalité d'arrêt. Certaines d'entre elles sont même documentées. La vulnérabilité que nous avons trouvée se trouve dans l'interface WindowsShutdown.
Qu'est-ce que l'interface WindowsShutdown ?
WindowsShutdown est l'une des rares interfaces RPC exposées par Wininit. Elle est responsable du processus d'arrêt, tout comme le reste des interfaces RPC de Wininit (quelle surprise !). L'UUID de l'interface est d95afe70-a6d5-4259-822e-2c84da1ddb0d. Elle fait partie du MS-RSP (Remote Shutdown Protocol, protocole d'arrêt à distance) et offre les fonctionnalités suivantes :
En étudiant le fichier IDL public, nous pouvons constater que seuls WsdrInitiateShutdown et WsdrAbortShutdown sont documentés. Devrions-nous nous soucier d'une fonction non documentée ? (Une question qu'aucun chercheur n'a jamais posée.) Pour y répondre, examinons le rappel de sécurité de cette interface.
Le rappel de sécurité
Le rappel vérifie d'abord le protocole de transport et autorise uniquement ALPC ou TCP. Ensuite, le niveau d'authentification est vérifié et seul RPC_C_AUTHN_LEVEL_PKT_PRIVACY est autorisé. Si la fonction appelée n'est pas WsdrCheckForHiberboot, le jeton de l'utilisateur appelant distant est vérifié. Pour cela, le jeton est comparé avec un SID bien connu (enregistré dans une variable globale créée lors de l'initialisation de Wininit) : SECURITY_NETWORK_RID. Les appels à WsdrCheckForHiberboot ne sont pas du tout limités par le rappel de sécurité.
L'interface RPC est enregistrée avec le comportement de cache par défaut. Donc, théoriquement, si nous pouvions appeler avec succès WsdrCheckForHiberboot, le résultat mis en cache réussi nous permettrait de contourner la vérification SID sur les appels suivants pour WsdrInitiateShutdown et WsdrAbortShutdown.
Alors, que devons-nous faire pour appeler WsdrCheckForHiberboot
WsdrCheckForHiberboot
Nous ne nous soucions pas vraiment de ce que fait la fonction ou de ce qu'est l'« hiberboot » (pour ceux qui se le demandent, il s'agit du démarrage rapide de Windows), mais nous avons besoin de savoir comment l'appeler correctement. Nous voulons définir la fonction correctement dans un fichier IDL afin de lui compiler un client. Bien que la fonction ne soit pas documentée, nous pouvons essayer de regarder l'advapi CheckForHiberboot, le seul client RPC documenté que nous avons trouvé pour WsdrCheckForHiberboot. Il ne requiert que deux arguments : un pointeur de booléen et un booléen.
Nous pouvons recréer la définition de fonction avec ces arguments, en compilant un fichier et un programme IDL. Mais il manque encore quelque chose. Lorsque nous appelons la fonction à distance avec notre client, l'exécution RPC nous indique que nous appelons la fonction de manière incorrecte en renvoyant une erreur de stub RPC.
Au lieu de poursuivre les conjectures, nous pouvons entreprendre quelque chose de plus délicat, mais complexe. Dans le cadre du stub de l'interface RPC, nous trouvons un champ appelé ProcFormatString. Pour faire simple, il s'agit d'une longue chaîne binaire qui décrit les types d'arguments et les valeurs de retour pour toutes les fonctions exposées par l'interface. Ce champ est utilisé par l'exécution RPC pendant la sérialisation et la désérialisation des appels de fonction (qui est le processus qui a renvoyé l'erreur de stub RPC lors de notre première tentative). En analysant manuellement et avec minutie cette chaîne binaire, nous avons finalement découvert que la fonction s'attend à obtenir un autre argument (du type wchar_t*). Dans la figure suivante, vous pouvez voir la section du ProcFormatString de WsdrCheckForHiberboot, avec nos propres commentaires concernant chaque partie :
L'ajout de ce « nouvel » argument à notre définition de fonction a complètement débloqué la situation. CheckForHiberboot renvoie avec succès, et nous pouvons continuer à appeler à la fois WsdrInitiateShutdown et WsdrAbortShutdown.
Mais, ce n'est pas tout à fait fini…
Notre objectif immédiat est d'utiliser l'attaque par mise en cache pour appeler WsdrInitiateShutdown et d'effectuer un arrêt à distance. Cette fois-ci, il n'y a pas de conjectures. Non seulement la définition de la fonction est documentée dans le fichier IDL, mais les indicateurs qu'elle prévoit obtenir sont documentés sous la fonction advapi InitiateShutdownA. Ainsi, en utilisant la combinaison d'indicateurs SHUTDOWN_GRACE_OVERRIDE, SHUTDOWN_HYBRID et SHUTDOWN_FORCE_OTHERS, nous pouvons forcer un arrêt immédiat.
Grâce à cela, nous avons fini notre chaîne d'attaque et ainsi pu contourner le rappel de sécurité et sa vérification SID, et effectuer l'arrêt à distance. Techniquement, il s'agit d'une augmentation des droits (permettant à tout utilisateur authentifié d'appeler WsdrInitiateShutdown à distance au lieu de se restreindre à celles autorisées dans SECURITY_NETWORK_RID). Comme nous ne pouvons effectuer un arrêt qu'avec l'aide de cette interface, la vulnérabilité a été classée dans la catégorie vulnérabilité DoS.
Détection
Nous fournissons un OSQuery pour détecter les versions non corrigées (et donc vulnérables) de Wininit.exe. Pour identifier les ressources vulnérables, les clients dotés de la technologie Guardicore Segmentation d'Akamai peuvent utiliser la fonction d'information avec cette requête.
WITH product_version AS (
WITH os_minor AS (
WITH os_major AS (
SELECT substr(product_version, 0, instr(product_version, ".")) as os_major, substr(product_version, instr(product_version, ".")+1) as no_os_major_substr
FROM file
WHERE path = "c:\windows\system32\wininit.exe"
)
SELECT substr(no_os_major_substr, instr(no_os_major_substr, ".")+1) as no_os_minor_substr, substr(no_os_major_substr, 0, instr(no_os_major_substr, ".")) as os_minor, os_major
FROM os_major
)
SELECT
CAST(substr(no_os_minor_substr, instr(no_os_minor_substr, ".")+1) AS INTEGER) AS product_minor,
CAST(substr(no_os_minor_substr, 0, instr(no_os_minor_substr, ".")) AS INTEGER) AS product_major,
CAST(os_minor AS INTEGER) AS os_minor,
CAST(os_major AS INTEGER) AS os_major
FROM os_minor
)
SELECT
CASE
WHEN NOT ((os_major = 6 AND os_minor = 3) OR (os_major = 6 AND os_minor = 2) OR (os_major = 10 AND os_minor = 0))
THEN "not supported"
WHEN os_major = 6 AND os_minor = 3 AND product_major = 9600 AND product_minor >= 20716 THEN "patched"
WHEN os_major = 6 AND os_minor = 2 AND product_major = 9200 AND product_minor >= 24011 THEN "patched"
WHEN (
(product_major = 14393 AND product_minor >= 5582)
OR
(product_major = 10240 AND product_minor >= 19624)
OR
(product_major = 19041 AND product_minor >= 1620)
OR
(product_major = 22621 AND product_minor >= 963)
OR
(product_major = 22000 AND product_minor >= 1335)
OR
(product_major = 20348 AND product_minor >= 1366)
OR
(product_major = 17763 AND product_minor >= 3770)
)
THEN
"patched"
ELSE
"not patched"
Synthèse
Bien que cette vulnérabilité ne soit pas critique (il s'agit juste d'un arrêt à distance, authentifié qui plus est), il démontre le potentiel destructeur inhérent de MS-RPC, car il est intégré même dans les services les plus critiques du système d'exploitation Windows. En outre, contrairement à d'autres types de vulnérabilités, il n'y a presque aucune conjecture concernant RPC : toutes les cartes sont sur la table (binaire), il suffit de savoir les lire.
Trouver des moyens d'exploiter ces fonctions critiques est précisément la raison pour laquelle nous continuons nos recherches approfondies sur MS-RPC. Dans un contexte global, en dépit de son utilisation généralisée, il est largement sous-étudié. Des vulnérabilités comme celle abordée aujourd'hui montrent la nécessité de ce type de travail, et nous avons hâte de voir d'autres chercheurs travailler sur MS-RPC s'ils le souhaitent.
Cette vulnérabilité a été révélée de manière responsable à la fin du mois d'août et corrigée dans le Patch Tuesday de décembre 2022.