Shai Laron | Chercheur en sécurité Chercheur en sécurité

Active Directory (AD) reste le joyau de l'infrastructure d'entreprise, et pour les cybercriminels, l'objectif ultime est clair : obtenir des privilèges d'administrateur de domaine. Ce niveau de privilège confère en effet un contrôle total sur votre environnement. La protection des identités joue donc un rôle essentiel dans la sécurité des entreprises, et celles-ci déploient des efforts considérables pour empêcher les cybercriminels d'accéder aux identifiants des administrateurs.

Mais que se passerait-il si des pirates parvenaient simplement à induire en erreur les contrôleurs de domaine (DC), les amenant ainsi à les identifier comme étant quelqu'un d'autre ?

Il y a quelque temps, j’ai assisté à une conférence de Yossi Sassi sur les techniques de persistance dans Active Directory. M. Sassi a présenté un concept que je ne connaissais pas : la possibilité d’ajouter des caractères Unicode « invisibles » aux attributs des objets. Il a présenté cette fonctionnalité comme une technique de persistance permettant de créer des utilisateurs dont le nom semble être identique à celui d’autres utilisateurs légitimes existants. Cette technique peut semer la confusion au sein des équipes de sécurité et informatiques et retarder considérablement les enquêtes.

Le sujet a éveillé ma curiosité. Je me suis demandé :

  • Active Directory accepte-t-il d'autres caractères « invisibles » ?
  • Comment détecter efficacement l'utilisation abusive de ces caractères ?
  • Les caractères masqués ont-ils d'autres utilités, outre la persistance ?

Cet article détaille mes recherches sur ces questions, qui ont conduit à la découverte de deux nouvelles vulnérabilités d’élévation de privilèges dans Active Directory : KerberLoss (CVE-2026-25177) et ResetNightmare (CVE-2026-27912). Chacune de ces vulnérabilités utilise une approche unique pour provoquer une confusion d'identité sur les contrôleurs de domaine, ce qui entraîne des conséquences variées. La seconde (et la plus grave) permet à un utilisateur disposant de privilèges limités d'obtenir instantanément les privilèges d'administrateur de domaine.


Recherches préliminaires : Unicode et Active Directory

Mes recherches m'ont entraîné dans une exploration passionnante autour d'Unicode et du traitement LDAP côté serveur. J'ai commencé par rechercher sur Internet les caractères Unicode susceptibles d'apparaître invisibles, puis je les ai répertoriés dans une liste. Je m'intéressais particulièrement aux caractères invisibles au sein des noms d'objets, car cela me semblait être la manière la plus intéressante d'exploiter cette technique à des fins malveillantes.

Pour commencer mes tests, j'ai créé un compte utilisateur d'exemple nommé UniqueUser. Comme on pouvait s'y attendre, il n'était pas possible de créer un autre compte utilisateur portant le même nom (Figure 1).

La création d'utilisateurs en double est bloquée par défaut.
Figure 1. La création d'utilisateurs en double est bloquée par défaut

Mais en utilisant un caractère « invisible », j'ai pu créer un compte utilisateur apparemment identique (figure 2).

En ajoutant un caractère Unicode masqué à la chaîne, la commande s'exécute correctement
Figure 2. En ajoutant un caractère Unicode masqué à la chaîne, la commande s'exécute correctement

La figure 2 montre que la console PowerShell analyse le compte obtenu comme comportant un espace inhabituel (avant « ser »). Cependant, lorsque l'on examine ces deux comptes dans les outils graphiques de gestion d'Active Directory, ils semblent identiques (figure 3).

Figure 3. Les différents utilisateurs semblent identiques en raison de caractères masqués

À l'aide de cette méthode de base, j'ai parcouru ma liste de caractères Unicode, en créant des comptes utilisateurs avec chacun d'entre eux. Comme le montre la figure 4, certains caractères ont été analysés de manière étrange, et certainement pas « invisible », dans Active Directory.

Figure 4. Certains caractères Unicode étaient visibles au lieu d'être masqués

Après avoir supprimé ces éléments, il me restait un répertoire similaire à celui de la figure 5.

Mon répertoire après avoir supprimé les utilisateurs dont le nom contient des caractères visibles
Figure 5. Mon répertoire après avoir nettoyé les comptes utilisateurs contenant des caractères visibles

De plus, bien que mon script ait ignoré certains caractères parce que le DC ne les acceptait pas, il en a également ignoré d’autres parce que le DC considérait que le nom d’utilisateur existait déjà. (Avec le recul, c’était un signe avant-coureur.) Afin de tester autant de ces caractères que possible sans être limité par les contraintes d’unicité, j’ai également créé d’autres noms d’utilisateur (Figure 6), chacun représentant un caractère invisible unique caché entre deux tirets (« – »).

Création d'utilisateurs distincts pour chaque caractère Unicode masqué
Figure 6. Création d'utilisateurs distincts pour chaque caractère Unicode masqué

À ce stade, j'étais convaincu que ma liste de 385 caractères invisibles répondait à ma première question de recherche. Il était désormais temps de s'attaquer au défi de la détection.


Une approche différente en matière de détection

Dans sa présentation initiale, Sassi a présenté un outil permettant de détecter les caractères Unicode cachés dans les objets AD. Je souhaitais comprendre le fonctionnement de cet outil. Le script repose sur une approche simple :

  • Créez un dictionnaire de 29 personnages « invisibles ».
  • Récupérer toutes les propriétés de tous les objets du domaine.
  • Parcourir chaque attribut.
    • Convertir l'attribut en un tableau de caractères.
    • Récupérez la valeur hexadécimale de chaque caractère et comparez cette valeur à celle du dictionnaire.

