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,
AlertLinkExplanation
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:
-
Define Time Parameters:
query_frequencyis set to 5 minutes.query_periodis set to 2 days.
-
Filter Risk Events:
- It retrieves events from the
AADUserRiskEventstable that occurred within the last 2 days. - It specifically looks for events where the source is "IdentityProtection" and the risk event type is "leakedCredentials".
- It retrieves events from the
-
Summarize Events:
- It summarizes the data to find the first occurrence of each event by
Idand keeps the most recent details.
- It summarizes the data to find the first occurrence of each event by
-
Filter Recent Events:
- It only keeps events that first occurred within the last 5 minutes.
-
Project Relevant Columns:
- It selects specific columns to keep for further processing, excluding some like
IpAddress,RequestId, andCorrelationId.
- It selects specific columns to keep for further processing, excluding some like
-
Process Additional Information:
- It processes the
AdditionalInfofield to create a structured bag of key-value pairs.
- It processes the
-
Create Alternative Fields:
- It constructs alternative alert links, descriptions, severity, tactics, techniques, and sub-techniques based on the processed data.
-
Join with Security Alerts:
- It performs a left outer join with the
SecurityAlerttable to correlate risk events with existing security alerts. - It matches records based on
Idfrom the risk events andVendorOriginalIdfrom the security alerts.
- It performs a left outer join with the
-
Extend and Coalesce Fields:
- It fills in missing alert details using alternative fields created earlier if the original data is unavailable.
-
Filter Out Resolved or Remediated Events:
- It excludes events that are resolved and closed or remediated by a password change.
-
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
Released: October 7, 2025
Tables
Keywords
Operators