Multiple Verified Threat Actor IP
Query
let query_frequency = 5m;
let query_period = 2d;
AADUserRiskEvents
| where TimeGenerated > ago(query_period)
| where Source == "IdentityProtection" and RiskEventType == "nationStateIP"
| 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 detection type indicates sign-in activity that is consistent with known IP addresses associated with nation state actors or cyber crime groups, based on Microsoft Security Intelligence (MSTIC)",
AltAlertSeverity = strcat(toupper(substring(RiskLevel, 0, 1)), substring(RiskLevel, 1)),
AltTactics = "InitialAccess",
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 == "NationStateIP"
| 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))
| as _Events
| lookup kind=leftouter (
SigninLogs
| where TimeGenerated > ago(query_period)
//| where RiskEventTypes_V2 has "estsNationStateIP"
| where OriginalRequestId in (toscalar(_Events | summarize make_list(RequestId)))
| extend TimeReceived = _TimeReceived
| summarize arg_max(TimeReceived, *) by OriginalRequestId
| project
TimeGenerated,
CreatedDateTime,
Type,
//UserDisplayName,
//UserPrincipalName,
//UserId,
AlternateSignInName,
SignInIdentifier,
UserType,
IPAddress,
AutonomousSystemNumber,
Location,
NetworkLocationDetails,
ResultType,
ResultSignature,
ResultDescription,
ClientAppUsed,
AppDisplayName,
ResourceDisplayName,
DeviceDetail,
UserAgent,
Status,
MfaDetail,
AuthenticationContextClassReferences,
AuthenticationDetails,
AuthenticationProcessingDetails,
AuthenticationProtocol,
AuthenticationRequirement,
AuthenticationRequirementPolicies,
SessionLifetimePolicies,
//TokenIssuerType,
IncomingTokenType,
TokenProtectionStatusDetails,
ConditionalAccessStatus,
ConditionalAccessPolicies,
SignInLogs_RiskDetail = RiskDetail,
RiskEventTypes_V2,
RiskLevelAggregated,
RiskLevelDuringSignIn,
SignInLogs_RiskState = RiskState,
HomeTenantId,
ResourceTenantId,
CrossTenantAccessType,
AppId,
ResourceIdentity,
UniqueTokenIdentifier,
SessionId,
OriginalRequestId//,
//CorrelationId
) on $left.RequestId == $right.OriginalRequestId
// | where case(
// AlertStatus == "Resolved" and tostring(todynamic(ExtendedProperties)["State"]) == "Closed", false,
// RiskState == "dismissed" and RiskDetail == "aiConfirmedSigninSafe", false,
// RiskState == "remediated" and RiskDetail == "userChangedPasswordOnPremises", false,
// SignInLogs_RiskState == "remediated" and SignInLogs_RiskDetail == "userPassedMFADrivenByRiskBasedPolicy", 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,
TimeGenerated,
CreatedDateTime,
Type,
//UserDisplayName,
//UserPrincipalName,
//UserId,
AlternateSignInName,
SignInIdentifier,
UserType,
IPAddress,
AutonomousSystemNumber,
Location,
NetworkLocationDetails,
ResultType,
ResultSignature,
ResultDescription,
ClientAppUsed,
AppDisplayName,
ResourceDisplayName,
DeviceDetail,
UserAgent,
Status,
MfaDetail,
AuthenticationContextClassReferences,
AuthenticationDetails,
AuthenticationProcessingDetails,
AuthenticationProtocol,
AuthenticationRequirement,
AuthenticationRequirementPolicies,
SessionLifetimePolicies,
//TokenIssuerType,
IncomingTokenType,
TokenProtectionStatusDetails,
ConditionalAccessStatus,
ConditionalAccessPolicies,
SignInLogs_RiskDetail,
RiskEventTypes_V2,
RiskLevelAggregated,
RiskLevelDuringSignIn,
SignInLogs_RiskState,
HomeTenantId,
ResourceTenantId,
CrossTenantAccessType,
AppId,
ResourceIdentity,
UniqueTokenIdentifier,
SessionId//,
//OriginalRequestId,
//CorrelationIdExplanation
This KQL query is designed to analyze and correlate security risk events related to Azure Active Directory (AAD) user sign-ins that are potentially associated with nation-state actors. Here's a simplified breakdown of what the query does:
-
Define Time Periods:
query_frequencyis set to 5 minutes.query_periodis set to 2 days.
-
Filter Risk Events:
- The query starts by filtering
AADUserRiskEventsto include only those generated in the last 2 days (query_period) and specifically from the "IdentityProtection" source with a risk event type of "nationStateIP".
- The query starts by filtering
-
Summarize Events:
- It summarizes these events to find the first time each event was generated and keeps the latest details for each unique event ID.
-
Filter Recent Events:
- Further filters to include only those events that were first generated in the last 5 minutes (
query_frequency).
- Further filters to include only those events that were first generated in the last 5 minutes (
-
Project Relevant Fields:
- Selects specific fields to work with, including user and event details.
-
Process Additional Info:
- Extracts and processes additional information from the
AdditionalInfofield to create a structured data bag.
- Extracts and processes additional information from the
-
Create Alternative Descriptions:
- Constructs alternative alert links, descriptions, severity levels, tactics, and techniques based on the risk level and additional information.
-
Join with Security Alerts:
- Joins the processed risk events with
SecurityAlertdata to enrich the information with alert details, using the event ID as a key.
- Joins the processed risk events with
-
Extend and Coalesce Data:
- Extends the dataset with additional fields and uses
coalesceto fill in missing data with alternative values.
- Extends the dataset with additional fields and uses
-
Lookup Sign-in Logs:
- Looks up related sign-in logs to gather more context about the user sign-ins associated with these risk events.
-
Project Final Output:
- Projects a comprehensive list of fields from both the risk events and sign-in logs, providing a detailed view of each incident.
In summary, this query identifies and analyzes recent AAD user risk events linked to nation-state IPs, enriches them with additional alert and sign-in information, and formats the output for further investigation or reporting.
Details

Jose Sebastián Canós
Released: October 7, 2025
Tables
Keywords
Operators