Cette approche est exhaustive. Cependant, je privilégie les détections pouvant fonctionner de manière systématique et efficace dans des environnements de production ; je me suis donc demandé si le filtrage pouvait être effectué directement au sein des requêtes LDAP (c'est-à-dire côté serveur) plutôt que de traiter chaque caractère individuellement côté client.

Pour le savoir, j’ai d’abord effectué un test simple afin de déterminer si le DC pouvait traiter ces caractères « tels quels ». J’ai converti un caractère (0x200B) sous forme de chaîne « invisible », puis je l'ai copiée et collée dans PowerShell (Figure 7).

Une requête dans laquelle 0x200B a été inséré entre « Unique » et « User »
Figure 7. Une requête dans laquelle la valeur 0x200B a été insérée entre « Unique » et « User »

Jusqu'ici, tout va bien ! En collant directement le caractère dans un filtre LDAP, je n'ai obtenu que l'objet attendu. Cependant, lorsque j'ai commencé à tester différents caractères, je me suis retrouvé confronté à des situations un peu plus étranges.

J'ai refait le même test, mais cette fois-ci, j'ai utilisé le caractère 0x200C, qui est un autre caractère invisible. Comme ce caractère n'a pas passé les tests de validité des noms d'utilisateur, je n'avais pas d'utilisateur nommé Unique{0x200C}User dans mon répertoire à ce moment-là. Cependant, ma requête a tout de même renvoyé un résultat (Figure 8).

Une requête dans laquelle 0x200C a été inséré entre « Unique » et « User »
Figure 8. Une requête dans laquelle la valeur 0x200C a été insérée entre « Unique » et « User »

Les plus observateurs remarqueront peut-être que, cette fois-ci, le résultat ne présente pas cet espacement étrange que j'avais constaté lors de l'affichage de ces caractères sur la console. Après avoir vérifié l'identifiant de sécurité (SID) de l'utilisateur renvoyé, j'ai confirmé qu'il s'agissait bien du nom d'utilisateur sans caractères supplémentaires. Le caractère Unicode présent dans le filtre a été ignoré, soit par PowerShell, soit par le contrôleur de domaine.

Si ma théorie était correcte, il suffirait alors de modifier la requête pour rechercher tout objet comportant le 0x200C Ce caractère devrait afficher tous les objets présents dans le répertoire. Cette fois-ci, j'ai converti la valeur Unicode affichée dans la console afin d'améliorer la lisibilité (Figure 9).

La recherche d'un SamAccountName contenant 0x200C renvoie toutes les identités du domaine
Figure 9. La recherche d'un SamAccountName contenant 0x200C renvoie toutes les identités du domaine

À ce stade, le comportement du serveur LDAP n'était pas cohérent : certains caractères étaient pris en compte, tandis que d'autres étaient ignorés. Souhaitant écarter la possibilité que la chaîne de filtrage soit à l'origine du problème, j'ai consulté la norme RFC 4515, qui stipule :

La représentation sous forme de chaîne d'un filtre de recherche LDAP est une chaîne composée de caractères Unicode encodés en UTF-8.

La RFC fournit également quelques exemples. Cela m’a donné l’espoir qu’il soit possible de convertir et d’interroger n’importe quel caractère Unicode, qu’il soit imprimable ou non. En m’appuyant sur la RFC, j’ai créé la fonction suivante pour recevoir une valeur hexadécimale Unicode (par exemple, 0x200B) et renvoie sa chaîne compatible avec les filtres LDAP (Figure 10).

La fonction Convert-UnicodeToLdapUtf8, basée sur la norme RFC 4515
Figure 10. La fonction Convert-UnicodeToLdapUtf8, conforme à la norme RFC 4515

J'ai vérifié que ma fonction effectuait correctement la conversion en utilisant la valeur 0x200B, que j'avais réussi à filtrer lors de mes tests précédents (figure 11).

Vérification du bon fonctionnement de la fonction en testant la valeur 0x200B
Figure 11. Vérification du bon fonctionnement de la fonction en testant la valeur 0x200B

Après avoir vérifié cela, j'ai ensuite testé les caractères qui posaient problème (figure 12).

Test de la fonction présentant le problème 0x200C
Figure 12. Test de la fonction avec le code d'erreur 0x200C

Malheureusement, même en suivant les recommandations du RFC, cela n'a pas fonctionné. En utilisant cette méthode pour rechercher mes 385 caractères invisibles avec LDAP, j'ai observé trois catégories distinctes de caractères :

  • Caractères pouvant faire l'objet d'un filtrage : seulement 106 sur 385
  • Caractères considérés comme des espaces : même s’ils sont invisibles dans l’interface graphique, la commande « char » renvoie tous les noms d’objets contenant des espaces (par exemple, « Domain Admins », « Print Operators »)
  • Les caractères totalement ignorés par la fonction DC : char renvoie tous les objets

À ce stade, j'étais trop impliqué. Il fallait que je sache s'il existait un moyen de filtrer ces caractères. En réfléchissant à ce qui pourrait être modifié (outre la chaîne de filtrage elle-même), je me suis demandé si l'un des Commandes étendues LDAP pourrait m'aider. En passant en revue les différentes options, la seule qui a retenu mon attention était LDAP_SERVER_SORT_OID. Même si la description du contrôle ne mentionne que l'ordre de tri, La documentation AD le précise à plusieurs reprises que la présence de cet élément de contrôle influe également sur le comportement de la comparaison des chaînes Unicode.

Pour vérifier cela, j'ai créé une fonction qui m'a permis, très facilement, d'ajouter le LDAP_SERVER_SORT_OID contrôle et pour interroger LDAP avec n’importe quel « OID de règle de tri » de mon choix. J’ai ensuite recréé un scénario auquel j’avais été confronté sur un Forum Microsoft. J'ai créé deux utilisateurs :

  • Shai
  • Shäi II

Notez la différence entre « a » (Unicode 0x0061) et « ä » (Unicode 0x00E4).

J'ai vérifié la différence entre une requête LDAP sans le contrôle étendu (qui utilise par défaut l'anglais américain) et une requête avec ce contrôle et une règle de tri (« Swedish »). La deuxième approche a effectivement modifié le résultat renvoyé, et pas seulement l'ordre de tri (figure 13).

Des « règles de tri » différentes selon les langages renvoient des objets différents pour un même filtre
Figure 13. Les « règles de classement » propres à chaque langage renvoient des objets différents pour un même filtre

Ce problème s'est également produit lorsque j'ai utilisé mon Convert-UnicodeToLdapUtf8 fonction permettant de convertir « ä » en sa valeur échappée en UTF-8 (Figure 14).

Les règles de tri continuent de fonctionner de manière cohérente lors de la conversion en UTF-8
Figure 14. Les règles de tri restent cohérentes lors de la conversion au format UTF-8

J'espérais que l'un de ces OID de règles de tri permettrait peut-être de « filtrer l'infiltrable » en ce qui concerne ma liste de caractères invisibles.

