Aller au contenu principal

ESC17 - Au-delà de WSUS : interception RDP et contournement de NLA

Publié le 18 min de lecture

Une série de recherche par Alexis MARTIN (qu35t) et Alexis PARET (nnino). Chaque technique présentée dans cette série a été reproduite de bout en bout dans un environnement de laboratoire entièrement à jour.

Les volets précédents ont appliqué la primitive ESC17 au HTTPS interne et au LDAP. Le principe reste le même : ESC17 fabrique une identité de serveur TLS approuvée à l’échelle du domaine, pour n’importe quel nom d’hôte. Ce volet l’applique au RDP, le protocole d’administration à distance par excellence, celui que les administrateurs utilisent tous les jours pour se connecter aux serveurs avec des comptes souvent privilégiés.

Le RDP est une cible particulière, car le certificat de confiance y change tout. L’interception du RDP en homme du milieu existe depuis longtemps, mais elle se heurte d’ordinaire à un obstacle simple : le client mstsc affiche un avertissement de certificat, et un administrateur averti refuse la connexion. ESC17 supprime cet obstacle. Le certificat présenté étant réellement émis par la CA du domaine, aucune alerte n’est levée, et l’interception devient transparente.

Un mot sur la sécurité RDP

Pour comprendre ce qui est exploitable, il faut savoir comment une session RDP négocie sa sécurité. Au tout début de la connexion, dans l’échange X.224, le client annonce les couches de sécurité qu’il accepte. Trois options coexistent.

  • Standard RDP Security. Le chiffrement historique, à base de RC4. L’authentification et la session sont chiffrées avec une clé dérivée d’un aléa serveur. Le Client Info PDU, qui porte les identifiants, est chiffré en RC4.
  • TLS (SSL). La session RDP est encapsulée dans un tunnel TLS. Le serveur présente un certificat, et c’est là qu’ESC17 intervient. Une fois le TLS établi, le Client Info PDU circule en clair à l’intérieur du tunnel.
  • CredSSP (HYBRID), c’est-à-dire NLA. La Network Level Authentication authentifie l’utilisateur avant d’ouvrir la session graphique, via le protocole CredSSP. Point crucial, CredSSP lie l’authentification à la clé publique du certificat serveur (le champ pubKeyAuth), précisément pour détecter un homme du milieu.

Le déroulé d’une connexion normale enchaîne ces éléments ainsi :

Négociation RDP normale : X.224, handshake TLS avec certificat serveur, puis Client Info PDU

Ce sont ces trois modes, et la façon dont ils sont négociés, qui décident de ce qu’un attaquant peut extraire.

Le certificat qui ne lève plus d’alerte

C’est l’apport direct d’ESC17 sur le RDP. Un outil d’interception RDP classique doit présenter un certificat qu’il ne contrôle pas, donc un certificat auto-signé ou émis par une autorité inconnue du client. mstsc affiche alors un avertissement clair, et l’administrateur prudent s’arrête là.

Avec ESC17, on forge un certificat pour le nom exact du serveur RDP, émis par la CA d’entreprise. Le client le valide comme il validerait le vrai, sans le moindre avertissement. L’utilisateur voit sa fenêtre RDP habituelle et saisit ses identifiants en toute confiance.

Comme pour le HTTPS, le certificat doit porter le bon nom. adecrypt le génère par nom d’hôte, à partir du FQDN du serveur (résolu par PTR ou fourni via --rdp-fqdn), et supporte aussi un certificat wildcard *.domaine pour couvrir plusieurs serveurs en une seule émission, avec le même bénéfice de discrétion que celui décrit dans le volet HTTPS.

Le downgrade de NLA, et pourquoi il ne passe plus

L’approche historique contre NLA est le downgrade. Dans l’échange X.224 qui ouvre la connexion, le client annonce les couches qu’il accepte, en général SSL et HYBRID (CredSSP). adecrypt réécrit cette annonce pour ne garder que SSL et retirer HYBRID. Si la bascule aboutit, l’authentification se fait dans la session, et le Client Info PDU, qui porte le mot de passe, circule en clair dans le tunnel TLS que nous possédons.

Downgrade de NLA : adecrypt retire HYBRID de la négociation X.224, force SSL seul et capture le Client Info PDU en clair

