Anomalous sign-in location by user account and authenticating application


Description

'This query examines Microsoft Entra ID sign-ins for each application and identifies the most anomalous change in a user's location profile. The goal is to detect user account compromise, possibly via a specific application vector.'

Query · kql

SigninLogs 
// Forces Log Analytics to recognize that the query should be run over full time range
| extend  locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/", 
tostring(LocationDetails["state"]), "/", tostring(LocationDetails["city"]), ";") 
| project TimeGenerated, AppDisplayName, UserPrincipalName, locationString 
// Create time series 
| make-series dLocationCount = dcount(locationString) on TimeGenerated step 1d
by UserPrincipalName, AppDisplayName 
// Compute best fit line for each entry 
| extend (RSquare, Slope, Variance, RVariance, Interception, LineFit) = series_fit_line(dLocationCount) 
// Chart the 3 most interesting lines  
// A 0-value slope corresponds to an account being completely stable over time for a given Azure Active Directory application
| top 3 by Slope desc
| extend timestamp = TimeGenerated, AccountCustomEntity = UserPrincipalName 
| render timechart
Raw source Anomalous sign-in location by user account and authenticating application · KQL
Esc
Published by Azure/Azure-Sentinel ↗, licensed under MIT ↗. Reproduced here unmodified.
id: 8159c663-6724-41b8-9ae8-b328aa8d0c4c
name: Anomalous sign-in location by user account and authenticating application
description: |
  'This query examines Microsoft Entra ID sign-ins for each application and identifies the most anomalous change in a user's location profile. The goal is to detect user account compromise, possibly via a specific application vector.'
description_detailed: |
  'This query over Microsoft Entra ID sign-in considers all user sign-ins for each Azure Active 
  Directory application and picks out the most anomalous change in location profile for a user within an 
  individual application. The intent is to hunt for user account compromise, possibly via a specific application
  vector.'
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |

  SigninLogs 
  // Forces Log Analytics to recognize that the query should be run over full time range
  | extend  locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/", 
  tostring(LocationDetails["state"]), "/", tostring(LocationDetails["city"]), ";") 
  | project TimeGenerated, AppDisplayName, UserPrincipalName, locationString 
  // Create time series 
  | make-series dLocationCount = dcount(locationString) on TimeGenerated step 1d
  by UserPrincipalName, AppDisplayName 
  // Compute best fit line for each entry 
  | extend (RSquare, Slope, Variance, RVariance, Interception, LineFit) = series_fit_line(dLocationCount) 
  // Chart the 3 most interesting lines  
  // A 0-value slope corresponds to an account being completely stable over time for a given Azure Active Directory application
  | top 3 by Slope desc
  | extend timestamp = TimeGenerated, AccountCustomEntity = UserPrincipalName 
  | render timechart

entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
version: 1.0.1
metadata:
    source:
        kind: Community
    author:
        name: Shain
    support:
        tier: Community
    categories:
        domains: [ "Security - Other", "Identity" ]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.