J'ai écrit un script qui passe en revue chaque règle de tri et interroge chaque caractère non filtrable à l'aide de celle-ci. J'en ai conclu qu'aucune règle de tri ne permettait au DC de « détecter » l'un quelconque des caractères problématiques.

C'est ainsi que s'est achevé mon travail d'analyse des caractères LDAP invisibles sous l'angle de la détection, me laissant face à un problème non résolu : il existe des caractères Unicode que le serveur LDAP d'Active Directory ignore complètement.

Cependant, ce problème de détection offrait une possibilité offensive intéressante.


Changer de casquette

Pour résumer rapidement :

  • Certains caractères Unicode ne sont pas correctement analysés par Active Directory et apparaissent donc comme invisibles.
  • Certains de ces caractères ne peuvent pas être filtrés par LDAP, ce qui signifie que lors d'un filtrage sur une valeur « normale », des valeurs contenant ces caractères pourraient être renvoyées.

En substance, il y a un potentiel pour contourner les contraintes d'unicité pour tout attribut basé sur Unicode. Lors de mes premiers tests avec SamAccountNameet cnJ'ai remarqué qu'Active Directory empêche (à juste titre) la création d'objets en double contenant des caractères non filtrables. Cependant, cela ne s'appliquait pas à tous les attributs.

En 2021, Microsoft a publié un correctif pour une vulnérabilité identifiée sous le numéro CVE-2021-42282. Ce correctif a introduit trois nouveaux contrôles de vérification de l'unicité :

  • Caractère unique du nom principal de l'utilisateur (UPN)
  • Caractère unique du nom principal de service (SPN)
  • Unicité des alias SPN

Chacune de ces valeurs doit être unique dans toute la forêt, et ces trois vérifications sont appliquées par défaut à l'échelle de la forêt par le dSHeuristics attribut. Tout utilisateur disposant de privilèges inférieurs à ceux d'un administrateur de domaine recevra un « message d'erreur d'unicité » s'il tente de définir une valeur qui enfreint ces contrôles de vérification de l'unicité.

Cependant, comme vous le verrez dans les sections suivantes, des caractères impossibles à filtrer peuvent permettre à des utilisateurs disposant de privilèges limités de contourner ces contrôles de vérification. Il s'agit de la première vulnérabilité que j'ai découverte. En raison de ses diverses répercussions, je l'ai baptisée « KerberLoss »; Microsoft lui a attribué le numéro CVE-2026-25177.


KerberLoss : À partir des SPN

Pour comprendre l'impact de cette vulnérabilité, il faut d'abord bien comprendre le principe des noms d'entité de service (SPN).


Que sont les noms de principal de service ?

Les SPN constituent un concept d'Active Directory souvent mal compris. Un SPN est le moyen par lequel Kerberos identifie les instances de service. Dans le contexte d'Active Directory, un service désigne toute ressource à laquelle un utilisateur accède et qui nécessite généralement une authentification. On peut citer, à titre d'exemple, les partages de fichiers SMB (cifs), les connexions Bureau à distance (TERMSRV), LDAP et HTTP… qui sont tous des exemples de ce que l'on appelle des « classes de services ».

Des services d'une même classe peuvent s'exécuter sur différents hôtes, et un même hôte peut fournir des services de plusieurs classes. Par conséquent, un SPN doit inclure ces deux composants, selon la structure de base suivante :

<service class>/<host>

Remarque : les SPN peuvent également comporter deux éléments supplémentaires facultatifs, qui ne font pas l'objet du présent document.

Les principes de base énumérés ci-dessus sont assez connus. Je tiens toutefois à souligner quelques points importants :

  • Dans l'univers d'Active Directory et de Kerberos, un service est hébergé par une identité quelconque. Il peut s'agir d'un compte d'ordinateur, d'un compte utilisateur ou d'un compte de service géré, mais il doit nécessairement y avoir une identité derrière le service. Cela s'explique par les principes cryptographiques sur lesquels repose Kerberos. Les tickets de service sont chiffrés à l'aide du secret appartenant au « service cible » (c'est-à-dire son identité).
  • L'objet représentant chaque identité dans Active Directory possède un attribut nommé servicePrincipalName, qui sert à gérer la liste des SPN de l'identité.
  • Lorsqu'il demande un ticket de service Kerberos, le centre de distribution de clés (KDC) utilise le SPN figurant dans la requête pour identifier le service cible. Dans ce contexte, le service cible correspond à l'identité associée au SPN concerné.
  • Enfin, les tickets Kerberos comportent une partie en clair et une partie chiffrée. Le SPN se trouve dans la partie en clair du ticket. Comme le SPN n’est pas chiffré, il est possible de le modifier dans les tickets et de les transférer entre différents services appartenant à la même identité. Ce qui importe, c’est la clé qui a été utilisée pour chiffrer le ticket.


Que sont les alias SPN ?

Si vous avez déjà géré un domaine Active Directory, vous avez sans doute remarqué que chaque ordinateur dispose de quelques SPN par défaut, notamment des SPN avec le HOST classe de service (HOST/computer). Pour ceux qui ne les connaissent pas, ces SPN peuvent prêter à confusion.

La partition de configuration d'Active Directory contient un attribut nommé sPNMappings. Cet attribut permet de mapper des SPN à ce qu'on appelle des Alias SPN. Par défaut, cet attribut contient une seule valeur, qui correspond à la HOST alias vers les services suivants :

alerter, appmgmt, cisvc, clipsrv, browser, dhcp, dnscache, replicator, eventlog, eventsystem, policyagent, oakley, dmserver, dns, mcsvc, fax, msiserver, ias, messenger, netlogon, netman, netdde, netddedsm, nmagent, plugplay, protectedstorage, rasman, rpclocator, rpc, rpcss, remoteaccess, rsvp, samss, scardsvr, scesrv, seclogon, scm, dcom, cifs, spooler, snmp, schedule, tapisrv, trksvr, trkwks, ups, time, wins, www, http, w3svc, iisadmin, msdtc

Lorsqu'il est ajouté au domaine, chaque compte d'ordinateur Active Directory se voit attribuer un HOST classe SPN. Lorsque les utilisateurs tentent d'accéder à un service associé à HOST (par exemple, cifs, http), le KDC chiffre le ticket de service à l'aide de la clé associée au compte correspondant HOST SPN.