En théorie, l’approche est directe. En pratique, sur un parc à jour, elle échoue, et le point important est que le blocage vient du client, pas du serveur.

Prenons le cas le plus favorable à l’attaquant, un serveur dont NLA est désactivé.

Terminal
nxc rdp 10.10.20.11
RDP 10.10.20.11 3389 WK01 [*] Windows 10 / Server 2016 Build 19041 (name:WK01) (domain:esc17.local) (nla:False)
Terminal
[RDP] 10.10.20.12 : client requests protocols=0x0B (SSL=1 HYBRID=1 HYBRID_EX=1)
[RDP] 10.10.20.12 : server -> SSL accepted ; TLS handshake with client (cert ESC17)
[RDP] 10.10.20.12 : TLS established (client=TLSv1.2, server=TLSv1.2) - capturing Client Info PDU

Le serveur accepte le SSL, le TLS s’établit avec notre certificat, tout semble en place pour la capture. Et pourtant aucun Client Info PDU n’arrive, la connexion s’interrompt, et la victime reçoit une erreur d’authentification.

Erreur mstsc, an authentication error has occurred (Code: 0x609)

La cause est entièrement côté client. Un mstsc moderne a proposé CredSSP (HYBRID) et refuse de s’en passer. Depuis les correctifs de 2018 (CredSSP Encryption Oracle Remediation, réglé par défaut sur « Force Updated Clients »), un client à jour n’accepte plus de retomber sur une session SSL sans CredSSP, même quand le serveur l’accepterait. Il coupe la connexion avec le code 0x609 plutôt que d’envoyer ses identifiants dans une session non protégée par CredSSP. Désactiver NLA côté serveur n’y change donc rien, c’est le client qui verrouille.

Le second blocage est côté serveur. S’il impose NLA, il refuse la bascule dès la négociation.

Terminal
nxc rdp 10.10.20.11
RDP 10.10.20.11 3389 WK01 [*] Windows 10 / Server 2016 Build 19041 (name:WK01) (domain:esc17.local) (nla:True)
Terminal
[RDP] 10.10.20.12 : client requests protocols=0x0B (SSL=1 HYBRID=1 HYBRID_EX=1)
[RDP] 10.10.20.12 : downgrade refused (server enforces NLA/HYBRID, proto=5) - pubKeyAuth = wall

Le serveur renvoie un échec de négociation proto=5 (HYBRID_REQUIRED_BY_SERVER), et la victime voit une erreur explicite.

Erreur mstsc, The remote computer requires Network Level Authentication

Relayer l’authentification CredSSP plutôt que la downgrader n’aboutit pas davantage, et c’est le pendant RDP du channel binding vu dans le volet LDAP. CredSSP inclut la clé publique du certificat serveur dans son échange, le champ pubKeyAuth. Un homme du milieu qui termine le TLS puis en rouvre un autre vers le vrai serveur présente une clé publique différente de celle attendue, et la vérification échoue.

Le constat est donc défavorable à l’approche classique. Sur un client moderne à jour, le downgrade échoue quelle que soit la configuration du serveur. Il ne reste exploitable que contre des clients anciens, non corrigés, ou dont la politique EOR a été affaiblie, ainsi que contre les sessions en Standard RDP Security (RC4), dont le Client Info PDU est chiffré autrement et n’est de toute façon pas extractable tel quel.

Faut-il en conclure que NLA ferme définitivement la porte ? Non. Il reste une voie, propre à ESC17, qui ne downgrade pas mais termine CredSSP. C’est l’objet de la section suivante.

Franchir le verrou : terminer CredSSP soi-même

Le blocage précédent repose sur un raisonnement solide, mais il contient une hypothèse implicite, que l’attaquant ne peut pas produire de pubKeyAuth valide. Précisons d’abord ce que ce champ lie réellement. Contrairement à une intuition répandue, il ne lie pas la clé publique du certificat à la clé privée TLS, mais à la clé de session de l’authentification NTLM, dérivée du secret de l’utilisateur. Posséder le certificat ESC17 et sa clé privée ne suffit donc pas, un relais classique échoue toujours faute de cette clé de session.

