CodeCommitsIssuesPull requestsActionsInsightsSecurity
master

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Detections/SigninLogs/AnomalousUserAppSigninLocationIncrease-detection.yaml

82lines · modecode

1id: 7cb8f77d-c52f-4e46-b82f-3cf2e106224a
2name: Anomalous sign-in location by user account and authenticating application
3description: |
4 'This query over Azure Active Directory sign-in considers all user sign-ins for each Azure Active
5 Directory application and picks out the most anomalous change in location profile for a user within an
6 individual application. An alert is generated for recent sign-ins that have location counts that are anomalous
7 over last day but also over the last 3-day and 7-day periods.
8 Please note that on workspaces with larger volume of Signin data (~10M+ events a day) may timeout when using this default query time period.
9 It is recommended that you test and tune this appropriately for the workspace.'
10severity: Medium
11requiredDataConnectors:
12 - connectorId: AzureActiveDirectory
13 dataTypes:
14 - SigninLogs
15 - connectorId: AzureActiveDirectory
16 dataTypes:
17 - AADNonInteractiveUserSignInLogs
18queryFrequency: 1d
19queryPeriod: 14d
20triggerOperator: gt
21triggerThreshold: 0
22tactics:
23 - InitialAccess
24relevantTechniques:
25 - T1078
26query: |
27
28 let lookBack_long = 7d;
29 let lookBack_med = 3d;
30 let lookBack = 1d;
31 let aadFunc = (tableName:string){
32 table(tableName)
33 | where TimeGenerated >= startofday(ago(lookBack_long))
34 | extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
35 | extend locationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city), ";")
36 | project TimeGenerated, AppDisplayName , UserPrincipalName, locationString
37 // Create time series
38 | make-series dLocationCount = dcount(locationString) on TimeGenerated in range(startofday(ago(lookBack_long)),now(), 1d)
39 by UserPrincipalName, AppDisplayName
40 // Compute best fit line for each entry
41 | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dLocationCount)
42 // Chart the 3 most interesting lines
43 // A 0-value slope corresponds to an account being completely stable over time for a given Azure Active Directory application
44 | where Slope > 0.3
45 | top 50 by Slope desc
46 | join kind = leftsemi (
47 table(tableName)
48 | where TimeGenerated >= startofday(ago(lookBack_med))
49 | extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
50 | extend locationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city), ";")
51 | project TimeGenerated, AppDisplayName , UserPrincipalName, locationString
52 | make-series dLocationCount = dcount(locationString) on TimeGenerated in range(startofday(ago(lookBack_med)) ,now(), 1d)
53 by UserPrincipalName, AppDisplayName
54 | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dLocationCount)
55 | where Slope > 0.3
56 | top 50 by Slope desc
57 ) on UserPrincipalName, AppDisplayName
58 | join kind = leftsemi (
59 table(tableName)
60 | where TimeGenerated >= startofday(ago(lookBack))
61 | extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
62 | extend locationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city), ";")
63 | project TimeGenerated, AppDisplayName , UserPrincipalName, locationString
64 | make-series dLocationCount = dcount(locationString) on TimeGenerated in range(startofday(ago(lookBack)) ,now(), 1d)
65 by UserPrincipalName, AppDisplayName
66 | extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dLocationCount)
67 | where Slope > 5
68 | top 50 by Slope desc
69 // Higher threshold requirement on last day anomaly
70 ) on UserPrincipalName, AppDisplayName
71 | extend timestamp = TimeGenerated, AccountCustomEntity = UserPrincipalName
72 };
73 let aadSignin = aadFunc("SigninLogs");
74 let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
75 union isfuzzy=true aadSignin, aadNonInt
76entityMappings:
77 - entityType: Account
78 fieldMappings:
79 - identifier: FullName
80 columnName: AccountCustomEntity
81version: 1.0.0
82kind: Scheduled