Un cas limite intéressant

La vérification de l'unicité des alias SPN, évoquée précédemment, empêche la création d'alias SPN mappés en conflit. Par exemple, si la forêt comporte un ordinateur nommé Server, qui comporte le HOST/Server SPN, attribution de la cifs/Server Les connexions SPN vers un autre serveur seront bloquées, même si le cifs/Server SPN n'existe pas explicitement.

J'ai déjà mentionné que la vulnérabilité découverte permet de contourner ce contrôle d'unicité. Mais ce qui est intéressant, c'est que l'algorithme de recherche de SPN recherche toujours en premier lieu un SPN explicite. Ce n'est que lorsqu'aucun SPN explicite n'est trouvé que l'algorithme recherche l'alias associé au SPN.

Grâce à cette introduction (assez longue) aux SPN, vous disposez désormais des connaissances nécessaires pour comprendre comment cette vulnérabilité peut être exploitée.


Démontrer l'impact

Je vais vous présenter plusieurs scénarios. Pour cela, je vais utiliser un environnement simple, composé de trois serveurs membres d'un domaine :

  • Serveur A
  • Serveur B
  • ServerC

L'attaquant exécute ses actions en tant qu'utilisateur sans privilèges nommé NotAdmin.


Scénario n° 1 : Déni de service visant les services associés à un hôte

Pour notre premier scénario, supposons que ServerB héberge un partage de fichiers SMB important. Les utilisateurs qui accèdent à ce partage demandent des tickets de service pour cifs/SERVERB. Parce que ServerB a le HOST/SERVERB Par défaut, le DC identifie ce SPN comme le compte détenant la clé de chiffrement correspondante pour le service cible. Si NotAdmin dispose de l'autorisation WriteSPN sur ServerC, ils ne peuvent pas ajouter le cifs/SERVERB Ajouter un SPN, car la vérification de l'unicité des alias SPN empêche cette action (Figure 15).

Les utilisateurs qui ne sont pas administrateurs de domaine ne peuvent pas configurer d'alias SPN entrant en conflit
Figure 15. Les utilisateurs qui ne sont pas administrateurs de domaine ne peuvent pas configurer d'alias SPN conflictuels

Cependant, en utilisant un caractère invisible et non filtrable et en l'insérant dans la chaîne de caractères, NotAdmin parvient à ajouter le SPN source de conflit (figure 16).

L'utilisation de caractères non filtrables permet de contourner la vérification de l'unicité des alias SPN.
Figure 16. L'utilisation de caractères non filtrables permet de contourner la vérification de l'unicité des alias SPN.

Comme on pouvait s'y attendre, le caractère n'est pas totalement invisible dans la console PowerShell. En revanche, il est invisible dans dsa.msc (Figure 17).

dsa.msc affiche un SPN qui semble légitime
Figure 17. Le fichier dsa.msc affiche un SPN qui semble légitime

Et, surtout, lorsqu'un utilisateur interroge le LDAP pour obtenir le cifs/SERVERB SPN, ServerC est l'objet qui est renvoyé (Figure 18).

Le serveur LDAP ne prend pas en compte les caractères non filtrables lors de l'interrogation des SPN
Serveur 18. Le serveur LDAP ne prend pas en compte les caractères non filtrables lors de l'interrogation des SPN

Dans ce scénario, en raison de la priorité explicite des SPN que j'ai expliquée précédemment, lorsqu'un utilisateur du domaine tente d'accéder à ServerB via SMB, le KDC chiffrera le ticket à l'aide de ServerCla clé de. Lorsque l'utilisateur tente d'utiliser ce ticket, ServerB ne peut pas le déchiffrer, ce qui entraîne un KRB_AP_ERR_MODIFIED erreur. Pour l'utilisateur lambda, cela peut se traduire par plusieurs messages différents et ambigus :

  • Le nom de réseau indiqué n'est plus disponible.
  • Le nom du compte de destination est incorrect.
  • Impossible de trouver le chemin « path » car il n'existe pas.

La suppression du faux SPN rétablit immédiatement un accès normal (figure 19).

Le SPN malveillant bloque l'accès au protocole SMB ; une fois ce SPN supprimé, l'accès est immédiatement rétabli
Figure 19. Le SPN malveillant bloque l'accès au protocole SMB ; lorsque l'on supprime ce SPN, l'accès est immédiatement rétabli

Disposer de l'autorisation WriteSPN sur tous un compte informatique ou utilisateur au sein de la forêt a permis de mener une attaque par déni de service (DoS) contre tous HOST- service mappé dans la forêt, en utilisant des caractères non filtrables. Figure 20 illustre ce scénario.

Déroulement d'une attaque par déni de service (DoS) sur des services arbitraires mappés à un hôte
Figure 20. Déroulement d'une attaque par déni de service (DoS) sur une cible arbitraire HOST- services mappés


Scénario n° 2 : détournement de SPN

Une autre faille intéressante concerne les attaques par délégation restreinte via Kerberos, car la délégation restreinte classique est configurée à l'aide de SPN. Pour illustrer cela, j'ai adapté un scénario tiré du blog d'Elad Shamir consacré au « SPN-jacking ».

Remarque : ce scénario suppose une bonne compréhension des attaques par délégation Kerberos (c'est-à-dire « l'attaque S4U complète »). L'explication du principe de délégation Kerberos dépasse le cadre du présent document.

Dans son article, Shamir a présenté le scénario suivant, dans lequel un pirate disposant d'un accès administrateur à ServerA souhaite obtenir des droits d'administrateur sur ServerC. ServerA est configuré pour la délégation restreinte vers cifs/ServerB, et l'attaquant dispose des droits WriteSPN sur ServerB et ServerC (Figure 21).

Le montage expérimental décrit dans l'article de Shamir
Figure 21. Le montage expérimental décrit dans l'article de Shamir

Shamir a proposé de gérer la vérification de l'unicité des alias SPN en utilisant WriteSPN sur les deux ServerB et ServerC, en supprimant temporairement le HOST/SERVERB SPN de ServerB, puis d'ajouter seulement à ce moment-là cifs/SERVERB à ServerC. À ce stade, l'attaquant peut lancer l'attaque S4U complète en utilisant ServerAle compte de pour obtenir un ticket de service destiné à un utilisateur privilégié afin de ServerC, avant d'annuler les modifications.