La question devient alors, peut-on obtenir cette clé de session autrement que par le mot de passe de la victime ? Oui, en contrôlant un compte machine du domaine. Tout membre du domaine peut demander au contrôleur de domaine de valider une authentification réseau, via le canal sécurisé Netlogon, et récupère au passage la clé de session que le DC calcule. C’est le pivot de la technique que nous avons implémentée dans adecrypt.

Terminaison de CredSSP : adecrypt joue le serveur NTLM, obtient la clé de session du DC par pass-through Netlogon, forge un pubKeyAuth valide et récupère le TSPasswordCreds en clair malgré NLA imposé

Le déroulé pas à pas :

  1. Le client se connecte, adecrypt accepte NLA (il ne downgrade pas) et termine le TLS avec le certificat ESC17.
  2. adecrypt joue le rôle du serveur NTLM. Il envoie son propre challenge, puis reçoit la réponse NTLMv2 du client (message Type 3), accompagnée du pubKeyAuth et du nonce du client.
  3. Il lui manque la clé de session pour valider cet échange et répondre. Il la demande au DC, en présentant son challenge et la réponse du client dans un pass-through Netlogon authentifié par un compte machine qu’il contrôle. Le DC valide et renvoie la clé de session.
  4. adecrypt dérive la clé de session, forge un pubKeyAuth serveur valide et le renvoie au client.
  5. Le pubKeyAuth validé, le client envoie le dernier message de CredSSP, ses identifiants délégués (TSPasswordCreds), c’est-à-dire son mot de passe en clair.

Ce qu’apporte ESC17 est double. Le certificat de confiance termine le TLS sans alerte, et sa clé privée fait de l’attaquant un serveur CredSSP crédible. Le compte machine, lui, débloque la clé de session. Aucun de ces éléments pris isolément ne suffit, c’est leur combinaison qui ouvre la voie.

Un point mérite d’être souligné, car c’est ce qui sépare cette approche du downgrade. Le downgrade cherchait à faire abandonner CredSSP, il dépendait donc directement du réglage NLA : bloqué si le client refuse de s’en passer, bloqué si le serveur l’impose dès la négociation. La terminaison ne cherche jamais à s’en passer. adecrypt accepte CredSSP et le joue jusqu’au bout à la place du serveur. Que le serveur impose NLA ou l’ait désactivé ne change alors rien, car l’authentification n’atteint jamais le vrai serveur, elle se termine chez l’attaquant, et le client obtient exactement le CredSSP qu’il réclame. C’est pourquoi la même commande capture le mot de passe dans les deux cas, avec ou sans NLA côté serveur. La démonstration qui suit vise volontairement le serveur à NLA imposé, celui qui bloquait toutes les approches précédentes.

NTLM direct, ou le détour par Kerberos

Le déroulé ci-dessus correspond à un client qui parle NTLM d’emblée, ce qui est le cas quand on se connecte par adresse IP : sans nom, pas de SPN, donc pas de Kerberos. Le CredSSP transporte alors directement du NTLM et les cinq étapes s’enchaînent sans détour. C’est le cas le plus simple, et il capture déjà le mot de passe en clair.

Connexion RDP par adresse IP vers 10.10.20.11, compte ESC17\Administrator

Connexion par adresse IP : faute de SPN, le client ne peut pas faire de Kerberos et passe directement en NTLM.

