Query Details

Token Replay Of Workload Identity From Outside Of Azure Network Range

Query

// Requires PrivilegedWorkloadIdentityInfo function and watchlist WorkloadIdentityInfo. See Thomas Naunheim blog for more details.
// https://www.cloud-architekt.net/entra-workload-id-advanced-detection-enrichment/#publish-watchlist-workloadidentityinfo-with-sentinelenrichment
// https://github.com/Cloud-Architekt/AzureSentinel/blob/main/Functions/PrivilegedWorkloadIdentityInfo.yaml
let AzureRanges = 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 AddressFamily = case(isnotnull(isipv4), "v4", "v6")
    | summarize IPRange=make_set(values_properties_addressPrefixes) by AddressFamily;
let AzureRangesIPv4 = toscalar(AzureRanges
    | where AddressFamily == "v4"
    | project IPRange);
let AzureRangesIPv6 = toscalar(AzureRanges
    | where AddressFamily == "v6"
    | project IPRange);
AzureActivity
| where parse_json(tostring(Authorization_d.evidence)).principalType == "ServicePrincipal"
| extend ClaimsObjectIdentifier = parse_json(Claims).["http://schemas.microsoft.com/identity/claims/objectidentifier"] 
| extend parsedClaims = parse_json(Claims_d)
| where ActivityStatusValue == "Success" and ActivitySubstatusValue == "OK"
| project
    TimeGenerated,
    CorrelationId,
    OperationName,
    ResourceProviderValue,
    _ResourceId,
    ActivityIpAddress = CallerIpAddress,
    ApplicationId = tostring(parsedClaims.appid),
    Uti = tostring(parsedClaims.uti),
    ActivityStatus
| join kind=inner (AADManagedIdentitySignInLogs
    | project
        ConditionalAccessPolicies,
        ConditionalAccessStatus,
        ServicePrincipalCredentialKeyId,
        UniqueTokenIdentifier
    )
    on $left.Uti == $right.UniqueTokenIdentifier
| extend IsInAzurev4Range = ipv4_is_in_any_range(ActivityIpAddress, AzureRangesIPv4)
| extend IsInAzurev6Range = ipv6_is_in_any_range(ActivityIpAddress, AzureRangesIPv6)
| extend IpAddressType = iff(IsInAzurev4Range or IsInAzurev6Range, "Azure Public IP", "None Azure IP")
| where IpAddressType == "None Azure IP"
| where isnotempty(ApplicationId)
| join kind=leftouter(
    PrivilegedWorkloadIdentityInfo
    | project
        WorkloadIdentityName,
        WorkloadIdentityType,
        ApplicationObjectId,
        ServicePrincipalObjectId,
        ApplicationId,
        IsFirstPartyApp,
        EntraIdRoles,
        AppRolePermissions,
        WorkloadIdClassification = EnterpriseAccessModelTiering
    )
    on ApplicationId

Explanation

This query detects a token replay attack by checking if a workload identity is used to access Azure resources from an IP address outside of Azure's IP ranges. It retrieves Azure IP ranges, filters the activity logs for successful operations by service principals, and checks if the IP address is within Azure IP ranges. If the IP address is not within Azure IP ranges and the application ID is not empty, it joins the activity logs with privileged workload identity information. The query also includes incident configuration and entity mappings for incident creation and grouping.

Details

Fabian Bader profile picture

Fabian Bader

Released: January 23, 2024

Tables

AzureActivityAADManagedIdentitySignInLogsPrivilegedWorkloadIdentityInfo

Keywords

DevicesIntuneUserAzureIPIdentityTokenReplayAttackAzureActivityAADManagedIdentitySignInLogsPrivilegedWorkloadIdentityInfoWorkloadIdentityNameWorkloadIdentityTypeApplicationObjectIdServicePrincipalObjectIdApplicationIdIsFirstPartyAppEntraIdRolesAppRolePermissionsWorkloadIdClassification

Operators

externaldatawithsummarizeextendcasetoscalarwhereprojectjoiniffisnotemptyipv4_is_in_any_rangeipv6_is_in_any_rangeisnotnullparse_ipv4parse_ipv6make_setparse_json

Severity

High

Tactics

PersistencePrivilegeEscalation

MITRE Techniques

Frequency: 1h

Period: 1h

Actions

GitHub