Simuler des services Active Directory légitimes sur un réseau interne : le cas de l’exploitation de GPO
La simulation de services Active Directory légitimes sur un réseau interne est une capacité puissante et polyvalente qui peut être exploitée dans le contexte de divers vecteurs d'attaque. De nombreux exemples d'exploits reposant sur la capacité à simuler des services LDAP et/ou SMB fonctionnels peuvent être cités, tels que l'exploitation des Group Policy Objects (GPO) ou, plus récemment, la faille ADCS Certighost (CVE-2026-54121).
Il s’agit d’un sujet sur lequel nous avons commencé à travailler il y a plusieurs années dans le cadre de l'exploitation des GPO (voir cet article ainsi que cet article). La présente publication introduit une recherche complémentaire qui fournit un moyen de simuler des services LDAP et SMB authentifiés sur un réseau interne, d'une manière flexible pouvant être adaptée à divers cas d'usage. Cette recherche s'appuie sur scapy et l'excellent travail de Gabriel Potter. Elle sera présentée au travers du prisme de l'exploitation des GPO, mais le but des outils qui en résultent est d'offrir une flexibilité facilitant les recherches futures dans d'autres domaines.
Vous souhaitez améliorer vos compétences ? Découvrez nos sessions de formation ! En savoir plus
0. Introduction
Nous avons rencontré le besoin de simuler des services Active Directory fonctionnels et authentifiés il y a quelque temps, lorsque nous avons commencé à travailler sur l'exploitation des Group Policy Objects. Ce besoin a également mis en évidence le manque d'outils existants pour y parvenir.
Plus précisément, l'exploitation des GPO via la modification de leur attribut gPCFileSysPath nécessitait un service SMB fonctionnel et authentifié pour distribuer des fichiers GPO empoisonnés (voir le rappel ci-dessous dans la partie 1). Un peu plus tard, l'exploitation de l'attribut gPLink sur les conteneurs a nécessité à la fois un service LDAP et un service SMB pour être possible (voir le rappel ci-dessous dans la partie 2).
Au fil des années, d'autres cas d’usage intéressants sont apparus nécessitant la simulation de services AD légitimes. Nous en avons rencontré certains lors de nos audits – avec l'exemple d'un logiciel spécifique récupérant des fichiers exécutables via une URI contrôlée par l'utilisateur, mais ne pouvant se connecter qu’à un service SMB légitime et authentifié. Plus récemment, l'exploit Certighost (CVE-2026-54121) constitue également une bonne illustration, puisqu'il nécessite la simulation de services authentifiés LDAP et SMB.
Lorsque nous avons publié pour la première fois nos outils d'exploitation GPO GPOddity et OUned, nous avions inclus divers moyens de simuler des services de domaine authentifiés. Cela a abouti à un mélange de scripts impacket modifiés plus ou moins stables, et de configurations complexes de redirection réseau s'appuyant sur des machines virtuelles Windows. Bien que la solution proposée puisse fonctionner dans certains cas, elle présentait également plusieurs limites qui seront détaillées ci-dessous. D'autres approches, telles que celle employée par le script d'exploitation Certighost, sont adaptées à un cas d'exploitation spécifique, mais manquent de flexibilité.
C'est pourquoi nous avons commencé à travailler sur une nouvelle implémentation qui permettrait aux professionnels de la sécurité offensive de simuler facilement les protocoles LDAP et SMB authentifiés de manière robuste et flexible. Nous décrirons d'abord la simulation d'un service SMB, avec notre implémentation prenant en charge le multiplexing de sessions (plusieurs sessions SMB au sein d'une même connexion TCP). Cela permet à GPOddity de gérer de manière transparente les GPO utilisateurs. Nous nous pencherons ensuite sur la simulation d'un service LDAP fonctionnel et sur son utilisation avec OUned pour l'exploitation des conteneurs AD.
Dans les deux cas, les scripts sont basés sur scapy et l'excellent travail de Gabriel Potter sur le projet, qui a fourni une base solide concernant la réimplémentation en Python des protocoles réseau Windows.
1. SMB
Le premier vecteur d'attaque nécessitant la simulation d'un service Active Directory authentifié et fonctionnel résidait dans l'exploitation des GPO via le relais NTLM. Les recherches associées ont abouti à un outil que nous avons appelé GPOddity et qui a été initialement publié en septembre 2023.
a. Rappels et contexte
Cette section fournit quelques rappels génériques concernant l'implémentation des GPO dans Active Directory, ainsi que le vecteur d'attaque GPOddity (qui est expliqué en profondeur dans l'article originel). Si vous êtes déjà familier avec ces sujets, n'hésitez pas à passer à la section suivante.
Dans Active Directory, une GPO est composée de deux éléments : le Group Policy Container (GPC), qui est un objet LDAP contenant les métadonnées de la GPO, et le Group Policy Template (GPT), qui est un partage SMB (situé par défaut sur le partage SYSVOL d'un contrôleur de domaine) contenant les fichiers de configuration de la GPO.
Lorsqu'un objet utilisateur ou ordinateur tente d'appliquer ses GPO, il récupère l'attribut gPLink de ses conteneurs (Unités Organisationnelles, Sites et objets Domaine). Cet attribut contient une liste de GPC correspondant aux GPO que l'objet doit appliquer. Pour chaque GPO, l'utilisateur ou l'ordinateur récupère d'abord le GPC via LDAP. Il lit les métadonnées de la GPO, y compris l'attribut gPCFileSysPath qui indique l'emplacement du partage SMB agissant comme le GPT. L'utilisateur ou l'ordinateur se connecte ensuite au partage SMB, s'authentifie et récupère les fichiers GPO afin d'appliquer les configurations souhaitées.
Le vecteur d'attaque GPOddity fonctionne de la manière suivante. Si un attaquant obtient un accès en écriture à l'objet LDAP GPC, il est possible de modifier l'attribut gPCFileSysPath pour le faire pointer vers un serveur SMB sous le contrôle de l'attaquant. Cela lui permet de distribuer des fichiers GPT malveillants contenant par exemple des tâches planifiées, et de compromettre tout objet appliquant la GPO empoisonnée. Étant donné que ce vecteur d'attaque repose uniquement sur la modification de l'objet LDAP GPC, il est exploitable via un relais NTLM vers le service LDAP.
b. Implémentation précédente du serveur SMB et limites
Dans le contexte de l'attaque GPOddity décrite ci-dessus, le serveur SMB malveillant vers lequel les clients GPO sont redirigés ne pouvait pas simplement fournir un accès anonyme ou accepter n'importe quels identifiants sans les vérifier. En effet, les clients SMB Windows raisonnablement modernes refuseront par défaut de se connecter de manière anonyme à un serveur SMB distant, et la fonctionnalité Secure Negotiate activée depuis Windows Server 2012 / Windows 8 impose l'échange d'un paquet signé, garantissant que le serveur distant a correctement authentifié le client. Pour plus d'informations sur ce point, veuillez vous référer à l'article GPOddity, partie 7 « After root: GPOddity’s inner workings ».
En conséquence, l'exploitation de l'attaque par empoisonnement gPCFileSysPath nécessite l'implémentation d'un serveur SMB qui authentifie correctement les clients entrants en validant leur identité de domaine. La première solution développée dans GPOddity reposait sur une version modifiée du serveur SMB d'impacket, qui a été ajustée pour mobiliser le protocole Netlogon et générer la clé de session appropriée. Cette implémentation se trouve dans le fichier gpoddity_smbserver.py de la première version de l’outil, tandis que des détails supplémentaires sont à nouveau fournis dans l'article GPOddity, partie 7 « After root: GPOddity’s inner workings ».
Cependant, après quelques tests supplémentaires, nous nous sommes rendu compte que si cette implémentation de serveur SMB impacket fonctionnait correctement pour l'exploitation des objets de stratégie de groupe d'ordinateur, elle échouait pour une raison inconnue lorsqu'il s'agissait des objets de stratégie de groupe d'utilisateur. Dans ce dernier cas, le client SMB Windows de l'hôte cible ne parvenait pas à récupérer le fichier du Group Policy Template depuis notre serveur SMB. Nous avons pu confirmer que le défaut venait de notre implémentation de serveur SMB, puisque tout fonctionnait correctement avec le serveur SMB d'un hôte Windows du domaine. Nous n'avons cependant pas pu, à l’époque, identifier précisément le problème (cela a été mentionné dans l'article GPOddity, partie 5 – b. « The special case of User Group Policy Objects »).
c. Création d'un serveur SMB supportant le multiplexing avec scapy
En revenant sur l'exploit quelques années plus tard, nous avons pu découvrir le problème de notre précédent serveur SMB Netlogon basé sur impacket. Nous en avons ensuite implémenté un plus résilient, en nous appuyant sur scapy et le travail de Gabriel Potter.
Afin de comprendre l'échec de l'implémentation initiale de l'exploit GPOddity dans le cas des GPO utilisateurs, il faut remonter à 2016, et plus précisément à la mise à jour de sécurité Microsoft MS16-072. Avant une telle mise à jour, la récupération des fichiers du modèle de stratégie de groupe lors de l'application d'une GPO était plutôt simple :
-
Pour les GPO s'appliquant à l'ordinateur, le compte machine associé audit ordinateur s'authentifie auprès du serveur SMB du Group Policy Template et récupère les fichiers.
-
Pour les GPO s'appliquant à un utilisateur, l'identité du compte utilisateur était utilisée pour s'authentifier auprès du serveur SMB et récupérer les fichiers.
Une telle configuration a conduit à une vulnérabilité d'élévation de privilèges découverte en 2016 (CVE-2016-3223). En effet, le client de stratégie de groupe s'exécute avec des privilèges d'administration élevés (sous l'identité NT AUTHORITY/SYSTEM) lors de l'application des GPO sur un hôte. La vulnérabilité se produit lorsqu'un attaquant connaît les identifiants d'un utilisateur du domaine pouvant se connecter à une machine, avec de faibles privilèges sur le système. Si l'attaquant peut également adopter une position de type "Man-in-the-Middle" entre la machine cible et le réseau, il peut alors créer un domaine factice avec le même utilisateur. Il peut ensuite rediriger tout le trafic de l'hôte cible vers le contrôleur de domaine Active Directory factice. Lors de l'actualisation de la GPO utilisateur, le domaine factice sera capable de valider correctement l'identité de l'utilisateur (qui a le même mot de passe sur le domaine factice), puis de fournir un GPC et un GPT arbitraires. De cette manière, l'attaquant peut amener le système cible à appliquer des configurations d'objets de stratégie de groupe arbitraires, qui seront exécutées par le client de stratégie de groupe en tant que SYSTEM, entraînant une élévation locale des privilèges à partir d'un utilisateur de domaine à faibles privilèges.
Selon les propres mots de Microsoft, le correctif proposé était le suivant : « Avant l'installation de MS16-072, les stratégies de groupe utilisateur étaient récupérées en utilisant le contexte de sécurité de l'utilisateur. Après l'installation de MS16-072, les stratégies de groupe utilisateur sont récupérées en utilisant le contexte de sécurité de l'ordinateur ». Cela corrigerait effectivement la vulnérabilité susmentionnée, puisqu'un attaquant connaissant uniquement le mot de passe d'un utilisateur à faibles privilèges ne serait pas en mesure d'authentifier correctement le compte machine pour distribuer une GPO malveillante. Et si l'attaquant connaît le mot de passe du compte machine, le système peut déjà être considéré comme entièrement compromis.
Quel est le rapport avec le problème de serveur SMB mentionné précédemment ? La communication de Microsoft sur le correctif implémenté par la mise à jour de sécurité n'est pas précise à 100%. Lors de la récupération des fichiers GPT pour une GPO utilisateur, deux étapes ont en réalité lieu :
-
Tout d'abord, l'utilisateur s'authentifie toujours auprès du serveur SMB pour récupérer le fichier
GPT.INI, qui contient certaines métadonnées de la GPO comme la version. -
Ensuite, le compte machine s'authentifie pour récupérer les autres fichiers contenant les configurations à appliquer.
La raison pour laquelle le compte utilisateur s'authentifie toujours pour récupérer le fichier GPT.INI n'est pas connue, cependant un détail important est que la même connexion TCP est utilisée à la fois pour l'utilisateur et le compte machine. La spécification SMB indique qu'il est possible de générer plusieurs sessions au sein d'une seule connexion TCP, via le mécanisme de SessionID (voir la documentation Microsoft : « Il peut y avoir plusieurs sessions actives sur une seule connexion au protocole SMB 2. Le champ SessionId de l'en-tête de paquet SMB2 distingue les différentes sessions »).
Les deux captures d'écran ci-dessous montrent une capture Wireshark du trafic SMB généré par l'application d'une GPO utilisateur. Un filtre a été appliqué sur le flux TCP 7, de sorte que tous les messages affichés font partie de la même connexion TCP. Les captures montrent qu'une première session est créée avec l'ID 0x0000380000000071 pour l'utilisateur auquel la GPO s'applique, adm-qroland. C'est avec cette identité que le fichier GPT.INI est récupéré. Par la suite, au sein de la même connexion TCP, une autre session SMB est créée, authentifiée cette fois avec l'identité de la machine sur laquelle l'utilisateur est connecté, AD01-SRV1$. Cette seconde session a l'ID 0x0000380000000075, et est utilisée pour récupérer les fichiers XML de la GPO, ici le fichier ScheduledTasks.xml.
Le problème rencontré dans le cas des GPO utilisateurs avec le serveur SMB impacket modifié était donc simplement que ladite implémentation n'était pas conçue pour gérer des sessions multiples (chacune d'elles ayant des clés de session différentes pour la signature des messages, entre autres) au sein d'une seule connexion TCP. Le serveur supposait qu'une connexion TCP ne pouvait être associée qu'à une seule session. En conséquence, une confusion apparaissait lorsque l’unique structure associée à la connexion TCP était utilisée à la fois par le compte utilisateur et le compte ordinateur, entraînant l'utilisation de clés de signature incorrectes, entre autres.
Après avoir cherché des alternatives pour un serveur SMB fonctionnant sur les systèmes Unix, nous n'avons trouvé que celui implémenté par Gabriel Potter dans le projet scapy. Cependant, ce serveur SMB ne prenait pas non plus en charge le multiplexing et gérait une seule session par connexion TCP.
Nous avons alors décidé de nous appuyer sur l'implémentation scapy existante et de modifier son serveur SMB afin de prendre en charge le multiplexing de sessions. Nous avons choisi d'abandonner impacket car le projet possède divers marqueurs distinctifs associés aux actions offensives.
La version modifiée du serveur SMB Scapy prenant en charge le multiplexing de sessions peut être trouvée dans le projet GPOddity (ainsi que dans le projet OUned). Les principales modifications par rapport à l'implémentation précédente résident dans la nouvelle classe SMBStreamSocketMultiplexing (scapy/layers/smb2.py), ainsi que dans les modifications apportées à la classe SMBServer (scapy/layers/smbserver.py) pour gérer plusieurs sessions parallèles.
Dans l'ensemble, grâce au travail de Gabriel Potter sur l'implémentation des protocoles réseau Windows et aux ajustements susmentionnés, nous disposons désormais d'un serveur SMB Python qui accepte l'authentification de domaine (via Kerberos ou Netlogon) et prend en charge le multiplexing conformément à la spécification SMB. Il s’agit d’une implémentation plus propre et robuste que la variante impacket d'origine. Cela nous permettra d'exploiter de manière transparente les objets de stratégie de groupe utilisateur via GPOddity, entre autres.
Veuillez noter que le serveur SMB compatible avec le multiplexing n'est pas encore intégré à la branche principale de scapy. Nous prévoyons de soumettre une pull request, mais devons encore effectuer des tests de régression sur d'autres fonctionnalités. En attendant, il est disponible dans les dépôts GPOddity et OUned, les deux projets fournissant un exemple d'utilisation.
d. Démonstration du serveur SMB supportant le multiplexing avec GPOddity
La vidéo suivante illustre l'utilisation du serveur SMB via GPOddity. Plus précisément et comme le montre la vidéo, un utilisateur de domaine, adove, a le contrôle d'une GPO appelée ADMINS_HARDENING_ANY. La GPO fournit des configurations utilisateur et s'applique à un administrateur de domaine, adm-qroland. La démonstration montre qu'un attaquant ayant le contrôle d'un compte machine ATTACKER$ et ayant pu relayer l'utilisateur adove vers le service LDAP peut ensuite utiliser GPOddity pour compromettre adm-qroland en manipulant l'emplacement SMB du GPT de la GPO ADMINS_HARDENING_ANY, et en exécutant le serveur SMB pour fournir les fichiers malveillants.
Notez que les commandes à exécuter via la GPO peuvent être fournies via un « module file ». Ces fichiers de module sont communs aux outils GroupPolicyBackdoor, GPOddity et OUned. Ils permettent d'injecter des configurations malveillantes dans les GPO de manière flexible (voir la documentation dans le projet GroupPolicyBackdoor). Alternativement et par commodité, il est également possible de fournir simplement une commande qui sera exécutée via une tâche immédiate en tant que SYSTEM.
Il est important de mentionner que dans la présente section, nous nous sommes concentrés sur l'utilisation du service SMB pour l'exploitation de GPO. Cependant, il ne s’agit que d'un exemple parmi d'autres. Des services SMB robustes et authentifiés peuvent être utilisés dans diverses situations. Par exemple, lors d'une mission interne, nous sommes récemment tombés sur le panneau d'administration d'un logiciel auquel nous avons accédé avec des identifiants par défaut. L'une des fonctionnalités proposées par le panneau d'administration consistait à fournir un chemin vers un fichier exécutable pour les mises à jour logicielles. Il était alors possible de spécifier un chemin SMB, de rediriger vers un serveur SMB authentifié et d'obtenir l'exécution de commandes sur le serveur sous-jacent. Cela n'aurait pas été possible avec un serveur SMB anonyme, car le client SMB Windows aurait refusé de s'y connecter. La faille Certighost a également récemment démontré le point que nous essayons de faire valoir ici, et ce ne sera probablement pas la dernière.
2. LDAP
Peu de temps après GPOddity, nous avons commencé à travailler sur un second vecteur d'attaque nécessitant de simuler un autre service Active Directory légitime et authentifié. Ce vecteur d'attaque était également lié aux Group Policy Objects, mais permettait l'exploitation de conteneurs Active Directory tels que les Unités Organisationnelles, les domaines et les objets Sites. Cela a abouti à la publication de l'outil OUned en avril 2024.
a. Rappels et contexte
Le vecteur d'attaque OUned est basé sur les travaux de Petros Koutroumpis et est expliqué en détail dans l'article associé.
En résumé, le vecteur d'attaque est exploitable lorsqu'un utilisateur contrôlé dispose de n'importe quel type d'autorisation lui permettant d'écrire l'attribut gPLink d'une Unité Organisationnelle, d'un Site ou même d'un objet domaine.
Dans un tel cas, l'attaquant serait en mesure d'ajouter un élément malveillant à la liste gPLink. Ledit élément indiquerait que les objets appartenant au conteneur cible doivent appliquer une GPO dont le GPC est situé sur un ordinateur contrôlé par l’attaquant. Par exemple, l'élément gPLink suivant forcerait tous les objets du conteneur à aller chercher un GPC sur attacker.corp.com :
[LDAP://cn={7B7D6B23-26F8-4E4B-AF23-F9B9005167F6},cn=policies,cn=system,DC=attacker,DC=corp,DC=com;0]
b. Implémentation précédente du serveur LDAP et limites
Après avoir empoisonné l'attribut LDAP gPLink, la machine contrôlée par l'attaquant doit être en mesure de jouer le rôle du Group Policy Container, en plus du Group Policy Template. Les prérequis de l'attaque vont alors un cran plus loin que pour GPOddity : l'attaquant doit non seulement agir en tant que service SMB légitime authentifié, mais aussi en tant que service LDAP légitime authentifié.
La première implémentation d'OUned en avril 2024 utilisait la même stratégie que celle présentée par Petros Koutroumpis dans sa recherche originelle pour la partie GPC. Au lieu d'implémenter un service LDAP, nous avons simplement redirigé le trafic LDAP vers un contrôleur de domaine Active Directory factice.
Dans le domaine factice, le mot de passe du contrôleur de domaine (DC) était synchronisé avec celui du compte machine avec le SPN LDAP sur le domaine cible. Cette machine résolvait vers un hôte contrôlé par l'attaquant, qui transférait le trafic LDAP vers le DC du domaine factice. Ce dernier pouvait vérifier le ticket Kerberos entrant grâce à la synchronisation des mots de passe, et agir en tant que service LDAP authentifié.
Bien que cette solution ait pu fonctionner dans certains cas, elle était loin d'être parfaite et présentait plusieurs inconvénients :
-
Premièrement, la configuration était assez fastidieuse, car elle nécessitait la mise en place d'un domaine Active Directory ainsi que la garantie d'un accès réseau approprié de la machine contrôlée au DC factice. Les configurations fastidieuses sont non seulement chronophages, mais aussi sujettes aux erreurs, ce qui devrait être évité, en particulier pour des considérations de discrétion.
-
Si le compte machine contrôlé (résolvant vers l'hôte de l'attaquant) était configuré pour utiliser AES pour les tickets Kerberos émis pour ses services, la configuration proposée ne fonctionnait pas. En effet, synchroniser le mot de passe du compte machine contrôlé avec celui du contrôleur de domaine factice résulterait en les mêmes clés RC4, mais pas les mêmes clés AES, qui sont calculées non seulement à partir du mot de passe lui-même, mais en incluant un sel construit à partir du nom d'hôte et du nom de domaine. Ce dernier était nécessairement différent pour le domaine factice, puisqu'il devait correspondre au nom de la machine avec le service LDAP.
Certes, en contrôlant le compte machine, il serait toujours possible de modifier l'attribut LDAP
msDS-SupportedEncryptionTypes(cela peut être modifié avec l'identité de la machine elle-même) pour passer à RC4. Cependant, cela n'est encore une fois pas souhaitable d’un point de vue discrétion. -
Enfin, synchroniser les mots de passe du compte machine contrôlé avec celui du DC factice est faisable, mais peut être loin d'être trivial selon la situation.
c. Création d'un serveur LDAP versatile avec scapy
La meilleure solution aux différents problèmes présentés ci-dessus consiste à implémenter directement un serveur LDAP dans l'outil OUned, en python – c'est-à-dire implémenter un service LDAP authentifié et fonctionnel en python.
Nous avons de nouveau rencontré le travail de Gabriel Potter sur scapy, qui a implémenté non seulement les couches d'authentification Windows, mais aussi un client LDAP avec diverses structures LDAP. Nous avons pu utiliser ce travail comme base pour l'implémentation d'un serveur LDAP en python.
La version de scapy incluant le serveur LDAP en question est la même que celle implémentant le serveur SMB multiplexing, et est donc disponible dans le project GPOddity (ainsi que le projet OUned). Le serveur en tant que tel est implémenté dans le fichier layers/ldapserver.py, et des modifications ont été apportées au fichier commun layers/ldap.py.
Veuillez noter que, tout comme pour le serveur SMB, nous n'avons pas encore créé de pull request vers scapy, mais nous le ferons dans un avenir proche.
L'idée derrière l'implémentation du serveur LDAP était de fournir une implémentation générique et flexible qui pourrait être réutilisée pour des recherches futures, plutôt que de construire uniquement des fonctionnalités LDAP spécifiques à l'exploit OUned. Il prend comme argument un fichier JSON qui représente les données LDAP à servir. Dans le fichier JSON, les clés correspondent au Distinguished Name des éléments LDAP, et les valeurs sont des dictionnaires contenant des attributs. Des éléments imbriqués peuvent être fournis. Il n'y a aucune vérification sur la cohérence des DN, ou de vérification de schéma LDAP (le serveur étant destiné à des fins de recherche, nous souhaitons laisser autant de liberté que possible concernant les données servies). Le fichier JSON peut spécifier un DTO personnalisé via la clé vide à la racine de l'objet JSON. Il est également possible de spécifier des valeurs d'attribut non imprimables en les préfixant par base64:.
Voici un exemple de fichier JSON contenant les données pour le serveur LDAP. Un DTO personnalisé est renseigné, simulant un domaine appelé corp.com. Une Unité Organisationnelle est ensuite définie (FINANCE), contenant plusieurs utilisateurs et d'autres Unités Organisationnelles imbriquées, contenant elles-mêmes des utilisateurs avec divers attributs de différents types.
$ cat ldap.json
{
"": {
"objectClass": [
"top",
"ScapyLDAProotDSE"
],
"altServer": [],
"namingContexts": [
"DC=CORP,DC=COM",
""
],
"supportedControl": [],
"supportedExtension": [],
"supportedFeatures": [],
"supportedLDAPVersion": [
3
],
"supportedSASLMechanisms": [
"GSSAPI",
"GSS-SPNEGO"
],
"defaultNamingContext": [
"DC=CORP,DC=COM"
],
"rootDomainNamingContext": [
"DC=CORP,DC=COM"
]
},
"OU=FINANCE,DC=corp,DC=com": {
"objectClass": [
"top",
"container"
],
"CN=bmetzler,OU=FINANCE,DC=corp,DC=com": {
"version": 5,
"samaccountname": "bmetzler",
"name": "Benjamin Metzler",
"objectClass": [
"top",
"person",
"organizationalPerson",
"user"
],
"description": "This is the user bmetzler",
"canonicalName": "Some canonical name for bmetzler ..."
},
"CN=isoliman,OU=FINANCE,DC=corp,DC=com": {
"version": 6,
"samaccountname": "isoliman",
"objectClass": [
"top",
"person",
"organizationalPerson",
"user"
],
"description": "This is the user isoliman",
"canonicalName": "Some canonical name for isoliman ..."
},
"OU=ACCOUNTING,OU=FINANCE,DC=corp,DC=com": {
"CN=istach,OU=ACCOUNTING,OU=FINANCE,DC=corp,DC=com": {
"version": "9",
"samaccountname": "istach",
"objectClass": [
"top",
"person",
"organizationalPerson",
"user"
],
"description": "This is the user istach",
"canonicalName": "Some canonical name for istach ..."
},
"OU=MadridOffices,OU=ACCOUNTING,OU=FINANCE,DC=corp,DC=com": {
"CN=ccorea,OU=MadridOffices,OU=ACCOUNTING,OU=FINANCE,DC=corp,DC=com": {
"sAMAccountName": "ccorea",
"objectClass": [
"top",
"person",
"organizationalPerson",
"user"
],
"description": [
"First description item",
"Second description item"
],
"canonicalName": "Some canonical name for ccorea ..."
}
}
}
}
}
Le serveur LDAP prend en charge les opérations standards d'ajout, de suppression, de modification et de recherche (mais pas les extensions LDAP). Pour les opérations qui entraînent une modification des données LDAP, le fichier de données JSON est modifié. Lors de la recherche, les portées BASE, LEVEL et SUBTREE peuvent être spécifiées. Les filtres standards sont pris en charge (présent, inférieur ou égal, supérieur ou égal, égal), tandis que les filtres de sous-chaîne et extensibles ne sont pas encore implémentés – il est possible de spécifier si ces filtres doivent retourner True ou False.
Voici ci-dessous un exemple simple de l'exécution du serveur LDAP. Il est configuré pour utiliser le fournisseur de support de sécurité (SSP) SPNEGO pour l'authentification, car les clients LDAP Windows enveloppent par défaut leur authentification dans SPNEGO. Le compte machine utilisé pour simuler le serveur LDAP (et qui se résout vers notre machine d'attaque) est SCAPY$ et est configuré pour utiliser AES256 pour l'authentification Kerberos. Il est possible de calculer la clé AES associée, que nous fournissons pour authentifier correctement les clients entrants. Le serveur LDAP est ensuite instancié avec le fichier de données JSON qui a été créé.
$ cat srv.py
from scapy.layers.ldapserver import ldapserver
from scapy.layers.ntlm import NTLMSSP, NTLMSSP_DOMAIN, MD4le
from scapy.layers.spnego import SPNEGOSSP
from scapy.layers.kerberos import KerberosSSP, Key, EncryptionType
ssp = SPNEGOSSP([KerberosSSP(KEY=Key(
EncryptionType.AES256_CTS_HMAC_SHA1_96,
key=bytes.fromhex("c2e2bee9c44cdbb2be3d438be986e74ccb3d4e6b401e7e3cf6a83225cb841822"),
),
SPN="ldap/scapy.corp.com")])
ldapserver(
data="ldap.json",
ssp=ssp,
ACCEPT_EXTENSIBLE=False
)
$ python3 srv.py
Server LDAP_Server started listening on ('192.168.123.20', 389)
Il est maintenant possible d’utiliser des clients LDAP Windows standards (tels que le client Powershell DirectorySearcher) depuis une machine Windows jointe au domaine pour interagir avec le serveur LDAP. La capture d’écran ci-dessous montre une recherche standard spécifiant un scope, un filtre, et les attributs à récupérer.
L'exécution d'une capture réseau en parallèle montre un échange LDAP standard entre le client LDAP Windows et le serveur python – une première requête rootDSE, suivie d'une requête bind (dans laquelle le ticket Kerberos de l'utilisateur pour le service LDAP du serveur scapy.corp.com est intégré), et enfin une requête de recherche à laquelle le serveur répond. À noter que les échanges sont en mode Intégrité, ce qui est la valeur par défaut pour DirectorySearcher. Le serveur LDAP s'adaptera par défaut à ce qui est demandé par le client (confidentialité, intégrité ou rien). Il est possible d’exiger le mode intégrité ou confidentialité dans les paramètres du serveur.
L’objectif principal était de permettre aux clients LDAP Windows d’interagir avec le serveur LDAP. Des efforts ont également été faits afin de permettre à d’autres clients LDAP Unix de fonctionner. Les cas suivants sont pour le moment testés et supportés :
-
ldapsearch utilisant SPNEGO.
$ ldapsearch -H ldap://scapy.corp.com -Y GSS-SPNEGO -b "OU=FINANCE,DC=corp,DC=com" '(&(objectClass=user))' "*"
-
ldap3 utilisant Kerberos et GSSAPI.
$ ldeep ldap -n -d corp.com -k -s ldap://scapy.corp.com --base 'OU=Finance,DC=corp,DC=com' search '(objectClass=user)'
Il doit cependant être noté que les configurations de certains clients LDAP sous Unix ne fonctionneront pas avec le serveur LDAP présenté, en raison de certaines particularités. Par exemple, ldapsearch, dans n'importe quel mode d’intégrité lors de l'utilisation de GSSAPI et Kerberos, place le champ krb5_sgn_cksum à la fin du paquet (après le payload GSSAPI). De même, lorsque la bibliothèque ldap3 demande une authentification NTLM, la requête bind spécifie le flag 137, qui n'est pas standard et n'est pas analysé par Wireshark. Ces spécificités sont principalement liées à la couche d'authentification. Elles ne sont pas implémentées dans cette première version actuelle du serveur LDAP, car les clients LDAP sous Unix n'étaient pas une priorité, mais pourraient l'être à l'avenir.
d. Démonstration du serveur LDAP avec OUned
À titre de démonstration, la vidéo suivante montre l'utilisation du serveur LDAP via OUned. Plus précisément, un utilisateur nommé anail dispose des autorisations WriteGPLink sur une Unité Organisationnelle SERVERS, contenant une OU imbriquée appelée PKI_SERVERS, qui contient elle-même la machine AD01-PKI. Après avoir compromis anail, OUned est utilisé afin de compromettre AD01-PKI en empoisonnant l'attribut gPLink de l’OU SERVERS, en distribuant un GPC malveillant via le serveur LDAP, et un GPT malveillant via le serveur SMB.
3. Conclusion
Au-delà de l'exploitation des Group Policy Objects, il reste très probablement encore de nombreuses attaques à découvrir qui reposent sur l'usurpation de services Active Directory authentifiés. Ce type d'attaques peut avoir des impacts critiques en matière d'élévation de privilèges dans un environnement Active Directory – cela a été illustré ci-dessus par l'exploitation des GPO, mais aussi plus récemment par la faille Certighost.
La motivation derrière la recherche présentée était donc de faciliter la découverte de ce type d'attaques en fournissant des implémentations Python robustes et génériques pour les services LDAP et SMB. Si vous êtes un chercheur Active Directory, nous espérons que cela pourra vous aider dans vos tests. De plus, les scripts sont loin d'être parfaits, alors n'hésitez pas à y contribuer et à vous appuyer dessus.