Breaking Active Directory Certificate Services: ESC1 Exploitation Mechanics, SAN Impersonation, and PKI Hardening

Article Hero

Technical Overview & Threat Model Active Directory Certificate Services (AD CS) provides public key infrastructure (PKI) capabilities to enterprise domains, underpinning smart card authentication, TLS server verification, S/MIME encryption, and Code Signing. Because Kerberos supports public key cryptography for initial authentication (PKINIT via RFC 4556), a certificate issued to an Active Directory account functions as a direct credential equivalent to a plaintext password or NTLM hash.

However, certificate templates in AD CS are frequently burdened by legacy administrative misconfigurations. The most critical and widespread vulnerability is known as ESC1 (Escalation Path 1, first systematized by SpecterOps in their Certified Pre-Owned research). The threat model targets the gap between certificate request parameters and identity authorization: when an enterprise Certificate Authority (CA) publishes a certificate template that permits the requesting client to specify a Subject Alternative Name (SAN), an unprivileged domain user can request a certificate while embedding the identity of a high-privilege account (such as a Domain Administrator or Enterprise Administrator).

Upon issuance, the unprivileged user receives a cryptographically valid certificate signed by the trusted domain CA containing the Domain Admin's User Principal Name (UPN) in the SAN field. The adversary presents this certificate to a Domain Controller during a Kerberos AS-REQ exchange via PKINIT. The KDC validates the certificate against the Enterprise NTAuth store, maps the requested identity from the SAN UPN, and issues a high-privilege Ticket Granting Ticket (TGT), achieving full domain compromise.

sequenceDiagram

    autonumber

    actor Attacker as Unprivileged Domain User

    participant CA as AD CS Enterprise CA

    participant KDC as Domain Controller / KDC

    participant NTAuth as Active Directory NTAuth Store

    Note over Attacker,CA: Phase 1: Certificate Enrollment with Forged SAN

    Attacker->>CA: Certificate Request (CSR)
