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
| 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
| 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)
| where case(
    AlertStatus == "Resolved" and tostring(todynamic(ExtendedProperties)["State"]) == "Closed", false,
    //RiskState == "dismissed" and RiskDetail == "aiConfirmedSigninSafe", false,
    RiskState == "remediated" and RiskDetail == "userChangedPasswordOnPremises", false,
    true
    )

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 it does:

  1. Set Parameters: It defines two parameters: query_frequency (5 minutes) and query_period (2 days).

  2. Filter Risk Events: It retrieves risk events from the AADUserRiskEvents table that were generated within the last 2 days and are specifically from the "IdentityProtection" source with a "leakedCredentials" risk event type.

  3. Summarize Events: For each unique event (Id), it finds the first occurrence time and the most recent occurrence, keeping all details of the latest event.

  4. Filter Recent Events: It only keeps events where the first occurrence was within the last 5 minutes.

  5. Select Columns: It selects specific columns to include in the output, such as user details and risk information.

  6. Generate Alert Link: It creates an alternative alert link for each event using the user ID.

  7. Join with Security Alerts: It performs a left outer join with the SecurityAlert table to find related alerts from Azure Active Directory Identity Protection, matching on the event ID.

  8. Extend Alert Link: It ensures that each event has an alert link, using the alternative link if necessary.

  9. Filter Conditions: It filters out events based on specific conditions:

    • Excludes resolved alerts that are marked as "Closed".
    • Excludes events where the risk state is "remediated" and the risk detail indicates the user changed their password on-premises.

The result is a list of recent, unresolved user risk events related to leaked credentials, with relevant details and links to further investigate each event.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: August 28, 2025

Tables

AADUserRiskEventsSecurityAlert

Keywords

AADUserRiskEvents

Operators

letagowheresummarizeminarg_maxbyprojectextendstrcatjoinkindoncoalescecasetostringtodynamic

Actions