Comment des attaquants potentiels peuvent obtenir une persistance des privilèges sur un DC par l'intermédiaire de DnsAdmins
L'équipe de recherche de Semperis a récemment développé une recherche antérieure montrant un abus de fonctionnalité dans l'environnement Windows Active Directory (AD) où les utilisateurs du groupe DnsAdmins pouvaient charger une DLL arbitraire dans un service DNS fonctionnant sur un contrôleur de domaine. Yuval Gordon de l'équipe de recherche de Semperis a développé cette recherche pour montrer comment un problème avec la technique précédente peut être surmonté, et pour montrer qu'un adversaire pourrait utiliser cette tactique pour laisser une porte dérobée non détectée dans l'Active Directory d'une entreprise - un risque important pour la sécurité.
Dans un post sur Medium en 2017, des chercheurs en sécurité ont montré comment les utilisateurs du groupe DnsAdmins pouvaient utiliser une "fonctionnalité" du protocole de gestion DNS de Microsoft pour que le service DNS charge n'importe quelle DLL. Ce service s'exécute sur les contrôleurs de domaine en tant que NT AuthoritySystem, ce qui permet aux DnsSAdmins d'escalader leurs privilèges jusqu'à SYSTEM sur DC (avec des autorisations au moins égales à celles des Admins de domaine). Cette "astuce mignonne", comme l'a appelée le chercheur original, Shay Ber, peut être utile aux équipes rouges qui explorent l'escalade des privilèges AD et constitue une porte dérobée potentielle pour les attaquants dans le contrôleur de domaine.
Lire la suite
Dans ce billet, je vais développer la recherche de Shay Ber en montrant comment surmonter un problème avec la technique précédente et comment la rendre plus furtive. Je passerai également en revue les autorisations requises pour montrer qu'un adversaire pourrait utiliser cette tactique pour laisser une porte dérobée à DC qui ne serait probablement pas remarquée et pourrait contourner certains outils.
Pour récapituler les recherches précédentes menées par Shay Ber et Nikhil Mittal, ils ont montré qu'il est possible de charger une DLL arbitraire dans un service DNS fonctionnant sur un contrôleur de domaine en suivant les étapes suivantes :
- L'attaquant doit faire partie du groupe DnsAdmins pour exécuter l'exécutable dnscmd et faire pointer le service vers une DLL sur un chemin UNC.
- L'attaquant redémarre le service DNS en utilisant "sc.exe stop/start dns" - il s'agit d'une limitation car les configurations par défaut ne permettent pas aux utilisateurs DnsAdmins d'arrêter et de démarrer les services.
Mes recherches permettent de mieux comprendre cette capacité d'attaque de la manière suivante :
- Il n'est pas strictement nécessaire de faire partie de DnsAdmins pour réaliser cette attaque, mais seulement d'avoir un accès en lecture/écriture au conteneur MicrosoftDNS et un accès RPC au DC.
- Il est possible de redémarrer le service DNS avec les seuls privilèges énumérés au point 1.
Il est donc possible pour un attaquant disposant de privilèges suffisants de créer/mettre à jour un utilisateur disposant d'autorisations minimales et uniquement d'un accès direct en lecture/écriture au conteneur MicrosoftDNS. Dans la plupart des cas, cet utilisateur passerait "sous le radar", car les autorisations des conteneurs sont rarement contrôlées. Cet utilisateur pourrait ne pas être détecté et pourrait être utilisé pour exécuter des commandes arbitraires avec les privilèges DC SYSTEM en chargeant une DLL dans le processus DNS.
Contexte de la recherche
Il y a trois ans, dans le billet Medium "Feature, not bug : DnsAdmin to DC compromise in one line", Shay Ber notait que n'importe quel membre du groupe DnsAdmins pouvait faire en sorte que le service DNS Server charge une DLL en tant que NT AuthoritySystem sur le DC au prochain redémarrage du service.
Par défaut, DnsAdmins ne dispose pas des autorisations nécessaires pour redémarrer à distance le service sur le DC, ce qui signifie que les attaquants qui souhaitent en abuser devront attendre (ou utiliser d'autres méthodes) que le serveur ou le service soit redémarré pour que leur charge utile soit exécutée.
Ma mission de recherche consistait à trouver un moyen de forcer le service à redémarrer ou à charger la DLL instantanément, et ce de la manière la plus furtive possible. (Même si j'avais les permissions de redémarrer à l'aide de sc.exe, cela générerait de nombreux journaux et serait probablement détecté). Cette approche s'est avérée étonnamment facile mais a eu des conséquences inattendues. Avant d'aller plus loin, je recommande de lire d'abord la recherche originale de Ber.
Un nouveau départ
J'ai commencé par passer en revue les spécifications de MS-DNSP, à la recherche de toute mention du mot "Restart", lorsque j'ai rencontré ce qui suit :
Cette ligne indique que l'une des commandes acceptées par le serveur (pszOperation) dans R_DnssrvOperation est restart, ce qui aura pour effet (sans surprise) de redémarrer le processus du serveur DNS. Cela semble parfait !
Pour me faciliter la vie, j'ai consulté la documentation de l'outil dnscmd (outil CLI pour la gestion des serveurs DNS) et j'ai cherché un moyen de l'utiliser pour envoyer une commande de redémarrage au serveur DNS, mais je n'ai rien trouvé.
Je me suis dit que je devais essayer de regarder les chaînes de l'outil pour "redémarrer" et (étonnamment), c'était là :
Bien que ni la documentation Microsoft ni l'aide de l'outil ne le mentionnent, il existe un commutateur "/Restart" que vous pouvez utiliser dans dnscmd pour déclencher le redémarrage du processus d'un serveur DNS.
Le service a déclenché un redémarrage interne, en appelant une fonction interne appelée "reloadShutdown", puis en redémarrant tout sans terminer le processus, contrairement à un redémarrage normal du service.
Je savais maintenant comment déclencher un rechargement et charger la DLL instantanément, mais quelles étaient les autorisations requises ?
Si l'on se réfère à la documentation, la vérification initiale de toute opération DNS consiste à tester les informations d'identification du client pour obtenir le privilège de lecture sur le conteneur MicrosoftDNS. Le droit d'écriture sur le même objet est également requis pour nos opérations spécifiques (redémarrage et ServerLevelPluginDll).
À ce stade, j'avais déclenché un rechargement, obligeant le service à charger la DLL envoyée précédemment (la commande présentée dans la section du scénario) et laissant moins de traces derrière lui car aucun journal sur le redémarrage du service ou la création de processus n'était généré.
Les journaux générés, tous deux dans le journal du serveur DNS, étaient l'ID d'événement 770 (un plugin DLL au niveau du serveur a été chargé) et l'ID d'événement 140 :
Dans la section suivante, j'expliquerai pourquoi cette erreur est générée. Si vous souhaitez ne pas entrer dans les détails, vous pouvez passer directement à la section "impact potentiel".
Le bogue
Un événement inattendu s'est produit lors de l'exécution de la commande de redémarrage : La DLL a été chargée (comme prévu) et le service a continué à fonctionner, mais le journal suivant a été généré dans le journal du serveur DNS :
La vérification du code d'erreur a montré qu'il y avait un problème avec le point de terminaison RPC (0x6cc - Le point de terminaison est un duplicata.).
Lorsque j'ai essayé d'envoyer une autre commande au même serveur DNS, en utilisant dnscmd, rien n'a fonctionné :
Même la console de gestion DNS ne fonctionne plus :
Pour comprendre ce qui ne fonctionnait pas, j'ai utilisé ApiMonitor et j'ai vu qu'il y avait un problème avec l'enregistrement du point de terminaison après le redémarrage.
Lorsque l'on redémarre le service du serveur DNS de la manière habituelle (par exemple, services.msc), le service s'arrête et démarre sans qu'aucune erreur ne soit générée. Par conséquent, l'étape suivante a consisté à déterminer pourquoi une erreur n'était pas générée ici, contrairement à ce qui se passe lorsqu'une commande de rechargement utilisant /restart est transmise au serveur.
Cependant, une chose intéressante s'est produite. Le redémarrage et le rechargement du service ont tous deux échoué lors du même appel à l'API.
Ce message vous rappelle quelque chose, n'est-ce pas ? Le service a échoué à désenregistrer le point de terminaison RPC et a ensuite généré une erreur en double lorsqu'il a essayé d'utiliser le point de terminaison.
Le fait qu'il n'y ait pas eu d'erreurs ultérieures après un redémarrage de service s'explique par le fait que lorsque le redémarrage de service a lieu, le processus se termine, ce qui entraîne le désenregistrement automatique du point de terminaison. Cela ne se produit pas lorsque vous déclenchez un rechargement de service (le processus n'est pas réellement redémarré, d'où l'absence de sortie du processus).
Impact potentiel
En utilisant ces méthodes, un attaquant peut exécuter des DLL arbitraires dans le contexte de sécurité du service DNS.
Comme le service DNS est souvent exécuté sur des contrôleurs de domaine, cette vulnérabilité permet aux attaquants d'élever les privilèges de n'importe quel utilisateur du domaine sous leur contrôle. Comme décrit dans la section suivante, en plus de faire progresser leur attaque actuelle, les spécificités des ACL des conteneurs d'Active Director donnent aux attaquants la possibilité de créer des "administrateurs dormants" - et des utilisateurs non privilégiés qui peuvent escalader leurs privilèges à volonté.
Pour que ces attaques fonctionnent, un attaquant doit disposer des éléments suivants :
- Dnscmd (pas nécessaire mais permet d'économiser du temps et des efforts)
- Le CPR ouvert à la DC
- Droits de lecture et d'écriture sur le conteneur MicrosoftDNS (dans le conteneur système)
Exemple de scénario
Les attaquants qui parviennent à compromettre un utilisateur du groupe DnsAdmin peuvent étendre leur emprise de plusieurs façons. Outre l'escalade immédiate des privilèges vers l'utilisateur SYSTEM d'un DC (ce qui leur permet d'ajouter des utilisateurs Domain Admin ou même d'obtenir une Skeleton Key), ils peuvent également donner à un utilisateur peu privilégié la possibilité d'élever ses privilèges en lui attribuant un accès en lecture/écriture au conteneur MicrosoftDNS dans le cadre de leur attaque initiale.
Cet utilisateur peut être créé par l'attaquant ou par un utilisateur existant avec un mot de passe compromis ou un SPN connu pour un kerberoasting ultérieur. Alors que les groupes AD privilégiés, tels que les administrateurs de domaine, sont souvent étroitement surveillés, les ACL d'objets exacts sont notoirement difficiles à surveiller efficacement. Cela crée une situation dans laquelle les utilisateurs semblent ne pas avoir de privilèges mais peuvent en réalité redémarrer le service DNS et injecter à nouveau des DLL pour retrouver un accès privilégié, ce qui constitue une porte dérobée persistante dans l'environnement.
Dans la capture d'écran ci-dessus, l'utilisateur "haxer" ressemble à un utilisateur non privilégié parce qu'il n'est qu'un membre des utilisateurs du domaine. Mais tout ce dont il a besoin pour devenir un administrateur de domaine, c'est d'avoir un accès RPC au DC et un accès en écriture à un partage accessible par le DC.
Dans cet exemple, la DLL lancera un shell au chargement qui recevra des commandes d'un fichier dans un partage (command.txt) et écrira la sortie dans un autre fichier dans le même partage (out.txt).
L'attaquant se connecte en tant qu'utilisateur "haxer", qui n'est actuellement membre d'aucun groupe privilégié, et crée un partage avec accès en lecture/écriture à tout le monde avec sa DLL malveillante à l'intérieur :
Il ne reste plus à l'attaquant qu'à enregistrer la DLL en tant que ServerLevelPluginDll sur le DC, à déclencher un redémarrage du service et à commencer à envoyer des commandes à l'aide du fichier qu'il va créer :
Deux aspects de ce scénario sont importants à retenir :
- Si le DC ne peut pas accéder au chemin envoyé en tant que ServerLevelPluginDLL, le déclencheur de redémarrage mettra fin au service DNS et le service ne pourra pas redémarrer tant que le DC ne pourra pas accéder au chemin ou que le chemin ne sera pas supprimé du registre du DC. (Et en tant que DnsAdmin, nous ne pourrons probablement pas démarrer le service lorsqu'il est arrêté).
- En raison du bogue décrit ci-dessus, le déclenchement d'un rechargement entraînera des problèmes de duplication du point de terminaison RPC, et nous ne pourrons plus envoyer de commandes ni déclencher un autre redémarrage :
Enregistrement et détection
Voici quelques mesures que les entreprises peuvent prendre pour enregistrer et détecter les intrusions potentielles. Tout d'abord, il est important de noter que si le processus du serveur DNS a été redémarré à l'aide de l'opération restart pszOperation, il ne générera PAS de journal avec l'ID d'événement 7036 car il ne s'agit pas d'un redémarrage de service.
1. L'ID d'événement 770 sera créé dans le journal du serveur DNS sur le DC lorsqu'il chargera la DLL après un redémarrage, avec le chemin d'accès à la DLL spécifié.
2. L'ID d'événement 140 sera créé lorsqu'un redémarrage sera déclenché à l'aide de R_DnssrvOperation (uniquement à cause du bogue ; idéalement, cela ne se produira plus à l'avenir).
3. Surveillez les changements de ntSecurityDescriptor sur MicrosoftDNS et le groupe DnsAdmins (ID d'événement 5136).
4. Surveiller l'ajout de membres au groupe DnsAdmins.
5 Traitez les utilisateurs et les groupes qui disposent déjà de ces autorisations comme des administrateurs de domaine et surveillez-les en conséquence.
6. Les utilisateurs et les groupes disposant de ces autorisations doivent être ajoutés au groupe Utilisateurs protégés pour une protection renforcée.
7. En utilisant IPSIDS, vous pouvez surveiller les requêtes R_DnssrvOperation et R_DnssrvOperation2 des ordinateurs non administrateurs vers les serveurs DNS.
8. Surveiller les modifications apportées au chemin de registre "HKLMSYSTEMCurrentControlSetServicesDNSParametersServerLevelPluginDLL" sur les serveurs DNS.
Protéger votre service Microsoft DNS
En mettant en œuvre ces meilleures pratiques de journalisation et de détection, votre organisation peut se prémunir contre un scénario dans lequel les utilisateurs qui disposent déjà d'autorisations pour gérer le DNS peuvent l'utiliser pour compromettre un contrôleur de domaine, ou laisser une porte dérobée furtive dans le domaine pour des intrus indésirables.