Capacités défensives dans Windows pour RPC
Table des matières
Le contenu de cet article a été présenté à l'origine à BlackHat USA 2023.
MS-RPC : Effilochage du protocole pour détecter les attaques
Nous poursuivons notre plongée dans MS-RPCavec un nouvel article de blog. Mais cette fois, au lieu de nous concentrer sur l'aspect offensif et la recherche de vulnérabilités, nous allons discuter de certaines des capacités défensives intégrées de Windows. Nous verrons comment les utiliser pour avoir un aperçu de ce qui se passe sous le capot de RPC, en espérant détecter des activités néfastes au passage.
MS-RPC fait partie intégrante du système d'exploitation Windows, ce qui signifie que de nombreuses voies de déplacement latéral passent par lui. Des attaques comme PSExec, Remote Scheduled Task, DCSyncet PetitPotam sont toutes exécutées via des protocoles MS-RPC, et il peut être difficile de distinguer le trafic réseau bénin du trafic malveillant, en ne disposant que des métadonnées réseau traditionnelles (port et adresse IP de la source et de la destination ; parfois aussi des informations sur le processus).
Nous renforcerons notre visibilité grâce à Event Tracing for Windows (ETW), un mécanisme de suivi et de surveillance intégré au système d'exploitation Windows. ETW nous fournit un grand nombre d'informations, notamment pour RPC, surtout si on les compare aux métadonnées traditionnelles du réseau.
Le concept de suivi des événements RPC en utilisant son fournisseur ETW n'est pas nouveau, et il existe déjà de nombreuses ressources et outils de qualité pour cela (nous en citons quelques-uns dans notre section Références ; n'hésitez pas à nous en indiquer d'autres). La plupart des recherches existantes se concentrent sur les événements côté client, que les attaquants peuvent altérer ou dont ils peuvent modifier le programme pour contourner complètement la journalisation. Nous allons plutôt nous concentrer sur la détection d'attaques utilisant des événements côté serveur, qui sont hors de portée des attaquants, et examiner les éléments uniques dont nous avons besoin pour les analyser.
Dans cet article, nous verrons comment consommer et analyser les événements du fournisseur ETW RPC, et comment analyser les événements à la recherche d'activités potentiellement malveillantes. En utilisant le fournisseur ETW, nous pouvons également connaître l'opération exacte qui est demandée, ce qui nous permet de détecter les attaques avec une granularité beaucoup plus fine. Nous verrons exactement comment cela fonctionne très prochainement.
Rappel : Qu'est-ce que RPC ?
RPC est l'abréviation de Remote Procedure Call (appel de procédure à distance). Il s'agit d'une forme de communication interprocessus (IPC). Plus précisément, ce protocole est conçu pour permettre l'invocation de fonctions à distance entre différents processus. Dans notre cas, nous nous concentrerons sur l'implémentation de Microsoft, MS-RPC.
MS-RPC est conçu comme un modèle client-serveur. Le serveur définit l'interface qu'il exposera à l'aide du langage IDL (interface Definition Language). L'IDL contient un identifiant universel unique (UUID) pour l'interface, ainsi que les définitions des fonctions qu'elle expose (Figure 1).
[
uuid(12345678-4000-2006-0000-20000000001a)
]
interface Test
{
void Foo([in] int number, [in] char *message);
void Bar([out] int * result);
}
Figure 1 : Exemple de définition d'interface IDL
La communication s'effectue par le biais de certains protocoles de transport, et chaque transport a son propre type de point de terminaison (Figure 2). La meilleure façon d'expliquer cela est de donner un exemple : La communication RPC peut s'effectuer via TCP, auquel cas le transport est TCP et le point de terminaison est le socket TCP, identifié par un numéro de port.
Transports |
Points de terminaison |
---|---|
TCP Canal nommé UDP ALPC HTTP Socket Hyper-V |
<numéro de port> <nom du canal> <numéro de port> <port ALPC> <Nom d'hôte> <UUID> |
Figure 2 : Les protocoles de transport courants et leurs types de points de terminaison respectifs
Il est important de noter que, bien que les fonctions aient un nom lisible dans le fichier IDL, elles sont identifiées différemment sur le réseau. Au lieu d'un nom sous forme de chaîne, elles sont identifiées par un nombre entier, appelé opnum (abréviation pour numéro d'opération). Il est généralement attribué en fonction de l'ordre d'apparition des fonctions dans la définition de l'interface IDL (ainsi, dans l'exemple de la Figure 1, Foo sera identifié par l'opnum 0, tandis que Bar sera l'opnum 1). Ceci est important pour la suite, lorsque nous devrons connaître l'opnum des fonctions pertinentes pour les identifier dans les données que nous verrons.
Pour un aperçu plus long et plus approfondi de MS-RPC, vous pouvez vous référer à notre article précédentou à l'une de nos présentations à la conférence HexaCon ou DEF CON sur le sujet.
#define ETW
Event Tracing for Windows (ETW) est un mécanisme intégré de suivi et de journalisation, mis en œuvre au sein du noyau Windows. Il fonctionne selon un modèle fournisseur-utilisateur ; les fournisseurs envoient des événements au noyau, qui les réachemine vers les programmes utilisateurs. Les fournisseurs et utilisateurs doivent s'enregistrer au préalable auprès du noyau (Figure 3).
De plus, comme le routage des événements est géré par le noyau, si des événements sont envoyés par des fournisseurs, mais qu'il n'y a pas d'utilisateur pour eux, les événements sont simplement rejetés et envoyés au vide.
Microsoft-Windows-RPC
Le fournisseur ETW RPC est implémenté dans le runtime RPC, rpcrt4.dll. Il affiche 13 événements différents, mais quatre d'entre eux nous intéressent particulièrement : les événements 5 et 7 pour le démarrage et l'arrêt de l'appel du client (respectivement), et les événements 6 et 8 pour le démarrage et l'arrêt de l'appel du serveur. Nous nous concentrerons sur les événements de début d'appel (Figure 4), car ce sont eux qui fournissent le plus d'informations. (Les événements d'arrêt d'appel indiquent simplement l'état de retour RPC.) Les événements client et serveur partagent le même format.
<template tid="RpcServerCallStartArgs_V1">
<data name="InterfaceUuid" inType="win:GUID"/>
<data name="ProcNum" inType="win:UInt32"/>
<data name="Protocol" inType="win:UInt32"/>
<data name="NetworkAddress" inType="win:UnicodeString"/>
<data name="Endpoint" inType="win:UnicodeString"/>
<data name="Options" inType="win:UnicodeString"/>
<data name="AuthenticationLevel" inType="win:UInt32"/>
<data name="AuthenticationService" inType="win:UInt32"/>
<data name="ImpersonationLevel" inType="win:UInt32"/>
</template>
Figure 4 : Schéma d'événement de début d'appel ETW RPC
En théorie, les données contenues dans l'événement devraient nous fournir toutes les métadonnées dont nous avons besoin pour mieux comprendre le trafic RPC. Nous savons maintenant ce qui est demandé grâce à l'UUID et à l'opnum de l'interface, et nous connaissons également l'adresse de la source ou de la destination (selon que nous examinons les événements client ou serveur), grâce au champ NetworkAddress (adresse réseau). Ça ne peut pas être aussi simple, n'est-ce pas ?
En effet. Il s'avère que le runtime RPC ne remplit pas le champ NetworkAddress lorsqu'il traite les événements d'appel de serveur. Nous devrons donc trouver un autre moyen de trouver ces données si nous voulons avoir les métadonnées de connexion. Ce champ est rempli lors des événements clients.
Cela soulève la question suivante : pouvons-nous ignorer les événements du serveur et nous fier uniquement au côté client ? La réponse est non. Puisque nous essayons de trouver un comportement malveillant, qui proviendra de la machine contrôlée par l'attaquant, nous ne pouvons pas être sûrs qu'il ne modifiera pas le fournisseur ETW côté client (et ne le désactivera pas, ou ne bloquera pas ses événements), ni même qu'il ne passera pas par le runtime RPC.
La bibliothèque python populaire Impacket, souvent utilisée dans les validations de principe (PoC) et les outils d'attaque (et qui contient des implémentations d'attaques réseau, comme PSExec), implémente le trafic RPC en son sein, de sorte que tout attaquant l'utilisant contournera le runtime RPC et ne sera pas enregistré dans le fournisseur ETW. Les événements de serveur échappant au contrôle des attaquants, il est plus prudent de s'y fier.C'est pourquoi nous allons maintenant nous concentrer sur la manière d'obtenir les données réseau d'ailleurs et de les faire correspondre à l'événement RPC.
Correspondance entre les événements RPC et les flux réseau
Faisons une pause dans notre étude du fournisseur ETW RPC pour nous intéresser à d'autres fournisseurs ETW, à savoir les fournisseurs TCP et SMB . Ces deux protocoles sont les protocoles de transport les plus courants pour le trafic RPC. Puisque nous avons dit que le type de point de terminaison RPC dépend du protocole de transport, nous pouvons faire correspondre le point de terminaison (numéro de port, nom du canal, etc.) tel que nous le recevons du fournisseur RPC à la valeur correspondante dans le fournisseur ETW de transport.
Pour TCP, c'est assez simple. Examinons l'événement ID 1017, appelé TcpAcceptListenerComplete, qui se déclenche une fois la connexion TCP en trois temps terminée.
Il comporte deux champs (essentiellement) : LocalAddress et RemoteAddress (bien que, dans notre cas, puisque nous nous intéressons aux événements du serveur, local se réfère au serveur tandis que distant se réfère au client). Les valeurs des champs d'adresse sont binaires et contiennent la famille d'adresses, l'adresse IP et le numéro de port (Figure 6).
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
---|---|---|---|---|---|---|---|
Famille d'adresses |
Numéro de port |
Valeur d'adresse (IP si AF_INET) |
Figure 6 : Format du champ binaire d'adresse
Tout ce que nous avons à faire est d'extraire l'adresse IP (du client) du champ RemoteAddress et le suivre avec le port (de destination) dans le champ LocalAddress . Maintenant, dès que nous recevons un événement RPC sur notre serveur, sur le même numéro de port TCP, nous pouvons savoir d'où il vient sur la base de la correspondance port-IP que nous avons extraite du fournisseur TCP (Figure 7).
Avec SMB, la situation est un peu plus compliquée, car différents éléments d'information apparaissent dans différents événements ETW. Le point de terminaison est un canal nommé, identique à un fichier, mais qui permet d'accéder à plusieurs fichiers au cours d'une même session SMB. Ainsi, dans ce cas, pour faire correspondre le point de terminaison avec sa source réseau, nous devons suivre deux événements : un pour la connexion proprement dite et un pour la demande de fichier (Figure 8).
Pour l'événement de connexion, nous avons l'ID d'événement 500, Smb2ConnectionAcceptStart, qui est déclenché lorsque la connexion SMB est établie. Nous en obtenons l'IP source et un UUID de connexion. Nous recherchons ensuite l'événement ID 8, Smb2RequestCreate_V2, qui contient le nom de fichier demandé et le même champ UUID de connexion. Il ne nous reste plus qu'à croiser les deux événements via l'UUID de connexion pour faire correspondre le nom du canal à l'IP qui l'a demandé (et plus tard, nous devrons faire correspondre le nom du canal au point de terminaison de l'appel RPC).
Pour vous éviter d'avoir à effectuer cette correspondance vous-même, nous avons implémenté cet algorithme. Vous pouvez trouver l'outil RPC Visibility dans notre référentiel GitHub. Cet outil est écrit en Python et sauvegarde le trafic réseau RPC enregistré dans une base de données Neo4j pour une visualisation facile.
Détection de l'attaque
Maintenant que nous disposons de toutes les informations nécessaires, nous pouvons enfin nous concentrer sur la détection des flux RPC malveillants. Le processus général est simple : Nous détectons une attaque passant par RPC, nous effectuons une validation de principe de celle-ci et voyons quelle interface RPC elle utilise, ainsi que l'opération demandée. Il suffit ensuite de créer une signature correspondant à cette opération. Nous avons inclus des requêtes de signature fonctionnant avec l'implémentation de la base de données Neo4j dans notre version, mais continuez votre lecture pour la logique générale et les conditions.
PSExec
PSExec est à la fois le nom général d'une technique d'attaque et un outil Sysinternals (celui qui a donné son nom à la technique). Fondamentalement, l'outil copie un binaire de service dans le partage ADMIN$ de la cible distante (dossier d'installation de Windows) et communique ensuite avec le gestionnaire de service via son interface RPC (MS-SCMR) pour créer un service pour le binaire et l'exécuter (Figure 9).
Il existe de nombreuses raisons légitimes de contacter des machines distantes par l'intermédiaire de SCMR ; les surveillances interrogeant l'état des services distants, par exemple. Il y a beaucoup moins de raisons de créer de nouveaux services à distance. Par conséquent, nous ne voulons pas déclencher d'alertes sur n'importe quelle connexion via SCMR (que nous pouvons détecter même sans l'ETW RPC, simplement en faisant correspondre une connexion réseau entrante au processus du gestionnaire de services services.exe), mais uniquement sur les connexions créant des services distants.
Notre signature doit donc être (en termes généraux, non spécifiques à notre implémentation de la visibilité RPC) :
interface_uuid == “367ABB81-9844-35F1-AD32-98F038001003” AND (opnum == 0xC OR opnum == 0x18)
où 0xC est l'opnum de RCreateServiceW et 0x18 est pour RCreateServiceA.
Planificateur de tâches à distance
Tout comme PSExec, le planificateur de tâches peut être utilisé pour lancer un binaire distant et réaliser ainsi un mouvement latéral. Il n'est même pas nécessaire de lancer un nouveau binaire, puisqu'il peut tout aussi bien lancer une console cmd ou PowerShell, et télécharger un binaire hébergé en ligne.
De même, comme pour PSExec, nous ne voulons pas détecter n'importe quel accès au service Planificateur de tâches, mais nous nous intéressons surtout aux appels RPC à SchRpcRegisterTask.
interface_uuid == “86D35949-83C9-4044-B424-DB363231FD0C” AND opnum == 0x1
DCSync
DCSync est une autre attaque basée sur RPC, mais qui vise les contrôleurs de domaine. Dans ce cas, l'attaquant se connecte au contrôleur de domaine réel, en prétendant être un nouveau contrôleur de domaine. Il demande ensuite à répliquer la base de données d'informations d'identification du contrôleur de domaine, afin d'accéder aux hachages de mots de passe KRBTGT.
La demande de réplication s'effectue par l'intermédiaire de l'interface RPC MS-DRSR et utilise la fonction spécifique DRSGetNCChanges (opnum 3).
interface_uuid == “e3514235-4b06-11d1-ab04-00c04fc2dcd2” AND opnum == 0x3
PetitPotam
PetitPotam est une attaque par coercition d'authentification sur le service EFS (Encrypted File System). Les attaquants peuvent se connecter à l'interface RPC EFS (MS-EFSR) et lui demander d'ouvrir un fichier distant spécifié par un chemin UNC. Cela déclenche alors une connexion SMB sortante avec authentification que l'attaquant peut ensuite relayer.
Lorsque la validation de principe d'attaque a été publiée, elle a utilisé EfsRpcOpenFileRaw (opnum 0), qui a depuis été corrigé. Topotam, le chercheur qui a trouvé la vulnérabilité, a également découvert que EfsRpcEncryptFileSrv (opnum 4) présentait le même défaut.
interface_uuid == “c681d488-d850-11d0-8c52-00c04fd90f7e” AND (opnum == 0x0 OR opnum == 0x4)
Inconvénients
Bien qu'il y ait beaucoup d'informations à tirer du fournisseur ETW RPC, il ne s'agit pas d'un guichet unique pour tous nos besoins en matière de sécurité. Si connaître l'opération demandée dans chaque flux réseau est une donnée précieuse, l'information la plus cruciale, à savoir les données transmises dans chaque requête, n'est pas enregistrée. Cela signifie que la détection des mouvements latéraux via le fournisseur ETW n'est encore qu'une approche heuristique, bien qu'avec beaucoup plus de contexte que les quadruplets de réseau traditionnels.
Il s'agit également d'une méthode de détection pure, qui ne peut pas être utilisée pour arrêter les attaques ou y répondre. Microsoft nous fournit un autre mécanisme de défense intégré pour RPC, le filtre RPC du pare-feu Windows. Vous pouvez en savoir plus sur ce mécanisme de filtrage et apprendre à l'utiliser dans notre article Guide définitif sur le filtre RPC (Remote Procedure Call).
Synthèse
Le fournisseur ETW RPC n'est pas un nouvel ajout à Windows, mais il a été largement négligé en ce qui concerne la défense du réseau. Il existe quelques outils qui interagissent et consomment des événements avec celui-ci, mais ils sont principalement destinés aux chercheurs en sécurité, et moins axés sur le côté réseau.
Dans cet article, nous avons discuté de la façon dont nous pouvons utiliser le fournisseur ETW RPC, couplé aux fournisseurs TCP et SMB, pour gagner en visibilité sur les opérations RPC demandées provenant du réseau. Nous disposons ainsi d'une approche heuristique que nous pouvons utiliser pour détecter d'éventuelles requêtes malveillantes susceptibles d'être utilisées pour un mouvement latéral.
Références
Outils grand public ETW RPC par d'autres chercheurs en sécurité
RpcMon par CyberArk
RpcInvestigator par Trail of Bits
Utilizing RPC Telemetry par Jonathan Johnson de SpecterOps
Notre référentiel GitHub pour tout ce qui concerne RPC
Offensive Windows IPC Internals 2: RPC par Carsten Sandker
- How to secure a Windows RPC Server, and how not to par James Forshaw