En utilisant KerberLoss, associé à une priorité SPN explicite, un attaquant peut contourner la nécessité d'un WriteSPN vers le service intermédiaire. Pour illustrer cela, j'ai configuré mon environnement de test comme le montre la figure 22.

Configuration de mon environnement de test ; notez que l'utilisateur NotAdmin ne dispose pas du privilège WriteSPN sur ServerB
Figure 22. La configuration de mon laboratoire ; notez que NotAdmin ne dispose pas du privilège WriteSPN sur ServerB

Comme dans l'exemple précédent de déni de service, l'attaquant peut utiliser des caractères non filtrables pour provoquer ServerC le cifs/SERVERB SPN, en contournant le contrôle d'unicité et en créant une situation où les billets vers cifs/SERVERB sera chiffré avec ServerCla clé de (Figure 23).

Une fois encore, la recherche du compte associé à cifs/SERVERB renvoie ServerC
Figure 23. Une fois encore, recherchons le compte qui contient cifs/SERVERB rendements ServerC

Désormais, l'attaquant peut exécuter l'intégralité de la chaîne d'attaque S4U afin d'obtenir un ticket privilégié pour cifs/SERVERB (Figure 24).

Exécution de l'attaque S4U complète pour obtenir un ticket d'administrateur pour cifs/ServerB
Figure 24. Exécution de l'attaque S4U complète pour obtenir un ticket d'administrateur pour cifs/ServerB

J'ai réussi à obtenir un ticket de service pour un utilisateur administrateur. Je vais maintenant vérifier si le ticket a bien été chiffré à l'aide de ServerCest la clé. Comme le SPN se trouve dans la partie non chiffrée du ticket, je peux le modifier pour qu’il devienne cifs/ServerC et accéder au serveur. L'accès ne fonctionnera que si le ticket est chiffré avec ServerCla clé de…, pas ServerB» (Figure 25).

Compromission réussie de ServerC
Figure 25. Compromission réussie ServerC


Scénario n° 3 : Réduction du niveau d'authentification

Nous connaissons désormais les conséquences de la création de SPN mappés conflictuels. Mais qu'en est-il des SPN explicites en double ? Rappelons-nous notre capacité à provoquer un déni de service (DoS) via le mappage HOST.

En cas de conflits entre les alias SPN, lorsqu’un ticket de service est demandé, un SPN est identifié et un ticket de service est créé. Du point de vue du contrôleur de domaine, Kerberos a fonctionné ; l’effet DoS se produit parce que la ressource elle-même impossible de déchiffrer le ticket, ce qui entraîne une KRB_AP_ERR_MODIFIED erreur. Cependant, si l'on crée un double exact d'un SPN, le résultat est différent.

En cas de doublons explicites de SPN, lorsqu’un ticket de service est demandé, le contrôleur de domaine identifie deux comptes associés à ce SPN. Dans ce cas, le contrôleur de domaine ne peut pas « choisir » Quelle clé faut-il utiliser pour le billet ? et renvoie donc un KDC_ERR_S_PRINCIPAL_UNKNOWN erreur. Cette fois-ci, le contrôleur de domaine renvoie une erreur indiquant que l'authentification Kerberos a échoué, ce qui oblige le client à se rabattre sur le protocole NTLM.

Cela signifie qu'en disposant de l'autorisation WriteSPN sur n'importe quel ordinateur ou compte utilisateur de la forêt, outre la possibilité de provoquer un déni de service (DoS) complet sur n'importe quel service mappé à un hôte (HOST) de la forêt, vous pourriez forcer n'importe quel service de la forêt, qu'il soit mappé à un hôte ou non, à utiliser exclusivement le protocole NTLM (à moins que celui-ci ne soit désactivé, ce qui entraînerait un déni de service).

Du point de vue de l'utilisateur, l'accès semble fonctionner normalement (figure 26, figure 27).

Même après avoir configuré un SPN HOST en double, l'accès fonctionne toujours
Figure 26. Après avoir défini un doublon HOST SPN : l'accès en lui-même fonctionne toujours
Wireshark indique que le serveur DC renvoie l'erreur KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, ce qui entraîne le passage au protocole NTLM.
Figure 27. Wireshark montre que le serveur DC renvoie l'erreur « KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN », ce qui entraîne le recours au protocole NTLM.

La figure 28 illustre ce scénario.

Procédure pour forcer des services arbitraires à basculer de Kerberos vers NTLM
Figure 28. Procédure permettant de forcer des services arbitraires à basculer de Kerberos vers NTLM


ResetNightmare : Et les UPN ?

À la recherche d'une méthode permettant d'élever directement les privilèges, je me suis tourné vers les noms principaux d'utilisateur (UPN). Étant donné que la vérification de l'unicité des UPN est contrôlée par le même mécanisme que celle des SPN, j'ai supposé à juste titre qu'il était possible de la contourner de la même manière (figure 29).

Duplication de l'UPN de DemoAdmin1 à l'aide de caractères non filtrables
Figure 29. Duplication de l'UPN de DemoAdmin1 à l'aide de caractères non filtrables

La vérification de l’unicité a été corrigée, tout comme une série d’autres vulnérabilités découvertes par Andrew Bartlett, notamment l’attaque « Dollar Ticket/noPac » (CVE-2021-42287 + CVE-2021-42278), qui permettait à n’importe quel utilisateur d’obtenir instantanément des privilèges d’administrateur de domaine. Comme cette attaque impliquait une confusion dans la dénomination des contrôleurs de domaine, j’espérais que KerberLoss permettrait de faire resurgir cette vulnérabilité ou d’en découvrir une similaire. Pour cela, ainsi que pour toutes mes autres idées, je devais comprendre si, et dans l’affirmative, comment Kerberos utilise les UPN.


Types de noms Kerberos

Dans Kerberos, un identifiant de principal est composé d'un Realm et un PrincipalName. A PrincipalName est structuré comme suit :

PrincipalName   ::= SEQUENCE {
	   name-type       [0] Int32,
	   name-string     [1] SEQUENCE OF KerberosString
}


Les name-string Le champ spécifie le nom sous la forme d'une chaîne Kerberos. Cependant, le name-string à lui seul ne suffit pas pour identifier un mandant. Le name-type Le champ « field » précise le type du nom, ce qui, en termes simples, signifie quel attribut On examine d'abord ce champ pour identifier le principal. Le protocole Kerberos ([RFC4120], section 6.2) définit plusieurs valeurs possibles pour ce champ.

