ADCS certificate template hunt

Hypothesis: certificate template misconfigurations allow an attacker to request a certificate authenticating as a privileged account.

Two phases: enumerate templates in Active Directory to identify ESC1-vulnerable configurations, then review CA issuance events for certificates where an unprivileged requester specified a Subject Alternative Name identifying a different account.

Data sources: Active Directory LDAP (certificate template objects); CA Security event log (Event ID 4887) on the Certificate Authority server; requires CA audit logging enabled (“Issue and manage certificate requests” in the CA Auditing tab).

# Phase 1: identify ESC1-vulnerable certificate templates

Import-Module ActiveDirectory

$configNC      = (Get-ADRootDSE).configurationNamingContext
$templateBase  = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC"
$clientAuthEKU = '1.3.6.1.5.5.7.3.2'

Get-ADObject -SearchBase $templateBase -Filter * -Properties @(
    'msPKI-Certificate-Name-Flag',
    'pKIExtendedKeyUsage',
    'msPKI-RA-Signature'
) | Where-Object {
    # CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT: bit 0x1 in msPKI-Certificate-Name-Flag
    ($_.'msPKI-Certificate-Name-Flag' -band 0x1) -and
    ($_.pKIExtendedKeyUsage -contains $clientAuthEKU) -and
    ($_.'msPKI-RA-Signature' -eq 0)
} | Select-Object Name, 'msPKI-Certificate-Name-Flag', pKIExtendedKeyUsage |
    Sort-Object Name

A template appearing in this output has all three ESC1 conditions in its current configuration: the enrolling user can supply an arbitrary SAN, the certificate supports client authentication, and no manager approval is required. Any domain user able to enrol in the template can request a certificate for any principal.

The query does not check enrolment permissions. A template may restrict enrolment to specific groups, limiting exposure to those members. Reviewing the template’s nTSecurityDescriptor for enrolment rights, or running certutil -v -template TEMPLATE_NAME on the CA server, identifies who can enrol.

# Phase 2: CA event log hunt for anomalous SAN values in issued certificates

$startTime = (Get-Date).AddDays(-7)

Get-WinEvent -ComputerName CA_SERVER -FilterHashtable @{
    LogName   = 'Security'
    Id        = 4887
    StartTime = $startTime
} | ForEach-Object {
    $xml  = [xml]$_.ToXml()
    $data = $xml.Event.EventData.Data

    $requester  = ($data | Where-Object Name -eq 'Requester').'#text'
    $attributes = ($data | Where-Object Name -eq 'Attributes').'#text'
    $requestId  = ($data | Where-Object Name -eq 'RequestId').'#text'

    # CertificateTemplateName is not a separate Event 4887 field; it appears in Attributes
    $template = if ($attributes -match '(?i)CertificateTemplate:([^\r\n]+)') {
        $Matches[1].Trim()
    } else { '' }

    # SAN passed as a request attribute; format: san:upn=account@domain
    if ($attributes -notmatch '(?i)san:') { return }

    [PSCustomObject]@{
        Time       = $_.TimeCreated
        Requester  = $requester
        Template   = $template
        Attributes = $attributes
        RequestId  = $requestId
    }
} | Sort-Object Time

In the output, compare the Requester value against the SAN UPN in the Attributes field. A requester identifying as a standard user account and a SAN UPN identifying a Domain Admin or other privileged account is the ESC1 exploitation signature. The RequestId identifies the certificate in the CA database; certutil -view -restrict "RequestID=ID" on the CA server retrieves the full certificate for further review.

The “san:” check covers requests where the SAN was passed as a request attribute, which is how web enrolment and certreq -attrib style requests work. Certify and Certipy embed the SAN as an X.509 extension inside the CSR; that extension is not reflected in the Event 4887 Attributes field and will not appear here. For comprehensive SAN review, query the CA database directly:

certutil -view -restrict "NotBefore>=MM/DD/YYYY,Disposition=20" -out "RequestID,RequesterName,CommonName"

This lists issued certificates with requester and subject. Certificates where the CommonName differs from the RequesterName by a privileged account name are worth retrieving individually with certutil -view -restrict "RequestID=N" to inspect the full SAN extension.

For ESC6 exploitation, the Attributes field contains a SAN on any issued certificate, not only templates explicitly configured for it. The Phase 1 query does not identify ESC6 because the vulnerability is on the CA policy rather than a specific template. Checking whether the CA carries the EDITF_ATTRIBUTESUBJECTALTNAME2 flag uses certutil -getreg policy\EditFlags on the CA server; a value with bit 0x00040000 set indicates the flag is active.