Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Anomaly Sign In Event from an IP

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.
severity: Medium
relevantTechniques:
- T1078
customDetails:
  Score: Score
  UserCount: UserCount
  UserList: UserList
  PasswordResult: PasswordResult
  AppName: AppName
  Baseline: Baseline
queryFrequency: 1h
kind: Scheduled
version: 1.0.1
metadata:
  support:
    tier: Community
  categories:
    domains:
    - Identity
  author:
    name: Juanse
  source:
    kind: Community
triggerOperator: gt
description: |
    'Identifies sign-in anomalies from an IP in the last hour, targeting multiple users where the password is correct after multiple attempts'
queryPeriod: 1h
id: 9c1e9381-79dd-4ddf-9570-b73a1dc59fe0
entityMappings:
- entityType: IP
  fieldMappings:
  - columnName: IPAddress
    identifier: Address
tactics:
- InitialAccess
name: Anomaly Sign In Event from an IP
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/Anomalies/SignInAnomaly.yaml
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.  
requiredDataConnectors:
- dataTypes:
  - SigninLogs
  connectorId: AzureActiveDirectory
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2023-02-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",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "InitialAccess"
        ],
        "techniques": [
          "T1078"
        ],
        "templateVersion": "1.0.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}