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.
entityMappings:
- entityType: IP
  fieldMappings:
  - columnName: IPAddress
    identifier: Address
tactics:
- InitialAccess
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'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
relevantTechniques:
- T1078
version: 1.0.1
id: 9c1e9381-79dd-4ddf-9570-b73a1dc59fe0
customDetails:
  Score: Score
  UserCount: UserCount
  UserList: UserList
  PasswordResult: PasswordResult
  Baseline: Baseline
  AppName: AppName
queryPeriod: 1h
kind: Scheduled
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/Anomalies/SignInAnomaly.yaml
queryFrequency: 1h
severity: Medium
name: Anomaly Sign In Event from an IP
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.  
metadata:
  support:
    tier: Community
  categories:
    domains:
    - Identity
  source:
    kind: Community
  author:
    name: Juanse
{
  "$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"
    }
  ]
}