Query Details

RULE 07 AD ADCS Certificate SAN Mismatch

Query

// =========================================================
// RULE-07 | AD-ADCS-Certificate-SAN-Mismatch
// Description : ADCS ESC1 / ESC8 Certificate Abuse —
//               Event 4887 (Certificate Issued) where the
//               Subject Alternative Name (SAN) in the
//               certificate contains a UPN that DIFFERS from
//               the requesting account, OR where the same
//               account requests ≥3 certificates within 10
//               minutes (burst enrollment pattern).
//               ESC1 lets an attacker supply any SAN
//               (e.g., administrator@domain) when requesting
//               a certificate, effectively impersonating DA.
// Severity    : High (SAN mismatch) → Critical (DA UPN in SAN
//               or machine-account cert issued as DC name)
// Frequency   : Every 15 minutes, look-back 15 minutes
// MITRE       : T1649 — Steal or Forge Authentication Certs
//               T1078  — Valid Accounts
// Tables      : SecurityEvent
// =========================================================

let LookBack       = 15m;
let BurstThreshold = 3;        // ≥3 certs/10 min from 1 account = suspicious

// High-value SANs (if SAN contains these → Critical)
let SensitiveSANPatterns = dynamic([
    "administrator", "domain admins", "enterprise admins",
    "krbtgt", "dc$", "-dc@", "dc01", "dc02"
]);

// Event 4887: Certificate Issued — extract SAN from EventData
SecurityEvent
| where TimeGenerated > ago(LookBack)
| where EventID == 4887
| extend
    RequesterAccount = SubjectUserName,
    RequesterDomain  = SubjectDomainName,
    CertificateDetails = EventData
// Parse the Subject and Requester from raw EventData (XML-style)
| extend SubjectField     = extract(@"<Data Name=""SubjectKeyIdentifier"">[^<]*</Data>", 0, EventData)
| extend TemplateName     = extract(@"<Data Name=""CertificateTemplate"">(.*?)</Data>", 1, EventData)
| extend RequestAttributes= extract(@"<Data Name=""RequestAttributes"">(.*?)</Data>", 1, EventData)
// Look for SAN that doesn't match the requester
| extend SANValue = extract(@"(?i)(san|subjectaltname|upn)=([^;,]+)", 2, RequestAttributes)
| extend SANNormalized = tolower(SANValue)
| extend RequesterNorm  = tolower(RequesterAccount)
| extend
    SANMismatch       = isnotempty(SANValue) and not(SANNormalized contains RequesterNorm),
    SensitiveSAN      = isnotempty(SANValue) and SANNormalized has_any (SensitiveSANPatterns)
// Burst enrollment detection
| summarize
    TotalCerts         = count(),
    MismatchedCerts    = countif(SANMismatch),
    SensitiveSANCerts  = countif(SensitiveSAN),
    Templates          = make_set(TemplateName, 10),
    SANValues          = make_set(SANValue, 10),
    EarliestCert       = min(TimeGenerated),
    LatestCert         = max(TimeGenerated)
    by RequesterAccount, RequesterDomain, Computer
| where TotalCerts >= BurstThreshold
    or MismatchedCerts >= 1
    or SensitiveSANCerts >= 1
| extend
    Severity = case(
        SensitiveSANCerts >= 1, "Critical",
        MismatchedCerts >= 1,   "High",
        TotalCerts >= BurstThreshold, "Medium",
        "Low"
    ),
    WhySuspicious = strcat(
        iff(SensitiveSANCerts >= 1, "SensitiveSAN_DA_or_DC; ", ""),
        iff(MismatchedCerts >= 1,   "SAN_Mismatch_ESC1_Pattern; ", ""),
        iff(TotalCerts >= BurstThreshold, "BurstEnrollment_>=3_certs; ", ""),
        "Templates: ", strcat_array(Templates, ",")
    )
| project
    TimeGenerated    = LatestCert,
    Severity,
    WhySuspicious,
    RequesterAccount,
    RequesterDomain,
    Computer,
    TotalCerts,
    MismatchedCerts,
    SensitiveSANCerts,
    SANValues,
    Templates
| order by SensitiveSANCerts desc, MismatchedCerts desc

Explanation

This query is designed to detect suspicious activities related to the issuance of certificates in an Active Directory Certificate Services (ADCS) environment. Here's a simplified explanation of what it does:

  1. Time Frame: It looks at security events from the last 15 minutes.

  2. Event Focus: It specifically examines Event ID 4887, which indicates that a certificate has been issued.

  3. Key Checks:

    • SAN Mismatch: It checks if the Subject Alternative Name (SAN) in the certificate differs from the User Principal Name (UPN) of the account that requested the certificate. This could indicate that someone is trying to impersonate another user.
    • Sensitive SANs: It looks for SANs that contain high-value terms like "administrator" or "domain admins". If these are found, it's considered highly suspicious.
    • Burst Enrollment: It identifies if the same account requests three or more certificates within a 10-minute window, which could indicate automated or malicious activity.
  4. Severity Levels:

    • Critical: If a sensitive SAN is detected.
    • High: If there's a SAN mismatch.
    • Medium: If there's a burst enrollment pattern.
    • Low: If none of the above conditions are met.
  5. Output: The query summarizes the findings, providing details such as the account that requested the certificates, the domain, the computer involved, and the number of certificates issued. It also explains why the activity is considered suspicious.

  6. Sorting: The results are ordered by the most critical findings, prioritizing sensitive SANs and mismatches.

Overall, this query helps identify potential certificate abuse or misconfigurations that could lead to unauthorized access or impersonation within a network.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

SecurityEvent

Keywords

SecurityEvent

Operators

letagodynamicwhereextendextracttolowerisnotemptycontainshas_anysummarizecountcountifmake_setminmaxbycaseiffstrcatstrcat_arrayprojectorder by

Actions