Back
Id9c1e9381-79dd-4ddf-9570-b73a1dc59fe0
RulenameAnomaly Sign In Event from an IP
DescriptionIdentifies sign-in anomalies from an IP in the last hour, targeting multiple users where the password is correct after multiple attempts
SeverityMedium
TacticsInitialAccess
TechniquesT1078
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1h
Query period1h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/Anomalies/SignInAnomaly.yaml
Version1.0.1
Arm template9c1e9381-79dd-4ddf-9570-b73a1dc59fe0.json
Deploy To Azure
let LookBack = 1h;
let Data = (
SigninLogs
| where TimeGenerated >= ago(LookBack)
| where parse_json(NetworkLocationDetails)[0].networkType != "trustedNamedLocation" // Excludes known tagged networks
// Counts the number of sign in events in the last hour every 15 minutes by IP
| make-series EventCounts = count() on TimeGenerated from ago(LookBack) to now() step 15m by IPAddress 
);
let AnomalyAlert = (
Data
| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(EventCounts,1.5,-1,'linefit')
| mv-expand EventCounts,TimeGenerated,Anomalies to typeof(double),Baseline to typeof(long),Score to typeof(double)
| where Anomalies > 0
);
AnomalyAlert
| join kind = inner (SigninLogs
| where TimeGenerated between (ago(LookBack) .. now())
| where parse_json(NetworkLocationDetails)[0].networkType != "trustedNamedLocation"
| extend PasswordResult = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail)
| summarize UserCount = dcount(UserPrincipalName), UserList = make_set(UserPrincipalName), AppName = make_set(AppDisplayName), PasswordResult = make_list(PasswordResult) by IPAddress) on IPAddress
| where PasswordResult has "Correct Password"
| where UserCount > 1 // looks for events targeting more than one user.
name: Anomaly Sign In Event from an IP
triggerOperator: gt
query: |
  let LookBack = 1h;
  let Data = (
  SigninLogs
  | where TimeGenerated >= ago(LookBack)
  | where parse_json(NetworkLocationDetails)[0].networkType != "trustedNamedLocation" // Excludes known tagged networks
  // Counts the number of sign in events in the last hour every 15 minutes by IP
  | make-series EventCounts = count() on TimeGenerated from ago(LookBack) to now() step 15m by IPAddress 
  );
  let AnomalyAlert = (
  Data
  | extend (Anomalies, Score, Baseline) = series_decompose_anomalies(EventCounts,1.5,-1,'linefit')
  | mv-expand EventCounts,TimeGenerated,Anomalies to typeof(double),Baseline to typeof(long),Score to typeof(double)
  | where Anomalies > 0
  );
  AnomalyAlert
  | join kind = inner (SigninLogs
  | where TimeGenerated between (ago(LookBack) .. now())
  | where parse_json(NetworkLocationDetails)[0].networkType != "trustedNamedLocation"
  | extend PasswordResult = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail)
  | summarize UserCount = dcount(UserPrincipalName), UserList = make_set(UserPrincipalName), AppName = make_set(AppDisplayName), PasswordResult = make_list(PasswordResult) by IPAddress) on IPAddress
  | where PasswordResult has "Correct Password"
  | where UserCount > 1 // looks for events targeting more than one user.
queryFrequency: 1h
description: |
  'Identifies sign-in anomalies from an IP in the last hour, targeting multiple users where the password is correct after multiple attempts'
id: 9c1e9381-79dd-4ddf-9570-b73a1dc59fe0
triggerThreshold: 0
queryPeriod: 1h
version: 1.0.1
kind: Scheduled
customDetails:
  Score: Score
  UserList: UserList
  UserCount: UserCount
  PasswordResult: PasswordResult
  AppName: AppName
  Baseline: Baseline
relevantTechniques:
- T1078
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
metadata:
  categories:
    domains:
    - Identity
  support:
    tier: Community
  author:
    name: Juanse
  source:
    kind: Community
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/Anomalies/SignInAnomaly.yaml
tactics:
- InitialAccess
entityMappings:
- fieldMappings:
  - identifier: Address
    columnName: IPAddress
  entityType: IP
{
  "$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/9c1e9381-79dd-4ddf-9570-b73a1dc59fe0')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/9c1e9381-79dd-4ddf-9570-b73a1dc59fe0')]",
      "properties": {
        "alertRuleTemplateName": "9c1e9381-79dd-4ddf-9570-b73a1dc59fe0",
        "customDetails": {
          "AppName": "AppName",
          "Baseline": "Baseline",
          "PasswordResult": "PasswordResult",
          "Score": "Score",
          "UserCount": "UserCount",
          "UserList": "UserList"
        },
        "description": "'Identifies sign-in anomalies from an IP in the last hour, targeting multiple users where the password is correct after multiple attempts'\n",
        "displayName": "Anomaly Sign In Event from an IP",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/Anomalies/SignInAnomaly.yaml",
        "query": "let LookBack = 1h;\nlet Data = (\nSigninLogs\n| where TimeGenerated >= ago(LookBack)\n| where parse_json(NetworkLocationDetails)[0].networkType != \"trustedNamedLocation\" // Excludes known tagged networks\n// Counts the number of sign in events in the last hour every 15 minutes by IP\n| make-series EventCounts = count() on TimeGenerated from ago(LookBack) to now() step 15m by IPAddress \n);\nlet AnomalyAlert = (\nData\n| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(EventCounts,1.5,-1,'linefit')\n| mv-expand EventCounts,TimeGenerated,Anomalies to typeof(double),Baseline to typeof(long),Score to typeof(double)\n| where Anomalies > 0\n);\nAnomalyAlert\n| join kind = inner (SigninLogs\n| where TimeGenerated between (ago(LookBack) .. now())\n| where parse_json(NetworkLocationDetails)[0].networkType != \"trustedNamedLocation\"\n| extend PasswordResult = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail)\n| summarize UserCount = dcount(UserPrincipalName), UserList = make_set(UserPrincipalName), AppName = make_set(AppDisplayName), PasswordResult = make_list(PasswordResult) by IPAddress) on IPAddress\n| where PasswordResult has \"Correct Password\"\n| where UserCount > 1 // looks for events targeting more than one user.\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "PT1H",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "InitialAccess"
        ],
        "techniques": [
          "T1078"
        ],
        "templateVersion": "1.0.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}