Active Directory utilise généralement le NT-PRINCIPAL type de nom permettant d'identifier les clients Kerberos, et il correspond au compte SamAccountName. Lors de l'envoi de requêtes de ticket d'octroi de ticket (TGT) Kerberos, il est également possible d'utiliser le NT-ENTERPRISE nom du type, qui permet de localiser les comptes en fonction de leur UserPrincipalName (UPN). Le nom réel du client figurant dans le ticket généré peut être le SamAccountName (NT-PRINCIPAL) ou l'UPN (NT-ENTERPRISE), et cela est généralement géré par le Name-canonicalize drapeau.

Heureusement, les outils Kerberos courants tels que Rubeus et Impacket permettent déjà de spécifier le type de nom à utiliser dans la requête TGT ; j'ai donc pu demander des tickets avec le NT-ENTERPRISE nom-type.

Cependant, lorsque j'ai essayé de demander des tickets avec mon UPN dupliqué, le contrôleur de domaine a tout de même tenté de m'authentifier en tant que cible privilégiée, ce qui a provoqué une erreur « Échec de la pré-authentification » (Figure 30).

La demande d'un TGT avec un UPN déjà utilisé échoue
Figure 30. Échec de la demande d'un TGT avec un UPN déjà utilisé

Cette méthode a fonctionné lors de la suppression de l'UPN de DemoAdmin1 , mais elle nécessite de disposer de droits d'écriture sur la cible, ce qui n'est pas réaliste dans le cadre d'une élévation de privilèges (figure 31).

L'obtention d'un ticket pour l'UPN ne fonctionne que si l'UPN d'origine est supprimé de l'utilisateur cible.
Figure 31. L'obtention d'un ticket pour l'UPN ne fonctionne que si l'UPN d'origine est supprimé de l'utilisateur cible

Peu après, j'ai trouvé une solution à ce problème : au lieu de contourner la vérification de l'unicité de l'UPN à l'aide de caractères non filtrables, je peux simplement définir mon UPN sur le SamAccountName de ma cible. Cela est autorisé, car les chaînes ne sont pas identiques (Figure 32).

Définition de l'UPN de l'utilisateur sans privilèges sur le SamAccountName de l'administrateur du domaine
Figure 32. Définition de l'UPN de l'utilisateur sans privilèges sur celui de l'administrateur de domaine SamAccountName

À présent, toute tentative de création d'un ticket pour DemoAdmin1 à l'aide du mot de passe de l'utilisateur UPNUser échouera, comme prévu. Mais en modifiant le type de nom pour NT-ENTERPRISE, je peux obtenir un billet au nom de DemoAdmin1 (Figure 33).

En modifiant le type de nom, on peut obtenir un ticket portant le nom de l'administrateur du domaine.
Figure 33. En modifiant le type de nom, on peut obtenir un ticket portant le nom de l'administrateur du domaine.

Cette méthode m'a essentiellement permis d'obtenir un ticket au nom de l'utilisateur de mon choix. Grâce à cela, j'ai tenté de trouver une autre faille de confusion, en utilisant diverses méthodes, notamment :

  • AS-REQ standard vers TGS-REQ
  • Déroulement de l'attaque « Dollar Ticket » modifiée
  • S4U2 : Automutilation
  • Abus du protocole Kerberos U2U
  • Attaque par déni de service (DoS) liée à la connexion d'un utilisateur, comme l'a mentionné Andrew Bartlett
  • Activer la synchronisation SyncJacking en mode « hard » ou « soft »
  • Ordre de priorité des UPN entre domaines

Cependant, toutes mes tentatives d'exploitation des UPN pour obtenir une élévation de privilèges ont échoué. Selon la méthode et l'outil utilisés, j'obtenais soit des erreurs, soit un ticket attribué à l'utilisateur correct, dépourvu de privilèges. J'ai analysé ce comportement pour en comprendre la raison.


Le correctif CVE-2021-42287

Comme mentionné précédemment, la vulnérabilité « Dollar Ticket » créait une confusion au niveau des noms, similaire à ce que j'essayais d'obtenir ici. Microsoft a corrigé cette vulnérabilité en ajoutant deux fonctionnalités importantes à Kerberos :

  • Les TGT renvoyés comprendront toujours un certificat d'attribut privilégié (PAC), même si le client a demandé un ticket sans PAC.
  • Le PAC des TGT comprend désormais un champ nommé PAC_REQUESTOR_SID, qui contient le SID du client ayant demandé le ticket.

Ceci PAC_REQUESTOR_SID valeur doit puis être validées au cours de la TGS Exchange, rendant ainsi caduc l'ancien vecteur d'attaque, dans lequel l'attaquant supprimait le compte ayant demandé le TGT, ce qui amenait le contrôleur de domaine à croire que le TGT appartenait en réalité au contrôleur de domaine portant un nom similaire.

Ce (très bon) correctif avait pour effet secondaire, quelque peu gênant, de rendre caduques toutes mes tentatives d’élévation de privilèges par confusion d’UPN. Le PAC contenait désormais systématiquement mon SID d’origine, et le contrôleur de domaine ignorait complètement le nom du client figurant dans mon ticket, se fiant uniquement au SID comme référence absolue du ticket.

Alors que j'étais sur le point d'abandonner l'idée d'une élévation de privilèges à l'aide des UPN, j'ai décidé de passer en revue une dernière fois ma liste d'idées d'exploits, afin de vérifier si chacune d'entre elles pouvait fonctionner. C'est alors que je suis tombé sur quelque chose d'intéressant.


Percée : (Ab)us du protocole Kerberos de changement de mot de passe

Par défaut, chaque utilisateur Active Directory peut modifier son propre mot de passe. Pour ce faire, il est notamment possible d’utiliser le protocole Kerberos « Change Password and Set Password » de Microsoft, qui définit la manière dont les changements de mot de passe sont effectués via Kerberos. Ce protocole est extrêmement simple (la spécification RFC ne compte que 7 pages) et ne comporte qu’un seul message de requête et un seul message de réponse.

