Depuis la mise à jour du 14 octobre 2025, la clé de registre AllowNtAuthPolicyBypass n’est plus prise en charge par les contrôleurs de domaine: un certificat d’ouverture de session doit maintenant venir d’une autorité présente dans le magasin NTAuth, comme l’indique Microsoft dans le KB5057784 (CVE-2025-26647). Un mois plus tôt, la mise à jour du 9 septembre 2025 avait retiré le dernier moyen de revenir au mode de compatibilité pour le mappage des certificats (KB5014754).
Ces deux changements portent sur la même brique: PKINIT, l’extension de Kerberos qui permet d’obtenir un TGT avec un certificat plutôt qu’avec un mot de passe, et que l’on retrouve aussi bien derrière une ouverture de session par carte à puce que derrière Certipy ou Rubeus. Dans cet article nous allons suivre un KRB_AS_REQ PKINIT depuis le client jusqu’au KDC, sur une maquette, pour comprendre comment un TGT peut être délivré sans que le mot de passe n’intervienne à aucun moment.
La maquette reste simple: un contrôleur de domaine dc01.test.local, une autorité de certification d’entreprise TEST-SRV-AC-CA installée sur srv-ac, et un client win11 sur lequel l’utilisateur jdupont@test.local ouvre sa session avec une carte à puce virtuelle.
Ce que contient un AS-REQ PKINIT
Dans un échange classique, comme nous l’avions vu dans l’article Kerberos: lire un ticket de service avec klist et Wireshark, le client prouve sa connaissance du mot de passe en chiffrant un horodatage (PA-ENC-TIMESTAMP, type 2) avec sa clé Kc, dérivée du mot de passe. Le KDC déchiffre avec la même clé tirée de l’annuaire, et c’est encore cette clé qui chiffre la partie de l’AS-REP destinée au client.
PKINIT, décrit dans la RFC 4556, remplace ce padata par un PA-PK-AS-REQ (type 16). Sa valeur est une structure CMS SignedData dont le contenu, de type id-pkinit-authData (1.3.6.1.5.2.3.1), est un AuthPack:
- pkAuthenticator: cusec, ctime, un nonce identique à celui du req-body, et paChecksum, une empreinte SHA-1 du KDC-REQ-BODY,
- clientPublicValue: les paramètres Diffie-Hellman et la clé publique éphémère du client,
- supportedCMSTypes et clientDHNonce, optionnels.
L’ensemble est signé avec la clé privée du certificat de l’utilisateur, et le certificat lui-même (avec si besoin la chaîne) voyage dans le champ certificates du SignedData. La signature remplace la preuve par le mot de passe; le paChecksum lie cette signature à la requête, ce qui empêche de recoller un AuthPack valide sur un autre req-body.
Lançons Wireshark sur win11 avec le filtre kerberos.padata_type == 16, puis verrouillons et déverrouillons la session. Voici l’arborescence simplifiée de l’AS-REQ:
Kerberos
as-req
pvno: 5
msg-type: krb-as-req (10)
padata: 2 items
PA-DATA pA-PK-AS-REQ
padata-type: pA-PK-AS-REQ (16)
signedAuthPack
ContentType: id-signedData (1.2.840.113549.1.7.2)
eContentType: id-pkinit-authData (1.3.6.1.5.2.3.1)
AuthPack
pkAuthenticator
cusec: 412
ctime: 2026-10-30 09:14:27 (UTC)
nonce: 1838212097
paChecksum: 3b9f0c…
clientPublicValue
algorithm: dhpublicnumber (1.2.840.10046.2.1)
certificates: 2 items
signerInfos: 1 item
PA-DATA pA-PAC-REQUEST
include-pac: True
req-body
cname: jdupont
realm: TEST.LOCAL
sname: krbtgt/TEST.LOCAL
nonce: 1838212097Tout est en clair: le certificat de l’utilisateur, son émetteur, son numéro de série. Rien de secret ne circule, seule la clé privée (dans la puce ou le TPM) a servi à signer.
Le KDC, son certificat et la clé de réponse
À réception, le KDC vérifie la signature du SignedData, construit la chaîne du certificat client jusqu’à une racine de confiance, contrôle la révocation, la fraîcheur de ctime (la tolérance d’horloge Kerberos, 5 minutes par défaut) et la correspondance entre paChecksum et le req-body. Depuis le KB5057784, il exige aussi que l’AC émettrice soit publiée dans l’objet NTAuthCertificates de la partition de configuration. Nous pouvons le vérifier depuis n’importe quel poste du domaine:
certutil -viewstore -enterprise NTAuthCôté EKU, le certificat de l’utilisateur doit porter Smart Card Logon (1.3.6.1.4.1.311.20.2.2) ou l’EKU client PKINIT de la RFC (1.3.6.1.5.2.3.4). Le modèle « Ouverture de session par carte à puce » ou un duplicata de « Utilisateur de carte à puce » fait l’affaire; pour une carte à puce virtuelle ou Windows Hello for Business en mode certificat, nous dupliquons ce modèle et ajustons le fournisseur de clés.
Le KDC a lui aussi besoin d’un certificat, ce que l’on oublie souvent. Le client le vérifie dans la réponse: il doit contenir l’EKU KDC Authentication (1.3.6.1.5.2.3.5) et, dans le SAN, le nom DNS du domaine et le FQDN du contrôleur. Le modèle « Authentification Kerberos » d’AD CS remplit ces conditions. Sans certificat KDC valide, le contrôleur ne peut pas répondre en PKINIT et l’ouverture de session par carte échoue.
Reste la question de départ: avec quelle clé le KDC chiffre-t-il l’enc-part de l’AS-REP, puisqu’il n’utilise plus Kc? Les clients Windows proposent l’échange Diffie-Hellman. Le KDC génère sa propre paire éphémère, place sa clé publique dans un KDCDHKeyInfo qu’il signe avec la clé privée de son certificat, et renvoie le tout dans un PA-PK-AS-REP (type 17). Chaque côté calcule alors le même secret partagé, d’où la RFC 4556 dérive la clé de réponse (fonction octetstring2key, en y mêlant les nonces DH s’ils sont présents). L’autre mode, dit de chiffrement RSA, fait générer la clé de réponse par le KDC, qui la chiffre avec la clé publique du client.
-> Conséquence pratique pour l’analyse de trame: un keytab contenant les clés de jdupont ne permet pas de déchiffrer l’AS-REP d’un échange PKINIT, la clé de réponse n’ayant aucun lien avec le mot de passe.
Mappage du certificat au compte
Une signature valide prouve que le client détient une clé privée, pas qu’il est jdupont. Le KDC doit relier le certificat à un compte de l’annuaire, et c’est là que se trouvaient les failles corrigées par le KB5014754 (Certifried, CVE-2022-26923). Historiquement, le mappage implicite par l’UPN du SAN suffisait: n’importe quel certificat d’une AC de confiance portant jdupont@test.local en UPN ouvrait la session de jdupont.
En mode d’application complète, seuls les mappages forts sont acceptés:
- L’extension SID (szOID_NTDS_CA_SECURITY_EXT, 1.3.6.1.4.1.311.25.2), ajoutée par une AC d’entreprise à jour pour les modèles en ligne, qui contient le SID du compte demandeur,
- Ou un mappage explicite dans l’attribut altSecurityIdentities sous une forme forte: X509IssuerSerialNumber, X509SKI ou X509SHA1PublicKey.
Les formes X509IssuerSubject, X509SubjectOnly ou X509RFC822 sont considérées comme faibles. Pour contrôler un certificat, certutil -dump jdupont.cer affiche les extensions; l’OID 1.3.6.1.4.1.311.25.2 doit y apparaître avec le SID de jdupont. Attention: les certificats émis avant la mise à jour de l’AC, ou issus de modèles hors ligne où le demandeur fournit le sujet, n’ont pas cette extension et doivent être réémis ou mappés explicitement. Les refus se lisent dans le journal Système du contrôleur, événements 39, 40 et 41 de la source Kdcsvc.
Côté journal Sécurité, l’événement 4768 trace la demande de TGT. Le champ Pre-Authentication Type vaut 2 pour un mot de passe et 16 pour PKINIT, et la section Certificate Information se remplit:
A Kerberos authentication ticket (TGT) was requested.
Account Information:
Account Name: jdupont
Supplied Realm Name: TEST.LOCAL
Additional Information:
Result Code: 0x0
Ticket Encryption Type: 0x12
Pre-Authentication Type: 16
Certificate Information:
Certificate Issuer Name: TEST-SRV-AC-CA
Certificate Serial Number: 6100000004A1B2C3D4E5F600000000000004
Certificate Thumbprint: 8F2A…Pour isoler ces événements sur dc01:
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4768] and EventData[Data[@Name='PreAuthType']='16']]" -MaxEvents 20Et le hash NTLM?
Le TGT obtenu suffit pour tout ce qui parle Kerberos. Mais un poste Windows doit encore savoir s’authentifier en NTLM, vers un serveur joint par son adresse IP ou une application qui ne négocie rien d’autre. Sans mot de passe, le client n’a pas de hash NT.
Le KDC le lui fournit dans le PAC. Comme décrit dans [MS-PAC], lorsqu’un TGT est émis via PKINIT, le PAC contient un buffer supplémentaire de type 0x00000002, PAC_CREDENTIAL_INFO. Son contenu, un PAC_CREDENTIAL_DATA avec une entrée « NTLM » de type NTLM_SUPPLEMENTAL_CREDENTIAL (hash LM et NT), est chiffré avec la clé de réponse de l’AS, celle que seul le client a pu calculer avec le KDC. Nous retrouvons ici la structure du PAC que nous avions déchiffrée dans l’article Exploration du protocole KERBEROS – Déchiffrer le PAC, avec un buffer de plus.
Le PAC du TGT étant chiffré avec la clé de krbtgt, le client ne peut pas le lire directement. La méthode utilisée par les outils publics consiste à demander un ticket de service user-to-user à destination de soi-même: le ticket est alors chiffré avec la clé de session du TGT, que le client connaît, et contient une copie du PAC. Le client en extrait PAC_CREDENTIAL_INFO puis le déchiffre avec sa clé de réponse AS. C’est ce que font Rubeus asktgt /certificate:… /getcredentials ou certipy auth, technique connue sous le nom d’UnPAC-the-hash.
Ce point est très important pour la sécurité: qui détient le certificat et sa clé privée obtient le hash NT du compte, et l’obtient à nouveau après chaque changement de mot de passe, tant que le certificat est valide et non révoqué. Un certificat d’ouverture de session volé vaut donc un identifiant persistant, souvent valable un ou deux ans. Pour les comptes à carte à puce obligatoire, l’attribut msDS-ExpirePasswordsOnSmartCardOnlyAccounts (niveau fonctionnel 2016) fait au moins tourner ce secret automatiquement. Pour le reste, je recommande des clés privées non exportables (carte, TPM, comme pour Windows Hello for Business), des durées de validité courtes sur les modèles d’ouverture de session, une révocation réellement publiée, et une surveillance des 4768 de type 16 dont l’émetteur n’est pas l’AC attendue.
Rémi Vernier
Responsable des opérations cybersécurité dans un grand groupe international. Identité, Active Directory, Entra ID, cloud hybride, et désormais SAP, IA et modèles en local.