Appel et enregistrement — attaque de relais sur le client RPC WinReg
Synthèse
Stiv Kupchik, chercheur chez Akamai, a découvert une nouvelle faille de sécurité par élévation des privilèges (EOP) dans l'accès client Remote Registry de Microsoft, CVE-2024-43532, qui a obtenu un score CVSS de 8,8.
Cette vulnérabilité abuse d'un mécanisme de secours dans l'implémentation du client WinReg qui utilise des protocoles de transport obsolètes de manière non sécurisée si le transport SMB n'est pas disponible.
En exploitant cette faille, un attaquant peut relayer les détails d'authentification NTLM du client aux services de certificats Active Directory (ADCS) et demander un certificat utilisateur dont il peut se servir pour une authentification ultérieure dans le domaine. Nous fournissons une démonstration dans notre référentiel GitHub.
La vulnérabilité a été divulguée de manière responsable au service Microsoft Security Resource Center en février 2024 et a été corrigée dans le cadre du patch Tuesday d'octobre 2024.
Cette faille de sécurité affecte toutes les versions de Windows non corrigées.
Nos conclusions ont également été présentées lors de la conférence de sécurité informatique No Hat.
Introduction
MS-RPC est l'implémentation par Microsoft du protocole et de la norme RPC (Remote Procedure Call) pour l'appel de procédure à distance. RPC est une forme de mécanisme de communication inter-processus qui permet aux processus d'exposer des fonctionnalités que d'autres processus peuvent appeler. Il s'agit d'un composant central du système d'exploitation Windows sur lequel de nombreux services s'appuient, du gestionnaire de services aux processus d'arrêts dans wininit.
Notre équipe a fait beaucoup de recherches sur MS-RPC — recherche à la fois offensive dans laquelle nous avons découvert un grand vecteur d'attaque sous la forme d'attaques par mise en cache, et recherche défensive dans laquelle nous avons analysé les mécanismes de sécurité correspondants.
Cette fois-ci, nous voulions observer le protocole RPC sous un angle différent. Tout protocole qui permet la communication et les opérations entre différents ordinateurs doit être impliqué dans l'authentification de l'utilisateur, et le protocole RPC prend effectivement en charge la transmission des informations d'identification et l'authentification dans le cadre de son processus de liaison. Cependant, là où il y a authentification, il y a possiblement un relai d'authentification, nous avons donc cherché des éventuels relais.
RPC, authentification et tout ce qui se trouve entre les deux
Les sessions RPC sont gérées par des liaisons. Une application cliente se connecte à une application serveur, se lie à l'interface RPC requise et demande à exécuter une fonction spécifique (figure 1).
La requête et la réponse de liaison peuvent transporter plusieurs champs de données, selon les besoins de la connexion. Normalement, s'il n'y a pas d'authentification impliquée, la liaison RPC est simplement utilisée pour déterminer la syntaxe de transfert qui sera utilisée pour encapsuler les paramètres de fonction lors des appels suivants.
Que manque-t-il à cette interaction ? L'authentification, bien sûr ! Comment le serveur peut-il connaître l'identité du client et savoir si ce client est autorisé à effectuer l'action demandée ? La réponse est : Il ne le sait pas, sauf si le client a spécifiquement ajouté un contexte de sécurité (authentification) à la liaison. Par défaut, toutes les connexions RPC ne sont pas authentifiées, et tous les serveurs RPC ne nécessitent pas d'authentification.
Pour s'authentifier (ou, en termes RPC, « ajouter un contexte de sécurité »), le client doit ajouter des données supplémentaires à la requête de liaison, négocier le protocole d'authentification (par exemple NTLM ou Kerberos) et insérer les métadonnées supplémentaires requises par le protocole d'authentification (comme nom d'utilisateur, domaine, etc. Figure 2).
Trouver des cibles de recherche
La première étape est de comprendre à quoi l'authentification devrait ressembler du point de vue de l'API. Pour ce faire, appelez l'une des fonctions RpcBindingSetAuthInfo* à partir du client après avoir créé un identifiant de liaison. Si vous regardez la documentation de ces fonctions, vous remarquerez un champ appelé AuthenticationLevel ; indique le niveau de sécurité fourni par l'authentification.
La sécurité avec authentification ne se limite pas à vérifier que l'utilisateur existe et est autorisé, elle peut également être utilisée pour empêcher la falsification. Les niveaux d'authentification varient de la simple vérification de la connexion réussie (RPC_C_AUTHN_LEVEL_CONNECT) au cryptage complet et à la signature de l'ensemble du trafic pour s'assurer que rien n'est falsifié (RPC_C_AUTHN_LEVEL_PKT_PRIVACY). Bien sûr, les attaquants seraient intéressés par le premier niveau, dénué de défenses sur le trafic, ce qui signifie qu'il peut être piraté.
Pourtant, l'histoire n'est pas si simple. Les attaques par relais RPC ne sont pas un concept nouveau, de sorte que beaucoup de clients et de serveurs RPC dans Windows ont déjà été corrigés et utilisent le plus haut niveau d'authentification afin de garantir que les attaques par relais ne puissent aboutir. Nous devrons passer le système d'exploitation au peigne fin dans l'espoir de trouver des pépites de code plus anciennes qui ne sont toujours pas sécurisées pour une raison quelconque (figure 3).
WinReg — Un candidat prometteur
Comme nous le soupçonnions, la liste des cibles potentielles que nous avons trouvées incluait moins de 5 % du total des serveurs et clients RPC ; la plupart n'utilisant tout simplement plus l'authentification non sécurisée. Mais dedans nous avons trouvé un candidat prometteur, advapi32.dll.
advapi32.dll est un composant central de l'API Windows, et implémente une grande partie de la logique avancée dans Windows (comme son nom l'indique, « advanced »). Il exporte plus de 800 fonctions de différents domaines : journalisation des événements, cryptage, WMI, et plus encore.
Dans le cadre de nos recherches, nous avons constaté que, à certaines occasions, la fonction interne BaseBindToMachine appelle RpcBindingSetAuthInfoA avec un niveau d'authentification RPC_C_AUTHN_LEVEL_CONNECT, ce que nous voulons. BaseBindToMachine est appelée par la fonction exportée (bien que non documentée) RegConnectRegistryExW (figure 4), si elle reçoit un chemin UNC pour un nom de machine.
En regardant BaseBindToMachine, nous pouvons voir qu'elle contient en fait des appels aux deux fonctions RpcBindingSetAuthInfoW et RpcBindingSetAuthInfoA. La première est utilisée de manière sécurisée, avec le niveau d'authentification RPC_C_AUTHN_LEVEL_PKT_PRIVACY, tandis que la seconde utilise le niveau d'authentification RPC_C_AUTHN_LEVEL_CONNECT, qui peut être relayé car il ne vérifie pas l'authenticité ou l'intégrité de la connexion (figure 5).
Nous devons juste comprendre pourquoi il y a deux appels contradictoires. En regardant la logique de fonction, nous pouvons voir qu'elle a une variable de pointeur de fonction et un tableau de fonctions (figure 6). Ces fonctions définissent les informations de liaison RPC pour utiliser un transport RPC spécifique ; par défaut, elles tentent d'utiliser SMB et les canaux nommés — mais, si elles échouent, elles tentent de se lier sur SPX, TCP/IP et autres.
Pour une raison quelconque, une fois que l'appel retombe sur un autre protocole que SMB, il utilise RpcBindingSetAuthInfoA Pour définir le niveau d'authentification de connexion, qui n'est pas sécurisé.
Le repli vers TCP/IP est assez prometteur, car cela signifie que nous pouvons utiliser la méthode d'authentification non sécurisée pour relayer le trafic en utilisant une attaque de type « machine-in-the-middle » sans que le client s'en aperçoive. Bien que cela soit possible avec les autres protocoles de transport, ils sont assez obsolètes, il pourrait donc être irréaliste de les trouver dans un réseau récent (et leur utilisation pourrait déclencher des sonnettes d'alarme). TCP/IP est beaucoup plus courant comme transport RPC, donc il devrait être correct même dans un paramètre d'équipe rouge.
Il est important de savoir que les deux fonctions BaseBindToMachine et RegConnectRegistryExW acceptent un indicateur comme argument qui empêche le comportement de secours, mais la fonction de base RegConnectRegistryW appelle RegConnectRegistryExW sans que ce drapeau soit présent.
Le processus de relais
Le processus est assez simple puisque le relais NTLM est une technique courante. La plupart de la logique dont nous avons besoin est déjà mise en œuvre dans Impacket avec le protocole ntlmrelayx, qui est ce que nous utiliserons le plus.
Création du serveur de relais RPC
Ce qui manque à ntlmrelayx, c'est un serveur RPC TCP/IP, car il implémentait uniquement un serveur SMB. Dans ce cas, nous devrons construire notre propre serveur relais, qui rejettera le canal nommé winreg pour déclencher un repli vers la fonction de liaison TCP/IP.
Il y a trois points critiques que nous devons mettre en œuvre :
le mappage du point de terminaison RPC
la requête de liaison RPC avec NTLM
le défi NTLM
Le mappeur de point de terminaison RPC prend en charge la traduction des UUID (identifiants uniques universels) de l'interface RPC vers leurs points de terminaison respectifs, qui, dans le cas du transport TCP/IP, serait un numéro de port. Contrairement à SMB, où les points de terminaison sont des canaux nommés qui sont uniques et qui peuvent être connus à l'avance, les points de terminaison TCP utilisent des ports éphémères, donc une autre couche de traduction est nécessaire.
La partie critique concerne les questions liées au NTLM. La liaison RPC avec NTLM se fait par l'envoi message de négociation NTLM pendant la requête de liaison, puis une demande d'authentification NTLM est envoyée avec la réponse de liaison. Enfin, le client doit envoyer un autre message, appelé AUTH3, en répondant à la question secrète (figure 7).
Pour relayer, il suffit d'analyser les messages correspondants dans notre serveur RPC. Une fois que nous verrons un message de négociation NTLM dans un message de liaison, nous ouvrirons notre propre connexion à notre serveur d'authentification cible et demanderons également à nous authentifier via NTLM. Ensuite, il suffit d'intercepter le défi d'authentification que le serveur nous envoie, de le relayer à la victime, et de relayer la réponse au serveur pour obtenir notre propre session authentifiée.
La seule chose à prendre en compte est le serveur sur lequel nous voulons relayer l'authentification.
Relais RPC à RPC
La première idée serait de relayer vers un autre serveur RPC sur une machine différente, comme le gestionnaire de services ou le planificateur de tâches, et de réaliser une exécution de code à distance de cette façon. Cependant, dans ce cas, le problème est que personne n'utilise plus l'authentification non sécurisée via RPC, donc nous ne pouvons pas relayer vers un serveur RPC haut de gamme. (Ce manque d'authentification non sécurisée via RPC est la même raison pour laquelle nous avions des cibles de recherche limitées.)
Les serveurs RPC du gestionnaire de services et du planificateur de tâches nécessitent une fonction RPC_C_AUTHN_LEVEL_PKT_PRIVACY, qui crypte la totalité du trafic avec le hachage NTLMv2 du client, que nous ne connaissons pas même avec le relais. Donc, regardons plutôt cela sous un angle différent.
RPC vers ADCS
Heureusement, les équipes de SpecterOps ont fait beaucoup de travail sur les services de certificats Active Directory (ADCS) ; Spécifiquement sur le relais NTLM vers ADCS. Ceci est également implémenté dans Impacket par défaut, donc tout ce que nous avons à faire est de passer notre session authentifiée vers le module HTTPAttack d'Impacket et de laisser la magie opérer.
Le serveur Web HTTP d'ADCS ne nécessite aucune sécurité et est vulnérable aux attaques de relais. En abusant de cette faille, une fois que nous nous sommes authentifiés, nous pouvons demander un certificat utilisateur, que nous pouvons ensuite utiliser par nous-mêmes pour nous authentifier, sans avoir à passer par la difficulté de relayer l'authentification à nouveau (figure 8).
Nous avons utilisé ce certificat pour nous authentifier auprès du service LDAP sur le contrôleur de domaine et créer un nouvel administrateur de domaine persistant dans le domaine compromis (figure 9).
Impact potentiel
Une fonction dans advapi n'a aucun intérêt en soi, elle n'a d'impact que si quelque chose d'autre l'utilise. Une recherche rapide des importations de RegConnectRegistryExW ou RegConnectRegistryExA n'affiche rien sur un contrôleur de domaine à jour, mais une recherche pour RegConnectRegistryW détecte de nombreux candidats potentiels, comme certutil et certsrv (AD CS), EFS, DFS, et plus encore.
Détection
Le service de registre distant (Remote Registry) n'est pas activé par défaut sur toutes les machines Windows. Il est possible de détecter son état en utilisant le programme osquery suivant :
SELECT display_name, status, start_type, pid FROM services WHERE name='RemoteRegistry'
Cela, cependant, ne protège pas de CVE-2024-43532, car il s'agit d'un problème client. Les résultats de la requête devraient générer des cas d'utilisation réels pour le registre distant dans l'organisation que vous devrez peut-être prendre en compte lors du renforcement de vos systèmes.
Pour détecter les clients qui utilisent l'une des WinAPI vulnérables, vous pouvez utiliser la règle YARA suivante :
import "pe"
rule winreg_client_import {
meta:
description = "Detect binaries that rely on RegConnectRegistry"
author = "Stiv Kupchik with Akamai Technologies"
condition:
pe.is_pe and (
pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryA")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryW")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExA")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExW")
)
}
Les utilisateurs d'Akamai Guardicore Segmentation peuvent également créer des règles de stratégie pour le trafic entrant dans le service RemoteRegistry (figure 10).
Il est également possible d'utiliser Event Tracing for Windows (ETW) pour surveiller le trafic RPC aussi bien du côté client que du côté serveur de la communication. Nous avons développé ce sujet lors de notre présentation à la conférence Black Hat 2023 et dans l' article de blog correspondant. Les utilisateurs peuvent utiliser notre outil open source de visibilité RPC pour suivre les appels RPC et filtrer l'UUID de l'interface RPC WinReg {338cd001-2244-31f1-aaaa-900038001003}.
Conclusion
Bien que les protocoles RPC — et MS-RPC — aient été construits avec la sécurité à l'esprit, nous pouvons clairement voir l'évolution des principes de sécurité au fil du temps en analysant diverses implémentations d'interface RPC. Alors que la plupart des serveurs et clients RPC sont sécurisés de nos jours, il est possible, de temps en temps, de découvrir des reliques d'une implémentation non sécurisée à des degrés divers.
Dans ce cas, nous avons réussi à réaliser le relais NTLM, qui est une classe d'attaques qui appartient plutôt au passé. Cela prouve simplement que les défenses réseau doivent être aussi approfondies que possible car vous ne savez jamais quelle ancienne interface est encore exposée ou en cours d'exécution.
Calendrier de divulgation
01/02/2024 — Vulnérabilité révélée à MSRC
25/04/2024 — Rapport clos en tant que problème de documentation
17/06/2024 — Rapport rouvert avec un meilleur POC et une explication
08/07/2024 — Vulnérabilité confirmée
08/10/2024 — Vulnérabilité corrigée
19/10/2024 — Article de blog publié