Microsoft utilise les expressions « modifier le mot de passe » et « définir le mot de passe » pour distinguer, respectivement, le cas où un utilisateur modifie son propre mot de passe et celui où un administrateur définit le mot de passe d'un utilisateur.

Le protocole reçoit des messages sur le port 464 (kpasswd), avec la structure de requête illustrée à la figure 34.

Message de demande de modification du mot de passe Kerberos
Figure 34 : Message de demande de modification du mot de passe Kerberos

Les deux éléments principaux de ce message sont le message KRB-PRIV et la structure AP-REQ.

Le message KRB-PRIV n'est, pour l'essentiel, qu'un moyen d'envoyer des données chiffrées. Il permet également d'y inclure des données personnalisées. Le protocole tire parti de cette fonctionnalité en plaçant la valeur du nouveau mot de passe au sein de cette structure (figure 35).

Utilisation de KRB-PRIV dans le protocole Kerberos de modification du mot de passe
Figure 35. Utilisation de KRB-PRIV dans le protocole Kerberos de modification du mot de passe

Le point le plus intéressant réside dans la structure AP-REQ. Cette structure contient un ticket et un authentificateur, qui prouve la possession légitime du ticket, puisqu’il est chiffré à l’aide de sa clé de session (figure 36).

La structure AP-REQ, qui contient un ticket et un authentificateur
Figure 36. La structure AP-REQ, contenant un ticket et un authentificateur

Dans la plupart des cas avec Kerberos, une requête TGS_REQ/TGS_REP donne lieu à l'émission d'un ticket de service (et de la clé de session correspondante). Cette réponse est ensuite utilisée pour construire le message AP_REQ, qui est envoyé au service afin de prouver la possession du ticket.

Dans le cas du protocole de modification de mot de passe Kerberos, le ticket de la structure AP-REQ doit être limité au kadmin/changepw SPN. Cependant, il s'agit d'un SPN appartenant au compte krbtgt, et nous savons que seule la clé de chiffrement importe, donc un billet pour kadmin/changepw Il s'agit simplement d'un TGT dont le SPN (sname) a été modifié..

Cela signifie que pour modifier le mot de passe d'un utilisateur, il suffit de disposer de son TGT. Cela signifie également que le processus de modification du mot de passe d'un utilisateur via Kerberos est celui illustré à la figure 37.

Déroulement d'une opération de modification de mot de passe Kerberos
Figure 37. Déroulement d'une opération de modification de mot de passe Kerberos

Ce qui est intéressant, c'est que ce flux passe directement d'une requête TGT-REQ à une requête AP-REQ, sans TGS-REQ entre les deux. Rappelez-vous le patch que nous avons examiné précédemment : Le TGS-REQ est l'endroit où le PAC_REQUESTOR_SID la validation a lieu.

Étant donné que je pouvais déjà demander un TGT avec n'importe quel nom d'utilisateur (en utilisant la commande NT-ENTERPRISE nom type), si le PAC_REQUESTOR_SID Si ce correctif n'a pas été mis en œuvre ici, le protocole pourrait alors présenter une vulnérabilité.

En mettant à profit toutes les connaissances acquises lors de mes tests précédents, je me suis mis au travail pour tenter l'expérience. Sachant que, par défaut, chaque utilisateur d'Active Directory dispose des autorisations nécessaires pour modifier son propre mot de passe, mon idée d'exploitation était la suivante :

  1. Un pirate a réussi à obtenir les identifiants d'un utilisateur nommé UPNUser, qui ne dispose d'aucune autorisation particulière hormis la possibilité de modifier sa propre valeur UPN.
  2. L'attaquant définit l'UPN de l'utilisateur sur le SamAccountName du compte visé ; par exemple, DemoAdmin1.
    Cette action ne nécessite pas de contourner les contrôles de vérification de l'unicité de l'UPN. DemoAdmin1L'UPN réel devrait être DemoAdmin1@demo.lab ; il faut donc définir UPNUserUPN de… pour simplement DemoAdmin1 est autorisé (Figure 38).
Définition de la valeur UPN de l'utilisateur UPNUser sur le SamAccountName de la cible
Figure 38. Définition de la valeur UPN de l'utilisateur UPNUser sur le SamAccountName de la cible
  1. L'attaquant demande un TGT pour kadmin/changepw en précisant DemoAdmin1 comme nom d'utilisateur, NT-ENTERPRISE comme type de nom, et Mot de passe de l'utilisateur UPN.
  2. Le DC renvoie un TGT_REP contenant un TGT pour UPNUser (comme indiqué par PAC_REQUESTOR_SID dans le PAC), mais avec le nom d'utilisateur DemoAdmin1(NT_ENTERPRISE), comme Figure 39 montre.
Un TGT pour DemoAdmin1, de type NT-ENTERPRISE, est renvoyé
Figure 39. Un TGT pour DemoAdmin1 avec le NT-ENTERPRISE Le type « nom » est renvoyé
  1. L'utilisation de ce ticket pour émettre une demande de changement de mot de passe réinitialisera le mot de passe de l'utilisateur UPNUser. Pour étendre ses privilèges, l'attaquant modifie ou efface la valeur UPN de l'utilisateur UPNUser, de sorte qu'aucun utilisateur ne dispose plus de l'UPN figurant sur le ticket.
  2. Toute tentative d'utilisation de ce ticket pour une requête TGS_REQ après la modification de l'UPN entraînera une KDC_ERR_TGT_REVOKED erreur, due à la PAC_REQUESTOR_SID correctif empêchant l'usurpation d'identité. Cependant, en utilisant ce ticket pour créer la demande de changement de mot de passe, le changement de mot de passe fonctionne.
  3. Désormais, l'attaquant peut demander un nouveau TGT pour DemoAdmin1, sans précisant le NT-ENTERPRISE type de nom. La requête fonctionne désormais et le type de nom du ticket est NT-PRINCIPAL, ce qui indique que le billet appartient au véritable (SamAccountName) DemoAdmin1 utilisateur (Figure 40).
Un TGT pour l'utilisateur « Domain Admin » réel
Figure 40. Un TGT pour l'utilisateur « Domain Admin » réel

C'est réussi ! Le simple fait de pouvoir créer un UPN nous a permis de compromettre le domaine.

