Analytic rule catalog
New country signIn with correct password
Back
| Id | 7808c05a-3afd-4d13-998a-a59e2297693f |
| Rulename | New country signIn with correct password |
| Description | Identifies an interrupted sign-in session from a country the user has not sign-in before in the last 7 days, where the password was correct. Although the session is interrupted by other controls such as multi factor authentication or conditional access policies, the user credentials should be reset due to logs indicating a correct password was observed during sign-in. |
| Severity | Medium |
| Tactics | InitialAccess CredentialAccess |
| Techniques | T1078 T1110 |
| Required data connectors | AzureActiveDirectory |
| Kind | Scheduled |
| Query frequency | 1d |
| Query period | 7d |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SigninLogs/NewCountryValidCreds.yaml |
| Version | 1.0.2 |
| Arm template | 7808c05a-3afd-4d13-998a-a59e2297693f.json |
// Creating a list of successful sign-in by users in the last 7 days.
let KnownUserCountry = (
SigninLogs
| where TimeGenerated between (ago(7d) .. ago(1d) )
| where ResultType == 0
| summarize KnownCountry = make_set(Location,1048576) by UserPrincipalName
);
// Identify sign-ins that are no successful but have the auth details indicating a correct password.
SigninLogs
| where TimeGenerated >= ago(1d)
| where ResultType != 0
| extend ParseAuth = parse_json(AuthenticationDetails)
| extend AuthMethod = tostring(ParseAuth.[0].authenticationMethod),
PasswordResult = tostring(ParseAuth.[0].authenticationStepResultDetail),
AuthSucceeded = tostring(ParseAuth.[0].succeeded)
| where PasswordResult == "Correct Password" or AuthSucceeded == "true"
| where AuthMethod == "Password"
| extend failureReason = tostring(Status.failureReason)
| summarize NewCountry = make_set(Location,1048576), LastObservedTime = max(TimeGenerated), AppName = make_set(AppDisplayName,1048576) by UserPrincipalName, PasswordResult, AuthSucceeded, failureReason
// Combining both tables by user
| join kind=inner KnownUserCountry on UserPrincipalName
// Compare both arrays and identify if the country has been observed in the past.
| extend CountryDiff = set_difference(NewCountry,KnownCountry)
| extend CountryDiffCount = array_length(CountryDiff)
// Count the new column to only alert if there is a difference between both arrays
| where CountryDiffCount != 0
| extend NewCountryEvent = CountryDiff
// Getting UserName and Domain
| extend Name = split(UserPrincipalName,"@",0),
Domain = split(UserPrincipalName,"@",1)
| mv-expand Name,Domain
tactics:
- InitialAccess
- CredentialAccess
triggerThreshold: 0
relevantTechniques:
- T1078
- T1110
queryPeriod: 7d
name: New country signIn with correct password
customDetails:
NewCountryEvent: NewCountryEvent
AppName: AppName
AuthSucceeded: AuthSucceeded
LastObservedTime: LastObservedTime
PasswordResult: PasswordResult
failureReason: failureReason
description: |
'Identifies an interrupted sign-in session from a country the user has not sign-in before in the last 7 days, where the password was correct. Although the session is interrupted by other controls such as multi factor authentication or conditional access policies, the user credentials should be reset due to logs indicating a correct password was observed during sign-in.'
eventGroupingSettings:
aggregationKind: SingleAlert
metadata:
source:
kind: Community
support:
tier: Community
author:
name: Juanse
categories:
domains:
- Identity
- Security - Threat Protection
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SigninLogs/NewCountryValidCreds.yaml
kind: Scheduled
entityMappings:
- fieldMappings:
- columnName: UserPrincipalName
identifier: FullName
- columnName: Name
identifier: Name
- columnName: Domain
identifier: NTDomain
entityType: Account
triggerOperator: gt
id: 7808c05a-3afd-4d13-998a-a59e2297693f
severity: Medium
version: 1.0.2
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
query: |
// Creating a list of successful sign-in by users in the last 7 days.
let KnownUserCountry = (
SigninLogs
| where TimeGenerated between (ago(7d) .. ago(1d) )
| where ResultType == 0
| summarize KnownCountry = make_set(Location,1048576) by UserPrincipalName
);
// Identify sign-ins that are no successful but have the auth details indicating a correct password.
SigninLogs
| where TimeGenerated >= ago(1d)
| where ResultType != 0
| extend ParseAuth = parse_json(AuthenticationDetails)
| extend AuthMethod = tostring(ParseAuth.[0].authenticationMethod),
PasswordResult = tostring(ParseAuth.[0].authenticationStepResultDetail),
AuthSucceeded = tostring(ParseAuth.[0].succeeded)
| where PasswordResult == "Correct Password" or AuthSucceeded == "true"
| where AuthMethod == "Password"
| extend failureReason = tostring(Status.failureReason)
| summarize NewCountry = make_set(Location,1048576), LastObservedTime = max(TimeGenerated), AppName = make_set(AppDisplayName,1048576) by UserPrincipalName, PasswordResult, AuthSucceeded, failureReason
// Combining both tables by user
| join kind=inner KnownUserCountry on UserPrincipalName
// Compare both arrays and identify if the country has been observed in the past.
| extend CountryDiff = set_difference(NewCountry,KnownCountry)
| extend CountryDiffCount = array_length(CountryDiff)
// Count the new column to only alert if there is a difference between both arrays
| where CountryDiffCount != 0
| extend NewCountryEvent = CountryDiff
// Getting UserName and Domain
| extend Name = split(UserPrincipalName,"@",0),
Domain = split(UserPrincipalName,"@",1)
| mv-expand Name,Domain
queryFrequency: 1d
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/7808c05a-3afd-4d13-998a-a59e2297693f')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/7808c05a-3afd-4d13-998a-a59e2297693f')]",
"properties": {
"alertRuleTemplateName": "7808c05a-3afd-4d13-998a-a59e2297693f",
"customDetails": {
"AppName": "AppName",
"AuthSucceeded": "AuthSucceeded",
"failureReason": "failureReason",
"LastObservedTime": "LastObservedTime",
"NewCountryEvent": "NewCountryEvent",
"PasswordResult": "PasswordResult"
},
"description": "'Identifies an interrupted sign-in session from a country the user has not sign-in before in the last 7 days, where the password was correct. Although the session is interrupted by other controls such as multi factor authentication or conditional access policies, the user credentials should be reset due to logs indicating a correct password was observed during sign-in.'\n",
"displayName": "New country signIn with correct password",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "Domain",
"identifier": "NTDomain"
}
]
}
],
"eventGroupingSettings": {
"aggregationKind": "SingleAlert"
},
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SigninLogs/NewCountryValidCreds.yaml",
"query": "// Creating a list of successful sign-in by users in the last 7 days.\nlet KnownUserCountry = (\nSigninLogs\n| where TimeGenerated between (ago(7d) .. ago(1d) ) \n| where ResultType == 0\n| summarize KnownCountry = make_set(Location,1048576) by UserPrincipalName\n);\n// Identify sign-ins that are no successful but have the auth details indicating a correct password.\nSigninLogs\n| where TimeGenerated >= ago(1d)\n| where ResultType != 0\n| extend ParseAuth = parse_json(AuthenticationDetails)\n| extend AuthMethod = tostring(ParseAuth.[0].authenticationMethod),\n PasswordResult = tostring(ParseAuth.[0].authenticationStepResultDetail),\n AuthSucceeded = tostring(ParseAuth.[0].succeeded)\n| where PasswordResult == \"Correct Password\" or AuthSucceeded == \"true\"\n| where AuthMethod == \"Password\"\n| extend failureReason = tostring(Status.failureReason)\n| summarize NewCountry = make_set(Location,1048576), LastObservedTime = max(TimeGenerated), AppName = make_set(AppDisplayName,1048576) by UserPrincipalName, PasswordResult, AuthSucceeded, failureReason\n// Combining both tables by user\n| join kind=inner KnownUserCountry on UserPrincipalName\n// Compare both arrays and identify if the country has been observed in the past.\n| extend CountryDiff = set_difference(NewCountry,KnownCountry)\n| extend CountryDiffCount = array_length(CountryDiff)\n// Count the new column to only alert if there is a difference between both arrays\n| where CountryDiffCount != 0\n| extend NewCountryEvent = CountryDiff\n// Getting UserName and Domain\n| extend Name = split(UserPrincipalName,\"@\",0),\n Domain = split(UserPrincipalName,\"@\",1)\n| mv-expand Name,Domain\n",
"queryFrequency": "P1D",
"queryPeriod": "P7D",
"severity": "Medium",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CredentialAccess",
"InitialAccess"
],
"techniques": [
"T1078",
"T1110"
],
"templateVersion": "1.0.2",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}