Terminal
sudo python3 adecrypt.py rdp --targets 10.10.20.12 --gateway 10.10.20.1 \
--interface eth0 --target 10.10.20.11 --pfx ../wk01.pfx \
--credssp-terminate --machine-account 'pwned$' --machine-pass '...' \
--dc-ip 10.10.20.10 --dc-name DC01 --domain esc17.local
[RDP] 10.10.20.12 -> 10.10.20.11
[RDP] 10.10.20.12 : client requests protocols=0x0B (SSL=1 HYBRID=1 HYBRID_EX=1)
[RDP] 10.10.20.12 : T1 - HYBRID accepted (impersonating server), TLS with ESC17 cert
[T1] PublicKey (PKCS#1 DER) 270 bytes
[T1] CredSSP v=6 SPNEGO=False (optimistic mechToken = NTLM Type1, no downgrade)
[T1] Type2 (CHALLENGE) sent, serverChallenge=e165aea7abb71744
[T1] Type3 (AUTHENTICATE) received : ESC17\Administrator wks=WK02 KEY_EXCH=1 mechListMIC=no
[T1][NRPC] alter bind (ALTER_CONTEXT) OK, AES-sealed channel
[T1][NRPC] NetrLogonSamLogonEx (sealed) -> ESC17\Administrator
[T1][NRPC] UserSessionKey (SessionBaseKey, cleartext) = ab66d828b910951db53ca249e2daae26
[T1] pubKeyAuth client MAC_ok=True (seq=0), binding_match=True
[T1] server pubKeyAuth sent (seq 0)
[T1] authInfo received and unsealed (seq 1, MAC_ok=True)
>>> [RDP-CREDSSP] ESC17\Administrator : Password1! (host=10.10.20.12)

Le mot de passe d’administrateur ressort en clair. On remarque au passage que le pubKeyAuth et l’authInfo portent ici les numéros de séquence 0 et 1, sans le décalage d’un cran qu’introduit le mechListMIC du détour Kerberos vu plus loin.

Côté client, en revanche, la connexion n’aboutit pas : mstsc affiche « An internal error has occurred ». La terminaison de CredSSP ne nous sert qu’à récupérer les identifiants délégués, sans faire de nous un vrai serveur RDP capable d’ouvrir ensuite la session graphique. L’échange s’arrête donc juste après la délégation.

Erreur « An internal error has occurred » affichée par mstsc côté client après la capture

Côté victime, la connexion échoue après la capture : adecrypt termine CredSSP mais n’ouvre pas la session graphique.

Pour que l’utilisateur ne reste pas bloqué, adecrypt retient la source déjà capturée et bascule sa tentative suivante en relais transparent vers le vrai serveur. Celui qui reclique obtient alors une session RDP normale, sans nouvelle saisie, la capture ayant déjà eu lieu.

Terminal
[RDP] 10.10.20.12 -> 10.10.20.11
[RDP] 10.10.20.12 : client requests protocols=0x0B (SSL=1 HYBRID=1 HYBRID_EX=1)
[RDP] 10.10.20.12 : already captured -> transparent relay (normal session)

Plus généralement, la victime se connecte par nom d’hôte, et là le client tente d’abord Kerberos : il détient un ticket de service pour TERMSRV/wk01.esc17.local que le DC lui a délivré, indépendamment de qui répond réellement sur cette adresse. Kerberos ne se prête pas à notre technique, son ticket est chiffré pour la clé du compte de l’ordinateur ciblé et le pass-through Netlogon ne valide que du NTLM. Il faut donc ramener le client sur NTLM.

Connexion RDP par nom d'hôte wk01.esc17.local, compte ESC17\Administrator

Connexion par nom d’hôte : le client tente d’abord Kerberos, qu’adecrypt ramène ensuite sur NTLM dans la négociation SPNEGO.

C’est possible parce que, dans sa négociation SPNEGO, le client propose Kerberos et NTLM. adecrypt répond en sélectionnant explicitement NTLM. Sélectionner un mécanisme qui n’est pas le préféré du client déclenche, comme l’impose SPNEGO, un échange de mechListMIC destiné à protéger l’intégrité de la négociation contre un downgrade malveillant. adecrypt vérifie le mechListMIC du client puis fournit le sien, ce qu’il peut faire puisqu’il tient désormais la clé de session obtenue du DC. Une fois la négociation ramenée sur NTLM et son intégrité satisfaite, la suite est strictement identique, pubKeyAuth puis délégation des identifiants.

Le résultat, par nom d’hôte, contre le même serveur à NLA imposé :

Terminal
sudo python3 adecrypt.py rdp --targets 10.10.20.12 --gateway 10.10.20.1 \
--interface eth0 --target 10.10.20.11 --pfx ../wk01.pfx \
--credssp-terminate --machine-account 'pwned$' --machine-pass '...' \
--dc-ip 10.10.20.10 --dc-name DC01 --domain esc17.local
[RDP] 10.10.20.12 : T1 - HYBRID accepted (impersonating server), TLS with ESC17 cert
[T1] SPNEGO negTokenInit mechTypes=['Kerberos', 'Kerberos', 'NEGOEX', 'NTLM'] (optimistic mechToken = AP-REQ Kerberos)
[T1] downgrade SPNEGO Kerberos->NTLM (negTokenResp request-mic, NTLM selection)
[T1] Type1 (NEGOTIATE) obtained (downgrade=True, MIC_required=True)
[T1] Type2 (CHALLENGE) sent, serverChallenge=3dd0b140b66e2913
[T1] Type3 (AUTHENTICATE) received : ESC17\Administrator wks=WK02 KEY_EXCH=1 mechListMIC=yes
[T1][NRPC] alter bind (ALTER_CONTEXT) OK, AES-sealed channel
[T1][NRPC] NetrLogonSamLogonEx (sealed) -> ESC17\Administrator
[T1][NRPC] UserSessionKey (SessionBaseKey, cleartext) = 2fc9b4153c8bdd9881c171d41552bdad
[T1] mechListMIC client (seq 0) MAC_ok=True
[T1] SPNEGO completed (accept-completed + server mechListMIC), waiting for client pubKeyAuth
[T1] pubKeyAuth client MAC_ok=True (seq=1), binding_match=True
[T1] server pubKeyAuth sent (seq 1)
[T1] authInfo received and unsealed (seq 2, MAC_ok=True)
>>> [RDP-CREDSSP] ESC17\Administrator : Password1! (host=10.10.20.12)

Un mot de passe d’administrateur, en clair, par nom d’hôte, sur un serveur à NLA imposé et un client Windows moderne à jour, là où le downgrade et le relais échouaient tous les deux.

Ce qui est repris, ce qui est nouveau

Cette chaîne ne sort pas de nulle part, et il faut créditer ce sur quoi elle s’appuie. Le downgrade de SPNEGO vers NTLM, la terminaison de CredSSP en serveur, et l’idée de récupérer la clé de session auprès du DC avec un compte machine, ont été décrits dès 2020 par Bertoli et Bourguenolle dans leur travail CredSSPy (SSTIC 2020), qui prolongeait l’analyse RDP de l’ANSSI de 2012. La primitive « un compte machine obtient une clé de session NTLM auprès du DC » est par ailleurs connue sous le nom Your Session Key is My Session Key (CVE-2019-1019).

Deux points restaient toutefois ouverts, et c’est là l’apport de cet article. D’abord, CredSSPy nommait explicitement le blocage : un compte machine permet d’obtenir un certificat RDP signé par la CA, mais pas au nom du serveur cible, si bien que le client affiche toujours une alerte. C’est précisément ce qu’ESC17 lève, un certificat approuvé pour le FQDN exact de la cible. Ensuite, l’état de l’art public s’arrêtait à la capture d’un NetNTLMv2, à casser ou à relayer, et considérait la suite impossible contre un NLA imposé. En forgeant un pubKeyAuth valide à partir de la clé de session obtenue, on mène l’échange jusqu’à la délégation et on récupère le mot de passe en clair.

Un mot enfin sur le durcissement de CVE-2019-1019, dont le correctif lie la clé de session au compte machine qui a ouvert le canal Netlogon. Cette technique n’entre pas en conflit avec lui, car elle ne relaie pas l’authentification vers un autre serveur, elle la termine en son propre nom de compte machine. La validation par le DC reste donc cohérente, ce que confirme le test sur un DC à jour, celui-ci imposant déjà le scellement du canal Netlogon.

Ce qui met l’attaque en échec

Aussi efficace soit-elle contre un NLA imposé, cette technique repose sur deux préconditions, et elle tombe dès que l’une des deux disparaît.

D’abord, elle a besoin d’un compte machine du domaine, trivial à obtenir quand MachineAccountQuota est supérieur à zéro (sa valeur par défaut). C’est ce compte qui interroge le DC pour obtenir la clé de session. En revanche, le durcissement Netlogon (la remédiation de CVE-2020-1472) n’y fait rien, le pass-through emprunte un canal signé et scellé tout à fait normal.

Ensuite et surtout, elle a besoin que le NTLM reste utilisable et qu’un secret réutilisable soit délégué dans la session. C’est précisément ce que le détour SPNEGO va chercher en ramenant Kerberos sur NTLM. Tout ce qui coupe cette précondition referme l’attaque :

  • NTLM désactivé dans le domaine. Sans NTLM, le pass-through Netlogon n’a plus rien à valider, et le détour SPNEGO n’a plus de mécanisme sur lequel se rabattre.
  • Protected Users. Placer les comptes sensibles (a fortiori les administrateurs) dans ce groupe leur interdit purement et simplement le NTLM et bloque la délégation d’identifiants en clair par CredSSP. Un membre de Protected Users ne peut donc pas se faire capturer de cette façon.
  • Remote Credential Guard et Restricted Admin. Ce sont deux modes de connexion RDP, distincts de Protected Users, qui évitent d’envoyer un secret réutilisable vers le serveur cible. Restricted Admin ne transmet aucun identifiant (la session s’établit par une ouverture de session réseau, sans mot de passe ni délégation), et Remote Credential Guard garde les identifiants sur le poste client en redirigeant les demandes d’authentification. Dans les deux cas, il n’y a plus de TSPasswordCreds en clair à intercepter. En contrepartie, ils ouvrent d’autres surfaces (rejeu de type pass-the-hash pour Restricted Admin), ce qui reste un arbitrage à faire selon l’environnement.
  • Authentification par carte à puce, qui ne fait transiter aucun mot de passe réutilisable.

Pour situer les deux notions que l’on confond souvent : Protected Users est un groupe Active Directory qui durcit le compte lui-même (pas de NTLM, pas de délégation CredSSP en clair, Kerberos AES uniquement), tandis que Restricted Admin et Remote Credential Guard sont des modes de connexion que l’on choisit au moment d’ouvrir la session RDP. Les premiers protègent le compte partout, les seconds protègent une connexion donnée.

Au-delà du mot de passe

La capture d’identifiants n’est qu’une des possibilités qu’ouvre la position d’homme du milieu. Une fois au cœur de la session TLS d’un RDP, on ne voit pas seulement le mot de passe, on voit toute la session : l’écran transmis à la victime, ses frappes clavier, le presse-papier, et les périphériques redirigés. On peut aussi injecter des entrées dans la session, souvent celle d’un administrateur.

adecrypt se concentre sur la capture des identifiants, qui est l’objectif le plus direct et le plus réutilisable. L’observation et la manipulation complètes de la session sont une capacité de la position, pas une fonction de l’outil, mais elles méritent d’être gardées en tête dans l’évaluation du risque.

Récapitulatif

Le tableau résume les deux approches contre RDP, une fois placé en homme du milieu avec le certificat ESC17.

ApprocheCe qu’on récupèrePréconditionsRésultat
Downgrade de NLArien sur un client modernele client accepte une session SSL sans CredSSPéchoue sur client à jour (erreur 0x609) comme sur serveur à NLA imposé ; ne reste exploitable que contre des clients anciens ou non corrigés
Terminaison CredSSP (--credssp-terminate)le mot de passe en clair (TSPasswordCreds)certificat ESC17 au bon FQDN, compte machine du domaine, NTLM utilisable et secret réutilisable déléguémot de passe réutilisable, y compris contre un serveur à NLA imposé et un client moderne (par IP en NTLM direct, par nom d’hôte via downgrade SPNEGO Kerberos → NTLM)

Ce qui referme l’attaque : NTLM désactivé, comptes en Protected Users, connexions en Restricted Admin ou Remote Credential Guard, ou authentification par carte à puce, autant de cas où aucun secret réutilisable n’est délégué.

Mitigations

  • Imposer NLA des deux côtés. Exiger CredSSP côté serveur (option « N’autoriser que les ordinateurs exécutant l’authentification au niveau du réseau ») et côté client. C’est ce qui ferme le downgrade classique, mais rappelons que cela ne suffit pas contre la terminaison CredSSP, qui n’a besoin que d’un compte machine et de NTLM.
  • Désactiver ou restreindre NTLM dans le domaine, et privilégier Remote Credential Guard ou Restricted Admin pour les connexions d’administration, afin qu’aucun mot de passe réutilisable ne transite.
  • Corriger le modèle ESC17 à la source, en retirant le drapeau « supply in the request » et en restreignant les droits d’enrôlement, ce qui prive l’attaquant du certificat de confiance qui rend l’attaque silencieuse.
  • Segmenter le réseau pour réduire les positions d’homme du milieu exploitables.

Actions

Changer de langueOuvrir le flux RSS

Aller à

AccueilBlogTagsArchives