J'ai baptisé cette vulnérabilité « ResetNightmare »; elle porte le numéro CVE-2026-27912. Elle permet à un attaquant disposant de droits d'écriture génériques sur n'importe quel objet utilisateur ou ordinateur du domaine, ou capable de créer des objets utilisateur ou ordinateur au sein de celui-ci (à l'exception de MachineAccountQuota), de prendre le contrôle total du domaine.

La figure 41 illustre le déroulement d'une exploitation abusive de ResetNightmare.

Déroulement de l'attaque « ResetNightmare »
Figure 41. Déroulement de l'attaque « ResetNightmare »

La seule autre condition est que le mot de passe de l'utilisateur cible doive avoir une ancienneté suffisante. Cependant, la durée minimale par défaut d'un mot de passe dans Active Directory étant d'un jour, il est très probable que le mot de passe de la cible réponde à cette exigence.

En prime (grâce à Andrea Pierini), cette vulnérabilité peut également être combinée avec la technique des « Shadow Credentials », ce qui permet de mettre en place un vecteur d'attaque plus furtif en exploitant des comptes informatiques disposant de droits d'écriture.

J'ai également créé un outil communautaire open source, ResetNightmare, qui met en œuvre l'intégralité du processus d'attaque ResetNightmare, y compris différents paramètres permettant de personnaliser son exécution. Cet outil est écrit en PowerShell et utilise Rubeus.exe ainsi que le module ActiveDirectory de PowerShell (figure 42). Vous pouvez le trouver sur GitHub.

Exécution du script ResetNightmare.ps1
Figure 42. Exécution du script ResetNightmare.ps1


Détection et protection contre KerberLoss et ResetNightmare

Les clients de Semperis Directory Services Protector (DSP) peuvent utiliser les nouveaux indicateurs de sécurité suivants pour détecter plusieurs erreurs de configuration mentionnées dans cet article :

  • La vérification de l'unicité de l'UPN ou du SPN est désactivée
  • Objets contenant des caractères Unicode masqués
  • Objets suspects en double utilisant des caractères Unicode cachés
  • Entité principale sans privilèges capable de définir un nom d'entité de service
  • Entité principale sans privilèges capable de définir un nom d'entité utilisateur

De plus, les clients d'DSP peuvent détecter les modifications anormales des identifiants SPN et UPN grâce à deux indicateurs de compromission (IoC) :

  • Un nom principal de service (SPN) en conflit a été ajouté (CVE-2026-25177)
  • Un nom principal d'utilisateur (User Principal Name) correspondant au nom de compte SAM d'un autre compte a été ajouté (CVE-2026-27912)

En l'absence d'DSP, le meilleur moyen de détecter l'utilisation abusive de ces deux techniques consiste à configurer les SACL pour qu'elles enregistrent les modifications apportées aux objets Active Directory. Une fois les SACL configurées, l'ID d'événement 5136 du journal de sécurité (« Un objet du service d'annuaire a été modifié ») sur les contrôleurs de domaine permet de détecter les modifications susceptibles d'entraîner l'exploitation de cette vulnérabilité.

Dans KerberLoss, l'entrée correspondant à l'ID d'événement 5136 indiquera l'ajout d'un ServicePrincipalName entrant en conflit avec un nom existant (figure 43).

Une entrée du journal de sécurité indiquant l'ajout d'une valeur SPN en conflit
Figure 43. Entrée du journal de sécurité indiquant l'ajout d'une valeur SPN en conflit

Pour ResetNightmare, l'entrée d'ID d'événement 5136 indiquera l'ajout d'un UserPrincipalName correspondant à un SamAccountName (Figure 44).

Une entrée du journal de sécurité indiquant l'ajout d'une valeur UPN qui imite un SamAccountName existant
Figure 44. Entrée du journal de sécurité indiquant l'ajout d'une valeur UPN imitant un SamAccountName existant

La meilleure mesure de prévention contre ces vulnérabilités consiste à appliquer les correctifs sur tous les contrôleurs de domaine. Microsoft a publié un correctif pour KerberLoss (CVE-2026-25177) en mars 2026 et pour ResetNightmare (CVE-2026-27912) en avril 2026.

Outre l'application des correctifs, les organisations doivent respecter le principe du « privilège minimal » et surveiller toute attribution anormale d'autorisations non prédéfinies. Des autorisations plus restrictives peuvent rendre ces vulnérabilités plus difficiles à exploiter.


Calendrier de divulgation

  • 26 novembre 2025 : la faille « KerberLoss » est découverte et signalée au MSRC.
  • 17 décembre 2025 : la vulnérabilité « ResetNightmare » est découverte et signalée au MSRC.
  • 9 janvier 2026 : le MSRC confirme que ResetNightmare fonctionne comme indiqué.
  • 17 janvier 2026 : le MSRC confirme que KerberLoss fonctionne comme indiqué.
  • 10 mars 2026 : Microsoft publie un correctif pour KerberLoss (CVE-2026-25177) lors du « Patch Tuesday », en le classant comme une vulnérabilité « importante » liée à l'élévation de privilèges.
  • 14 avril 2026 : Microsoft corrige la vulnérabilité « ResetNightmare » (CVE-2026-27912), classée comme une vulnérabilité « importante » liée à l'élévation de privilèges.


Remerciements

Nous tenons à remercier tout particulièrement les chercheurs suivants, qui ont grandement inspiré divers aspects de cette recherche :

  • Yossi Sassi (@Yossi_Sassi)
  • Andrew Bartlett
  • Elad Shamir (@elad_shamir)
  • Charlie Clark (@exploitph)
  • Will Schroeder (@harmj0y)
  • Andrea Pierini (@decoder_it)
  • Benjamin Delpy (@gentilkiwi)


Plus de ressources


Clause de non-responsabilité

Ce contenu est fourni à des fins éducatives et informatives uniquement. Il vise à promouvoir la prise de conscience et la remédiation responsable des vulnérabilités de sécurité qui peuvent exister sur les systèmes que vous possédez ou que vous êtes autorisé à tester. L'utilisation non autorisée de ces informations à des fins malveillantes, d'exploitation ou d'accès illégal est strictement interdite. Semperis n'approuve ni ne tolère aucune activité illégale et décline toute responsabilité découlant d'une mauvaise utilisation du matériel. En outre, la Semperis ne garantit pas l'exactitude ou l'exhaustivité du contenu et n'assume aucune responsabilité pour les dommages résultant de son utilisation.