Aller au contenu

Énumérer les métadonnées LDAP anonymes d'un domaine Active Directory

Énumérer les métadonnées LDAP anonymes d’un domaine Active Directory

Section intitulée « Énumérer les métadonnées LDAP anonymes d’un domaine Active Directory »

Un contrôleur de domaine Active Directory expose un service LDAP (Lightweight Directory Access Protocol) sur les ports 389 (clair/STARTTLS) et 636 (LDAPS). La plupart des données qu’il porte exigent une authentification, mais un sous-ensemble de métadonnées reste lisible sans identifiants, y compris lorsque le bind anonyme classique est désactivé : le RootDSE.

→ Lire l’explication complète de la marmotte

Le RootDSE (Root DSA-Specific Entry) est une entrée virtuelle placée à la racine logique de l’arbre LDAP, avec un nom distingué (DN, Distinguished Name) vide. Elle décrit le serveur lui-même, pas le contenu du domaine.

La RFC 4512 impose qu’elle reste interrogeable sans authentification préalable, pour qu’un client puisse découvrir les capacités du serveur avant de s’authentifier. Windows respecte cette règle même quand dsHeuristics désactive le bind anonyme sur le reste de l’annuaire : le RootDSE échappe aux ACL (Access Control List) qui protègent tout le reste.

Les attributs opérationnels exposés incluent notamment :

  • defaultNamingContext : le contexte de nommage du domaine (ex. DC=corp,DC=tortuetech,DC=lan).
  • rootDomainNamingContext : la racine de la forêt, révélant sa structure dans une forêt multi-domaines.
  • configurationNamingContext et schemaNamingContext : les partitions de configuration et de schéma, communes à toute la forêt.
  • dnsHostName : le FQDN (Fully Qualified Domain Name) du contrôleur de domaine interrogé.
  • domainFunctionality, forestFunctionality, domainControllerFunctionality : les niveaux fonctionnels, codés en entier.
  • serverName, supportedLDAPVersion, supportedSASLMechanisms, supportedControls.

Le niveau fonctionnel du domaine (DFL, Domain Functional Level) révèle indirectement la version minimale des contrôleurs de domaine, donc les mitigations potentiellement absentes. Table de correspondance utile : 0 = 2000, 2 = 2003, 3 = 2008, 4 = 2008 R2, 5 = 2012, 6 = 2012 R2, 7 = 2016, 10 = 2025. Il n’existe pas de valeur distincte pour 2019 et 2022, qui restent au niveau 7.

L’exemple suivant interroge le RootDSE d’un contrôleur de domaine en bind anonyme, avec ldapsearch (paquet ldap-utils sous Debian/Ubuntu), sans fournir aucun identifiant.

Fenêtre de terminal
# -x : authentification simple (ici anonyme, aucun -D ni -w)
# -H : URI du serveur LDAP (port 389 en clair)
# -s base : portée baseObject, on lit uniquement l'entrée RootDSE
# -b "" : base DN vide = le RootDSE
ldapsearch -x -H ldap://10.0.0.10 -s base -b "" \
defaultNamingContext dnsHostName domainFunctionality \
forestFunctionality domainControllerFunctionality \
rootDomainNamingContext supportedSASLMechanisms

Sortie typique (extrait) :

dn:
defaultNamingContext: DC=corp,DC=tortuetech,DC=lan
rootDomainNamingContext: DC=tortuetech,DC=lan
dnsHostName: DC01.corp.tortuetech.lan
domainFunctionality: 7
forestFunctionality: 7
domainControllerFunctionality: 10
supportedSASLMechanisms: GSSAPI
supportedSASLMechanisms: GSS-SPNEGO

On lit ici : domaine et forêt au niveau Windows Server 2016 (7), mais le contrôleur de domaine interrogé tourne sous Windows Server 2025 (10), signe d’une migration en cours non finalisée.

Pour cibler LDAPS (port 636) sans validation de certificat en phase de reconnaissance :

Fenêtre de terminal
# LDAPTLS_REQCERT=never désactive la vérification du certificat serveur.
# Utile pour un diagnostic rapide ; à ne jamais garder en production.
LDAPTLS_REQCERT=never ldapsearch -x -H ldaps://10.0.0.10 \
-s base -b "" supportedLDAPVersion

En PowerShell, la même information s’obtient nativement, sans passer de credentials :

Fenêtre de terminal
$root = [ADSI]"LDAP://10.0.0.10/RootDSE"
$root.defaultNamingContext
$root.domainFunctionality
$root.dnsHostName

Ces requêtes tiennent en une seule connexion TCP et laissent une trace minime : c’est précisément ce qui les rend utiles en reconnaissance, et discrètes côté journalisation.

Le RootDSE ne peut pas être totalement masqué sans casser l’interopérabilité LDAP (jonction de domaine, découverte de serveur). La défense repose donc sur plusieurs leviers cumulatifs, pas sur une interdiction.

  • Ne pas compter sur dsHeuristics pour bloquer la lecture du RootDSE. Le 7ᵉ caractère de cet attribut (fLDAPBlockAnonOps, lisible sous CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,...) contrôle les opérations anonymes sur le reste de l’annuaire, mais laisse le RootDSE lisible par conception ; sur une forêt saine, ce caractère doit valoir 0.
  • Segmenter l’exposition réseau. Les ports 389/636 et, pour le Global Catalog, 3268/3269, ne doivent être joignables que depuis les segments d’administration et les serveurs membres légitimes. Un contrôleur de domaine n’a aucune raison d’être exposé côté DMZ ou Internet ; voir le guide filtrage, segmentation et NAC pour les principes de cloisonnement.
  • Imposer LDAP signing et channel binding (LDAPServerIntegrity=2, LdapEnforceChannelBinding=2, correctifs de 2020) pour bloquer les attaques par relais NTLM sur les binds authentifiés qui suivraient la reconnaissance.
  • Finaliser les niveaux fonctionnels. Un domainFunctionality figé à un ancien niveau interdit certaines mitigations natives ; planifier la montée de DFL/FFL après validation de compatibilité des contrôleurs de domaine.
  • Surveiller les patterns d’énumération. Une rafale de connexions anonymes ciblant objectClass=* en scope=base depuis une IP inhabituelle est un indicateur de reconnaissance. Corréler avec l’event ID 2889 (binds non signés) ; la page menaces réseau et détection couvre les principes de détection applicables.