Template: Vulnerable-User-Cert
Supplied SAN: dadmin@corp.local Note over CA: CA verifies template flags:
1. Client Authentication EKU present
2. CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT enabled
3. Requester has Enroll permissions
4. Manager approval NOT required CA-->>Attacker: Signed X.509 Certificate with Domain Admin SAN UPN Note over Attacker,KDC: Phase 2: PKINIT Kerberos Authentication Attacker->>KDC: KRB_AS_REQ via PKINIT (padata: PA-PK-AS-REQ)
Signs authenticator with certificate private key KDC->>NTAuth: Validate CA certificate is present in NTAuthCertificates Note over KDC: KDC extracts SAN UPN (dadmin@corp.local)
Evaluates certificate mapping to target user object KDC-->>Attacker: KRB_AS_REP (TGT for Domain Admin + Session Key) Note over Attacker: Phase 3: Full Domain Escalation Achieved Attack Anatomy & Template Misconfiguration Prerequisites An AD CS certificate template represents an exploitable ESC1 escalation vector when four structural conditions align simultaneously in the template's Active Directory schema attributes: ```mermaid flowchart TD A[AD CS Certificate Template Discovered] --> B{Client Authentication EKU Present?} B -->|No| C[Not Vulnerable to PKINIT Impersonation] B -->|Yes: Client Auth 1.3.6.1.5.5.7.3.2 or Smart Card 1.3.6.1.4.1.311.20.2.2| D{Enrollee Supplies Subject?} D -->|No: CA Builds SAN from AD| E[Secure: Enrollee Cannot Forge SAN] D -->|Yes: msPKI-Certificate-Name-Flag contains 0x1| F{Manager Approval Required?} F -->|Yes: Issuance Requirements Enforced| G[Pending Approval: Administrative Gatekeeper] F -->|No: msPKI-Enrollment-Flag lack 0x2| H{Enrollment Permissions?} H -->|Restricted to Admins| I[Secure: Unprivileged Users Denied] H -->|Granted to Domain Users or Authenticated Users| J[CRITICAL ESC1 VULNERABILITY: Domain Takeover Possible] The Four ESC1 Pillars Explained 1. Enterprise CA Issuance: The template is published on an active Enterprise CA listed in the forest's NTAuthCertificates container. Certificates issued by standalone CAs cannot be used for smart card logon or PKINIT authentication. 2. Authentication EKU: The template specifies an Extended Key Usage (EKU) that supports domain identity authentication. The primary EKUs are: * Client Authentication (1.3.6.1.5.5.7.3.2) * Smart Card Logon (1.3.6.1.4.1.311.20.2.2) * Any Purpose (2.5.29.37.0) * SubCA (Null / No EKU), which behaves as Any Purpose by default. 3. Enrollee Supplies Subject (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT): In the Active Directory LDAP attribute msPKI-Certificate-Name-Flag, bit 0x00000001 is set. This instructs the CA not to construct the certificate's Subject or Subject Alternative Name from the requester's Active Directory object, but instead to accept whatever Subject Name and SAN the client passes in the Certificate Signing Request (CSR). 4. Permissive Access Rights: The security descriptor of the certificate template grants Enroll or GenericAll / WriteDacl permissions to broad security groups, such as Domain Users, Authenticated Users, or Domain Computers. Furthermore, the template requires zero authorized signatures (msPKI-RA-Signature == 0) and has manager approval disabled. Critical Operational Trap: Strong Certificate Mapping & KB5014754 In May 2022, Microsoft introduced KB5014754 to address certificate impersonation vulnerabilities (including CVE-2022-26923 and ESC1). The update fundamentally changed how domain controllers map certificates to user accounts during PKINIT: * Legacy Mapping (Weak): Domain controllers mapped certificates purely based on the UPN string found in the SAN (san:upn=victim@domain.local) or the Subject Name. * Strong Mapping: The CA embeds a non-critical extension containing the user's security identifier (SID): szOID_NTDS_CA_SECURITY_EXT (1.3.6.1.4.1.311.25.2). When processing PKINIT, the domain controller extracts the explicit object SID from this extension and verifies it directly against Active Directory. Authentication Phase Registry Setting / Mode DC Validation Logic ESC1 Impact Audit Mode StrongCertificateBindingEnforcement = 0 DC logs Warning Events (39, 40, 41) but permits weak SAN UPN mapping Fully exploitable via standard SAN injection Compatibility Mode StrongCertificateBindingEnforcement = 1 DC permits weak mapping if the certificate lacks the SID extension Fully exploitable if certificate is requested without SID extension Full Enforcement StrongCertificateBindingEnforcement = 2 DC rejects any certificate lacking strong mapping or explicit altSecurityIdentities Mitigates standard ESC1; requires explicit certificate mapping on user accounts A common production gotcha is assuming that applying KB5014754 automatically blocks ESC1. Because many enterprise environments run domain controllers in Compatibility Mode (value=1) to avoid breaking legacy smart cards and third-party PKI clients, attackers can still exploit ESC1 by requesting certificates without the SID extension or enrolling against non-updated CAs. Audit & Discovery Script The following production PowerShell script queries Active Directory Configuration naming context to identify all published certificate templates exhibiting the ESC1 configuration matrix: <# .SYNOPSIS Fast Cyber Defense - Active Directory Certificate Services (AD CS) ESC1 Auditor .DESCRIPTION Scans Active Directory certificate templates published in the Configuration container, identifying templates allowing enrollee-supplied SANs with Client Authentication EKUs. #> ```powershell [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$DomainController = $env:LOGONSERVER.TrimStart('') ) Import-Module ActiveDirectory -ErrorAction Stop Write-Host "[*] Interrogating Active Directory Configuration Partition on $DomainController..." -ForegroundColor Cyan $ConfigContext = (Get-ADRootDSE -Server $DomainController).configurationNamingContext $TemplatesPath = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext" $Templates = Get-ADObject -SearchBase $TemplatesPath ` -Filter {objectClass -eq "pKICertificateTemplate"} ` -Server $DomainController ` -Properties cn, displayName, msPKI-Certificate-Name-Flag, ` msPKI-Enrollment-Flag, pKIExtendedKeyUsage, nTSecurityDescriptor $VulnerableTemplates = [System.Collections.Generic.List[PSObject]]::new() ## OIDs supporting client identity authentication $AuthEKUs = @( "1.3.6.1.5.5.7.3.2", # Client Authentication "1.3.6.1.4.1.311.20.2.2", # Smart Card Logon "2.5.29.37.0" # Any Purpose ) foreach ($Tmpl in $Templates) { $NameFlag = $Tmpl.'msPKI-Certificate-Name-Flag' $EnrollFlag = $Tmpl.'msPKI-Enrollment-Flag' $EKUs = $Tmpl.pKIExtendedKeyUsage # Check for CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT (Bit 0x1) $EnrolleeSuppliesSAN = $false if ($null -ne $NameFlag -and ($NameFlag -band 0x1)) { $EnrolleeSuppliesSAN = $true } # Check if Manager Approval is required (CT_FLAG_PEND_ALL_REQUESTS = 0x2) $RequiresApproval = $false if ($null -ne $EnrollFlag -and ($EnrollFlag -band 0x2)) { $RequiresApproval = $true } # Check for Client Authentication EKUs or Null EKU (SubCA/All) $HasAuthEKU = $false if ($null -eq $EKUs -or $EKUs.Count -eq 0) { $HasAuthEKU = $true # No EKU implies Any Purpose } else { foreach ($Eku in $EKUs) { if ($AuthEKUs -contains $Eku) { $HasAuthEKU = $true break } } } # Check permissions for broad groups (Domain Users / Authenticated Users) $SecurityDescriptor = $Tmpl.nTSecurityDescriptor $PermissiveEnrollment = $false if ($null -ne $SecurityDescriptor) { $Sddl = $SecurityDescriptor.GetSddlForm([System.Security.AccessControl.AccessControlSections]::Access) # S-1-5-11 = Authenticated Users, S-1-5-21-...-513 = Domain Users if ($Sddl -match "(A;;(RP|CR|CCDCLCSWRPSDRCWDKEY);;;(AU|S-1-5-11|S-1-5-21-[0-9-]+-513))") { $PermissiveEnrollment = $true } } if ($EnrolleeSuppliesSAN -and $HasAuthEKU -and (-not $RequiresApproval) -and $PermissiveEnrollment) { $VulnerableTemplates.Add([PSCustomObject]@{ TemplateName = $Tmpl.cn DisplayName = $Tmpl.displayName EnrolleeSuppliesSAN = $EnrolleeSuppliesSAN RequiresApproval = $RequiresApproval ConfiguredEKUs = if ($null -eq $EKUs) { "None (Any Purpose)" } else { $EKUs -join ", " } Severity = "CRITICAL - ESC1 High Risk" }) } } Write-Host "[+] Audit complete. Scanned $($Templates.Count) templates. Found $($VulnerableTemplates.Count) ESC1 candidates." -ForegroundColor Green $VulnerableTemplates | Format-Table -AutoSize TemplateName, Severity, ConfiguredEKUs, RequiresApproval Defensive Hardening & Runtime Telemetry Remediating ESC1 misconfigurations requires removing enrollees' ability to supply SANs, enforcing CA manager approvals, and monitoring certificate issuance events. 1. Template Hardening Runbook Security architects must enforce the principle of least privilege across all published certificate templates:
  1. Disable Enrollee-Supplied Subject: In the Certificate Template Console (certtmpl.msc), navigate to the Subject Name tab. Select Build from this Active Directory information instead of Supply in the request. Ensure the Subject name format is set to Common Name, and include the User principal name (UPN) in the alternative subject name.
  2. Enforce CA Certificate Manager Approval: For templates requiring custom SANs (e.g., multi-domain TLS certificates), navigate to the Issuance Requirements tab and enable CA certificate manager approval. This enforces human review before certificate generation.
  3. Audit Template ACLs: Remove Domain Users and Authenticated Users from the template security permissions. Grant Enroll access strictly to tightly audited, dedicated security groups.

