Query Details

Multiple Leaked Credentials

Query

let query_frequency = 5m;
let query_period = 2d;
AADUserRiskEvents
| where TimeGenerated > ago(query_period)
| where Source == "IdentityProtection" and RiskEventType == "leakedCredentials"
| summarize FirstTimeGenerated = min(TimeGenerated), arg_max(TimeGenerated, *) by Id
| where FirstTimeGenerated > ago(query_frequency)
| project
    //TimeGenerated,
    ActivityDateTime,
    DetectedDateTime,
    Source,
    Activity,
    DetectionTimingType,
    UserDisplayName,
    UserPrincipalName,
    UserId,
    //IpAddress,
    //RequestId,
    //CorrelationId,
    TokenIssuerType,
    RiskEventType,
    RiskDetail,
    RiskLevel,
    RiskState,
    AdditionalInfo,
    Id
| mv-apply Auxiliar = AdditionalInfo on (
    summarize AdditionalInfoBag = make_bag(bag_pack(tostring(Auxiliar["Key"]), Auxiliar["Value"]))
    )
| extend
    AltAlertLink = strcat("https://entra.microsoft.com/#blade/Microsoft_AAD_IAM/RiskDetectionsBlade/riskState~/[]/userId/", UserId, "/riskLevel/[]/daysBack/90"),// Someone wrote "90s" incorrectly in Defender XDR portal
    AltDescription = "This risk event indicates that the user's valid credentials have been leaked",
    AltAlertSeverity = strcat(toupper(substring(RiskLevel, 0, 1)), substring(RiskLevel, 1)),
    AltTactics = "PreAttack",
    AltTechniques = replace_regex(tostring(split(tostring(AdditionalInfoBag["mitreTechniques"]), ",")), @'(\")(T\d+)(\.\d+)(\")', @"\1\2\4"),
    AltSubTechniques = replace_regex(tostring(split(tostring(AdditionalInfoBag["mitreTechniques"]), ",")), @'(\,)?(\"T\d+\")', @"")
| join kind=leftouter (
    SecurityAlert
    | where ProviderName == "IPC" and ProductName == "Azure Active Directory Identity Protection" and AlertType == "LeakedCredentials"
    | summarize arg_max(TimeGenerated, *) by VendorOriginalId
    | project
        AlertName,
        AlertSeverity,
        Description,
        AlertStatus = Status,
        Entities,
        ExtendedProperties,
        VendorName,
        ProviderName,
        ProductName,
        ProductComponentName,
        RemediationSteps,
        Tactics,
        Techniques,
        SubTechniques,
        VendorOriginalId,
        SystemAlertId,
        CompromisedEntity,
        AlertLink
    ) on $left.Id == $right.VendorOriginalId
| extend
    AlertLink = coalesce(AlertLink, AltAlertLink),
    Description = coalesce(Description, AltDescription),
    AlertSeverity = coalesce(AlertSeverity, AltAlertSeverity),
    Tactics = coalesce(Tactics, AltTactics),
    Techniques = coalesce(Techniques, iff(array_length(todynamic(AltTechniques)) == 0, "", AltTechniques)),
    SubTechniques = coalesce(SubTechniques, iff(array_length(todynamic(AltSubTechniques)) == 0, "", AltSubTechniques))
| where case(
    AlertStatus == "Resolved" and tostring(todynamic(ExtendedProperties)["State"]) == "Closed", false,
    //RiskState == "dismissed" and RiskDetail == "aiConfirmedSigninSafe", false,
    RiskState == "remediated" and RiskDetail == "userChangedPasswordOnPremises", false,
    true
    )
| project
    //TimeGenerated,
    ActivityDateTime,
    DetectedDateTime,
    Source,
    Activity,
    DetectionTimingType,
    UserDisplayName,
    UserPrincipalName,
    UserId,
    // IpAddress,
    // RequestId,
    // CorrelationId,
    TokenIssuerType,
    RiskEventType,
    RiskDetail,
    RiskLevel,
    RiskState,
    AdditionalInfo,
    Id,
    AlertName,
    AlertSeverity,
    Description,
    AlertStatus,
    Entities,
    ExtendedProperties,
    VendorName,
    ProviderName,
    ProductName,
    ProductComponentName,
    RemediationSteps,
    Tactics,
    Techniques,
    SubTechniques,
    VendorOriginalId,
    SystemAlertId,
    CompromisedEntity,
    AlertLink

Explanation

This query is designed to analyze and correlate user risk events related to leaked credentials in Azure Active Directory Identity Protection. Here's a simplified breakdown of what the query does:

  1. Define Time Parameters:

    • query_frequency is set to 5 minutes.
    • query_period is set to 2 days.
  2. Filter Risk Events:

    • It retrieves events from the AADUserRiskEvents table that occurred within the last 2 days.
    • It specifically looks for events where the source is "IdentityProtection" and the risk event type is "leakedCredentials".
  3. Summarize Events:

    • It summarizes the data to find the first occurrence of each event by Id and keeps the most recent details.
  4. Filter Recent Events:

    • It only keeps events that first occurred within the last 5 minutes.
  5. Project Relevant Columns:

    • It selects specific columns to keep for further processing, excluding some like IpAddress, RequestId, and CorrelationId.
  6. Process Additional Information:

    • It processes the AdditionalInfo field to create a structured bag of key-value pairs.
  7. Create Alternative Fields:

    • It constructs alternative alert links, descriptions, severity, tactics, techniques, and sub-techniques based on the processed data.
  8. Join with Security Alerts:

    • It performs a left outer join with the SecurityAlert table to correlate risk events with existing security alerts.
    • It matches records based on Id from the risk events and VendorOriginalId from the security alerts.
  9. Extend and Coalesce Fields:

    • It fills in missing alert details using alternative fields created earlier if the original data is unavailable.
  10. Filter Out Resolved or Remediated Events:

    • It excludes events that are resolved and closed or remediated by a password change.
  11. Project Final Output:

    • It selects a comprehensive set of columns for the final output, including alert details and user information.

In summary, this query identifies recent leaked credential events, enriches them with additional information, correlates them with existing security alerts, and filters out resolved or remediated cases to provide a detailed view of ongoing risks.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: October 7, 2025

Tables

AADUserRiskEvents SecurityAlert

Keywords

AADUserRiskEventsSecurityAlertUserIdentityProtectionAzureActiveDirectoryRiskEventTypeRiskLevelRiskStateAlertNameAlertSeverityDescriptionAlertStatusVendorNameProviderNameProductNameTechniquesSubTechniques

Operators

letagowheresummarizeminarg_maxbyprojectmv-applymake_bagbag_packextendstrcattouppersubstringreplace_regextostringsplitjoinkindleftoutercoalesceiffarray_lengthtodynamiccase

Actions

GitHub