Token Replay from workload identity with privileges in Microsoft Entra or Microsoft 365 (WorkloadIdentityInfo)
Token Replay From Workload Identity With Privileges In Microsoft 365 Workload Identity Info
Query
let azure_ranges = externaldata(changeNumber: string, cloud: string, values: dynamic)
["https://raw.githubusercontent.com/microsoft/mstic/master/PublicFeeds/MSFTIPRanges/ServiceTags_Public.json"] with(format='multijson')
| mv-expand values
| mv-expand values.properties.addressPrefixes
| mv-expand values_properties_addressPrefixes
| summarize by tostring(values_properties_addressPrefixes)
| extend isipv4 = parse_ipv4(values_properties_addressPrefixes)
| extend isipv6 = parse_ipv6(values_properties_addressPrefixes)
| extend ip_type = case(isnotnull(isipv4), "v4", "v6")
| summarize make_list(values_properties_addressPrefixes) by ip_type;
MicrosoftGraphActivityLogs
| project TimeGenerated, RequestId, ApiVersion, RequestMethod, ResponseStatusCode, ActivityIpAddress = IPAddress, UserAgent, RequestUri, Roles, AppId, Wids, SignInActivityId, ApplicationId = AppId, Scopes, Location
| join kind=inner (union AADServicePrincipalSignInLogs, AADManagedIdentitySignInLogs
| project ConditionalAccessPolicies, ConditionalAccessStatus, ServicePrincipalCredentialKeyId, SignInIpAddress = IPAddress, UniqueTokenIdentifier, Type
// Correlation between Activity and Sign-in based on Unique Token Identifier
) on $left.SignInActivityId == $right.UniqueTokenIdentifier
// AADManagedIdentitySignInLogs does not include SignInIpAddress
| where ActivityIpAddress != SignInIpAddress and isnotempty(SignInIpAddress)
| extend isipv4 = parse_ipv4(ActivityIpAddress)
| extend ip_type = case(isnotnull(isipv4), "v4", "v6")
| join kind=fullouter (azure_ranges) on ip_type
| extend ipv6_match = ipv6_is_in_any_range(ActivityIpAddress, list_values_properties_addressPrefixes)
| extend ipv4_match = ipv4_is_in_any_range(ActivityIpAddress, list_values_properties_addressPrefixes)
| extend IpAddressType = iff(ipv4_match or ipv6_match, "Azure Public IP", "None Azure IP")
| where isnotempty(ApplicationId)
| join kind=leftouter(
PrivilegedWorkloadIdentityInfo
| project
WorkloadIdentityName,
WorkloadIdentityType,
ApplicationObjectId,
ServicePrincipalObjectId,
ApplicationId,
IsFirstPartyApp,
EntraIdRoles,
AppRolePermissions,
WorkloadIdClassification = EnterpriseAccessModelTiering
)
on ApplicationId
| extend Severity = iff(WorkloadIdClassification contains "ControlPlane", "High", "Medium")Explanation
This query detects a token replay attack on Microsoft Graph API. It checks if a token has been used from a different IP address than during the sign-in process. The severity is set to medium, but it can be high if the workload identity has privileges on the Control Plane. The query frequency and period are both set to 1 hour. The query retrieves Azure IP ranges and then joins the MicrosoftGraphActivityLogs with AADServicePrincipalSignInLogs and AADManagedIdentitySignInLogs to correlate activity and sign-in based on a unique token identifier. It filters out activity with different IP addresses and joins with PrivilegedWorkloadIdentityInfo to get additional information. The query also includes incident configuration and alert details override.