The following script automates disabling the ENROLLEE_SUPPLIES_SUBJECT flag on a target template in Active Directory:

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]$TemplateName,
    [Parameter(Mandatory = $false)]
    [string]$DomainController = $env:LOGONSERVER.TrimStart('')
)
Import-Module ActiveDirectory -ErrorAction Stop
$ConfigContext = (Get-ADRootDSE -Server $DomainController).configurationNamingContext
$TemplatePath = "CN=$TemplateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigContext"
$Template = Get-ADObject -Identity $TemplatePath -Server $DomainController -Properties "msPKI-Certificate-Name-Flag"
if ($null -eq $Template) {
    Write-Error "Certificate template $TemplateName not found."
    return
}
$CurrentFlag = $Template.'msPKI-Certificate-Name-Flag'
Write-Host "[*] Current msPKI-Certificate-Name-Flag for $TemplateName : 0x{0:X}" -f $CurrentFlag
## Strip CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT (Bitwise NOT 0x1)
$HardenedFlag = $CurrentFlag -band (-bnot 0x1)
## Enforce CT_FLAG_SUBJECT_REQUIRE_DIRECTORY_PATH (0x80000000) or CT_FLAG_SUBJECT_REQUIRE_COMMON_NAME (0x40000000)
$HardenedFlag = $HardenedFlag -bor 0x40000000
Set-ADObject -Identity $TemplatePath -Replace @{'msPKI-Certificate-Name-Flag' = $HardenedFlag} -Server $DomainController
Write-Host "[+] Successfully hardened $TemplateName. New flag: 0x{0:X}" -f $HardenedFlag -ForegroundColor Green
2. Detection Engineering: Active Directory Certificate Services Auditing
To detect ESC1 abuse in real-time, security operations must enable auditing on the Certificate Authority and monitor domain controller security logs:
* CA Server Logs (Event ID 4886 & 4887): Event ID 4886 indicates Certificate Services received a certificate request; Event ID 4887 indicates Certificate Services approved and issued a certificate. Key inspection fields include Requester, Template, and Subject Alternative Name. An anomaly exists when Requester is an unprivileged user while the SAN specifies a privileged administrator account.
* Domain Controller Logs (Event ID 4768): Event ID 4768 records Kerberos TGT requests. When PKINIT is used, CertIssuerName, CertSerialNumber, and CertThumbprint are populated. Analysts must correlate Event 4768 PKINIT logins with recent 4887 certificate issuance logs.
Below is a production-grade Sigma rule detecting anomalous certificate issuance containing Subject Alternative Name additions:
```yaml
title: AD CS Suspicious Certificate Issued with Custom Subject Alternative Name (ESC1)
id: f39b1a24-8b6a-4d7e-9021-d1c876e9a032
status: production
description: |
  Detects the issuance of an Active Directory Certificate Services (AD CS) certificate
  where the requester supplied custom Subject Alternative Name (SAN) attributes matching
  privileged user identities or differing from the requester account.
references:
  - https://specterops.io/blog/2021/06/17/certified-pre-owned/
  - https://support.microsoft.com/en-us/servicing/os/windows-server/2022/05/kb5014754
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4887
  filter_san_empty:
    SubjectAlternativeName:
      - ''
      - '-'
  filter_machine_enrollments:
    Requester|endswith: '$'
  filter_known_templates:
    CertificateTemplate:
      - 'Machine'
      - 'DomainController'
  condition: selection and not 1 of filter_*
fields:
  - Computer
  - Requester
  - CertificateTemplate
  - SubjectAlternativeName
  - SerialNumber
falsepositives:
  - Legitimate multi-domain SSL/TLS server certificate requests generated by web administrators
  - Third-party mobile device management (MDM) enrollment systems using custom SANs
level: high
tags:
  - attack.credential_access
  - attack.privilege_escalation
  - attack.t1649
3. Automated Telemetry Correlator for PKI Impersonation
The following Python utility parses normalized Windows Event log streams to correlate CA certificate issuance (Event 4887) with subsequent Kerberos PKINIT TGT requests (Event 4768), alerting when an unprivileged account requests a certificate that is immediately used by an administrator:
```python
#!/usr/bin/env python3
```bash
## Fast Cyber Defense - AD CS PKINIT Anomaly Correlator
## Identifies identity mismatch between certificate enrollment and Kerberos PKINIT usage.
from collections import defaultdict
from datetime import datetime, timedelta
from typing import Dict, List, Optional
class ADCSCorrelator:
    def __init__(self, time_window_seconds: int = 600):
        self.window = timedelta(seconds=time_window_seconds)
        # Tracks issued certificates: { serial_number: (timestamp, requester, san_upn, template) }
        self.issued_certs: Dict[str, tuple] = {}
    def process_event(self, event: dict) -> Optional[dict]:
        event_id = event.get("EventID")
        timestamp_str = event.get("TimeCreated")
        timestamp = datetime.fromisoformat(timestamp_str)
        # Process Event 4887: Certificate Issued by CA
        if event_id == 4887:
            serial = event.get("SerialNumber", "").lower()
            requester = event.get("Requester", "").lower()
            san_upn = event.get("SubjectAlternativeName", "").lower()
            template = event.get("CertificateTemplate", "")
            if serial and san_upn:
                self.issued_certs[serial] = (timestamp, requester, san_upn, template)
            return None
        # Process Event 4768: Kerberos TGT Requested via PKINIT
        elif event_id == 4768:
            serial = event.get("CertSerialNumber", "").lower()
            target_user = event.get("TargetUserName", "").lower()
            client_ip = event.get("IpAddress", "unknown")
            if serial in self.issued_certs:
                issue_time, requester, san_upn, template = self.issued_certs[serial]
                # Check for temporal proximity
                if (timestamp - issue_time) <= self.window:
                    requester_user = requester.split("\")[-1]
                    # Flag identity mismatch: requester differs from authenticated user
                    if requester_user != target_user:
                        alert = {
                            "Alert": "Critical AD CS ESC1 Impersonation Detected",
                            "Severity": "CRITICAL",
                            "CertificateSerialNumber": serial,
                            "OriginalRequester": requester,
                            "ImpersonatedTarget": target_user,
                            "TemplateName": template,
                            "ClientIP": client_ip,
                            "Timestamp": timestamp_str
                        }
                        del self.issued_certs[serial]
                        return alert
        return None
if __name__ == "__main__":
    correlator = ADCSCorrelator(time_window_seconds=300)
    # Simulated attack scenario: Unprivileged user 'jdoe' enrolls certificate for 'admin_bob'
    sample_events = [
        {
            "EventID": 4887,
            "SerialNumber": "4f12ab34cd56",
            "Requester": "CORP\jdoe",
            "SubjectAlternativeName": "admin_bob@corp.local",
            "CertificateTemplate": "UserAuth-ESC1",
            "TimeCreated": "2026-09-03T12:05:00"
        },
        {
            "EventID": 4768,
            "CertSerialNumber": "4f12ab34cd56",
            "TargetUserName": "admin_bob",
            "IpAddress": "10.0.10.15",
            "TimeCreated": "2026-09-03T12:05:22"
        }
    ]
    for record in sample_events:
        alert = correlator.process_event(record)
        if alert:
            print(f"[!] SIEM ALERT: {alert['Alert']}")
            print(f"    Requester: {alert['OriginalRequester']} -> Impersonated: {alert['ImpersonatedTarget']}")
            print(f"    Serial: {alert['CertificateSerialNumber']} | Source IP: {alert['ClientIP']}")
Fast Cyber Defense Key Takeaways
* Certificates Are Direct Credential Equivalents: In Active Directory, any certificate containing a Client Authentication EKU acts as a primary authentication token via PKINIT (RFC 4556). Compromise of a certificate template granting user impersonation equates to full domain compromise.
* Eliminate Enrollee-Supplied SANs: The fundamental root cause of ESC1 is CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT. Standard user certificate templates must derive the Subject Alternative Name strictly from Active Directory attributes.
* Enforce Manager Approvals on Custom Templates: If an application genuinely requires enrollees to supply custom SANs (e.g., multi-domain TLS), enforce the CT_FLAG_PEND_ALL_REQUESTS flag to mandate manual review by a designated Certificate Manager.
* Enforce Full Strong Certificate Mapping (KB5014754): Ensure domain controllers are transitioned from Compatibility Mode (value=1) to Full Enforcement Mode (StrongCertificateBindingEnforcement = 2). This requires certificates to contain the explicit szOID_NTDS_CA_SECURITY_EXT SID extension, defeating legacy UPN spoofing.
* Correlate CA Issuance with PKINIT Logons: Monitor CA Event ID 4887 and Domain Controller Event ID 4768 to detect when a certificate requested by an unprivileged identity is subsequently used to request a Kerberos TGT for an administrative account.
References & Further Reading
* RFC 4556 - Public Key Cryptography for Initial Authentication in Kerberos (PKINIT)
* RFC 5280 - Internet X.509 Public Key Infrastructure Certificate and CRL Profile
* SpecterOps Research - Certified Pre-Owned: Abusing Active Directory Certificate Services
* Microsoft Support - KB5014754: Certificate-based authentication changes on Windows domain controllers
* National Vulnerability Database - CVE-2022-26923 Detail (Active Directory Domain Services Privilege Escalation)

Comments