Masquer le DFL n’a aucun intérêt : ce n’est pas un secret, c’est un signal d’inventaire qu’un attaquant utilise pour prioriser ses exploits. Traiter ces métadonnées comme publiques, et concentrer l’effort de défense sur ce qui suit la reconnaissance.

Si ldapsearch -x -s base -b "" renvoie une erreur, distinguer les causes. Un ldap_result: Can't contact LDAP server pointe un filtrage réseau ou un service arrêté. Un Operations error (code 1) accompagné de « In order to perform this operation a successful bind must be completed » signale généralement une requête portant sur autre chose que le RootDSE : vérifier que la portée est bien base et le DN de base vide.

Sur LDAPS, un échec TLS provient le plus souvent d’une chaîne de certificats non approuvée par le client ; LDAPTLS_REQCERT=never lève le contrôle pour un diagnostic ponctuel, jamais en production. Si supportedSASLMechanisms revient vide, le contrôleur de domaine refuse SASL et n’accepte que le bind simple : configuration inhabituelle à investiguer.

Peut-on désactiver complètement l’accès anonyme au RootDSE ? Non, pas sans rompre la conformité LDAP. Le RootDSE doit rester lisible avant tout bind selon la RFC 4512. Seule la restriction réseau limite cet accès.

Quelle différence entre defaultNamingContext et rootDomainNamingContext ? Le premier est le domaine du contrôleur interrogé ; le second, le domaine racine de la forêt. Ils diffèrent dans une forêt multi-domaines et révèlent alors la hiérarchie.

Peut-on énumérer les utilisateurs via cette méthode anonyme ? Non, pas avec dsHeuristics correctement configuré. Le RootDSE n’expose que des métadonnées du serveur, jamais les objets utilisateur, groupe ou ordinateur.

Le Global Catalog expose-t-il aussi un RootDSE ? Oui, sur les ports 3268/3269. Son RootDSE liste en plus les contextes de nommage de toute la forêt via namingContexts, utile pour cartographier l’ensemble des domaines.

LDAPS (port 636) protège-t-il ces métadonnées ? Il chiffre le transport et empêche l’interception, mais un client anonyme peut toujours lire le RootDSE via une session TLS. Le chiffrement ne remplace pas le filtrage réseau.

Le RootDSE d’un contrôleur de domaine livre, sans aucune authentification et par conception du protocole, les contextes de nommage, le nom DNS et les niveaux fonctionnels du domaine. Une simple requête ldapsearch -x -s base -b "" suffit, et aucune configuration ne le masque totalement. La défense repose sur trois leviers cumulatifs : restreindre l’exposition réseau des ports LDAP (389/636/3268/3269), imposer le signing et le channel binding pour la suite des échanges authentifiés, et détecter les rafales de requêtes anonymes en scope base. Traiter ces métadonnées comme publiques, et bâtir le modèle de menace en tenant leur fuite pour acquise.

Sans filtrage des ports de l’annuaire ni surveillance des requêtes anonymes, la reconnaissance d’un domaine se fait depuis n’importe quelle machine capable de joindre un contrôleur, et ne laisse dans les journaux qu’une connexion banale parmi des milliers. L’attaquant reçoit gratuitement sa liste de priorités : le niveau fonctionnel lui indique quelles protections natives manquent, le nom complet du contrôleur lui désigne la cible exacte de la suite. Faute de signature imposée sur les échanges authentifiés, cette suite prend volontiers la forme d’un relais d’authentification, et l’enquête découvre ensuite une session d’apparence légitime, ouverte avec le compte d’un utilisateur qui n’avait rien demandé.

Active Directory est l’annuaire central d’un réseau Windows d’entreprise : il enregistre les ordinateurs, les comptes et leurs droits. Les logiciels l’interrogent avec un protocole appelé LDAP, sur un port réseau en clair ou sur un port chiffré. Presque toutes les informations de l’annuaire exigent une authentification préalable. Une entrée fait exception : le RootDSE, placée à la racine de l’annuaire, qui décrit le serveur lui-même et non son contenu. La norme du protocole impose qu’elle reste lisible sans identifiant, car un logiciel doit connaître les capacités du serveur avant de pouvoir s’authentifier. Même quand l’accès anonyme est coupé pour le reste de l’annuaire, cette entrée reste donc lisible. Une seule requête suffit pour obtenir le nom du domaine, l’organisation de l’ensemble des domaines, le nom complet du serveur, les méthodes d’authentification acceptées et les niveaux fonctionnels, des nombres qui indiquent la version minimale de Windows des serveurs. Un attaquant s’en sert pour deviner quelles protections récentes manquent. La requête laisse très peu de traces. Comme cette entrée ne peut pas être masquée sans casser le fonctionnement normal, la défense porte sur la suite : n’ouvrir les ports de l’annuaire qu’aux machines légitimes, exiger la signature des échanges et leur liaison au canal chiffré pour empêcher le relais d’authentifications, faire monter les niveaux fonctionnels, et surveiller les rafales de requêtes anonymes venues d’adresses inhabituelles.

À lire aussi

Pages voisines