cloudflare/Azure-Sentinel
Publicmirrored fromhttps://github.com/cloudflare/Azure-Sentinel
Detections/SigninLogs/AuthenticationAttemptfromNewCountry.yaml
50lines · modecode
Added new detections and hunting queries for the AAD Sec Ops Guide Removed URL localization from queries Repleaced incorrect 'scheduled' with 'Scheduled'9bcef98
3 years ago
| 1 | id: ef895ada-e8e8-4cf0-9313-b1ab67fab69f |
| 2 | name: Authentication Attempt from New Country |
| 3 | description: | |
| 4 | 'Detects when there is a log in attempt from a country that has not seen a successful login in the previous 14 days. |
| 5 | Threat actors may attempt to authenticate with credentials from compromised accounts - monitoring attempts from anomalous locations may help identify these attempts. |
| 6 | Authentication attempts should be investigated to ensure the activity was legitimate and if there is other similar activity. |
| 7 | Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#monitoring-for-failed-unusual-sign-ins' |
| 8 | severity: Medium |
| 9 | requiredDataConnectors: |
| 10 | - connectorId: AzureActiveDirectory |
| 11 | dataTypes: |
| 12 | - SigninLogs |
| 13 | - AADNonInteractiveUserSignInLogs |
| 14 | queryFrequency: 1d |
| 15 | queryPeriod: 14d |
| 16 | triggerOperator: gt |
| 17 | triggerThreshold: 0 |
| 18 | tactics: |
| 19 | - InitialAccess |
| 20 | relevantTechniques: |
| 21 | - T1078.004 |
| 22 | tags: |
| 23 | - AADSecOpsGuide |
| 24 | query: | |
| 25 | let known_locations = |
| 26 | union isfuzzy=True AADNonInteractiveUserSignInLogs, SigninLogs |
| 27 | | where TimeGenerated between(ago(14d)..ago(1d)) |
| 28 | | where ResultType == 0 |
| 29 | | summarize by Location; |
| 30 | union isfuzzy=True AADNonInteractiveUserSignInLogs, SigninLogs |
| 31 | | where TimeGenerated > ago(1d) |
| 32 | | where ResultType != 50126 |
| 33 | | where Location !in (known_locations) |
| 34 | | extend LocationDetails_dynamic = column_ifexists("LocationDetails_dynamic", "") |
| 35 | | extend DeviceDetail_dynamic = column_ifexists("DeviceDetail_dynamic", "") |
| 36 | | extend LocationDetails = iif(isnotempty(LocationDetails_dynamic), LocationDetails_dynamic, parse_json(LocationDetails_string)) |
| 37 | | extend DeviceDetail = iif(isnotempty(DeviceDetail_dynamic), DeviceDetail_dynamic, parse_json(DeviceDetail_string)) |
| 38 | | extend City = tostring(LocationDetails.city) |
| 39 | | extend State = tostring(LocationDetails.state) |
| 40 | | extend Place = strcat(City, " - ", State) |
| 41 | | extend DeviceId = tostring(DeviceDetail.deviceId) |
| 42 | | extend Result = strcat(tostring(ResultType), " - ", ResultDescription) |
| 43 | | summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), make_set(Result), make_set(IPAddress), make_set(UserAgent), make_set(Place), make_set(DeviceId) by UserPrincipalName, Location, Category |
| 44 | entityMappings: |
| 45 | - entityType: Account |
| 46 | fieldMappings: |
| 47 | - identifier: FullName |
| 48 | columnName: UserPrincipalName |
| 49 | version: 1.0.0 |
| 50 | kind: Scheduled |