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

Failed AzureAD logons but success logon to host

Back
Id8ee967a2-a645-4832-85f4-72b635bcb3a6
RulenameFailed AzureAD logons but success logon to host
DescriptionIdentifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Microsoft Entra ID.

Uses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.
SeverityMedium
TacticsInitialAccess
CredentialAccess
TechniquesT1078
T1110
Required data connectorsAzureActiveDirectory
SecurityEvents
Syslog
WindowsForwardedEvents
WindowsSecurityEvents
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/AADHostLoginCorrelation.yaml
Version1.3.1
Arm template8ee967a2-a645-4832-85f4-72b635bcb3a6.json
Deploy To Azure
//Adjust this threshold to fit the environment
let signin_threshold = 5;
//Make a list of all IPs with failed signins to AAD above our threshold
let aadFunc = (tableName:string){
let suspicious_signins =
table(tableName)
| where ResultType !in ("0", "50125", "50140")
| where IPAddress !in ('127.0.0.1', '::1')
| summarize count() by IPAddress
| where count_ > signin_threshold
| summarize make_set(IPAddress);
//See if any of these IPs have sucessfully logged into *nix hosts
let linux_logons =
Syslog
| where Facility contains "auth" and ProcessName != "sudo"
| where SyslogMessage has "Accepted"
| extend SourceIP = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
| where SourceIP in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from IP address"
| project TimeGenerated, Computer, HostIP, IpAddress = SourceIP, SyslogMessage, Facility, ProcessName, Reason;
//See if any of these IPs have sucessfully logged into Windows hosts
let win_logons = (union isfuzzy=true
(SecurityEvent
| where EventID == 4624
| where LogonType in (10, 7, 3)
| where IpAddress != "-"
| where IpAddress in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from IP address"
| project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, LogonTypeName, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
),
(WindowsEvent
| where EventID == 4624 and has_any_ipv4(EventData, toscalar(suspicious_signins))
| extend LogonType = tostring(EventData.LogonType)
| where LogonType in (10, 7, 3)
| extend  IpAddress = tostring(EventData.IpAddress)
| where IpAddress != "-"
| where IpAddress in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from IP address"
| extend Activity = "4624 - An account was successfully logged on."
| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
| extend Account =  strcat(TargetDomainName,"\\", TargetUserName)
| extend TargetUserSid = tostring(EventData.TargetUserSid)
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
| extend AccountType =case(Account endswith "$" or TargetUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(TargetUserSid), "", "User")
| extend LogonProcessName = tostring(EventData.LogonProcessName)
| project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
)
);
union isfuzzy=true linux_logons,win_logons
| extend timestamp = TimeGenerated
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex+1), Computer)
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: Account
    identifier: FullName
  - columnName: TargetUserName
    identifier: Name
  - columnName: TargetDomainName
    identifier: NTDomain
- entityType: Host
  fieldMappings:
  - columnName: Computer
    identifier: FullName
  - columnName: HostName
    identifier: HostName
  - columnName: HostNameDomain
    identifier: NTDomain
- entityType: Host
  fieldMappings:
  - columnName: _ResourceId
    identifier: AzureID
- entityType: IP
  fieldMappings:
  - columnName: IpAddress
    identifier: Address
tactics:
- InitialAccess
- CredentialAccess
triggerOperator: gt
description: |
  'Identifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Microsoft Entra ID.
  Uses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.'  
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
- connectorId: AzureActiveDirectory
  dataTypes:
  - AADNonInteractiveUserSignInLogs
- connectorId: SecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: WindowsSecurityEvents
  dataTypes:
  - SecurityEvents
- connectorId: WindowsForwardedEvents
  dataTypes:
  - WindowsEvent
relevantTechniques:
- T1078
- T1110
version: 1.3.1
id: 8ee967a2-a645-4832-85f4-72b635bcb3a6
queryPeriod: 1d
kind: Scheduled
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/AADHostLoginCorrelation.yaml
queryFrequency: 1d
severity: Medium
name: Failed AzureAD logons but success logon to host
query: |
  //Adjust this threshold to fit the environment
  let signin_threshold = 5;
  //Make a list of all IPs with failed signins to AAD above our threshold
  let aadFunc = (tableName:string){
  let suspicious_signins =
  table(tableName)
  | where ResultType !in ("0", "50125", "50140")
  | where IPAddress !in ('127.0.0.1', '::1')
  | summarize count() by IPAddress
  | where count_ > signin_threshold
  | summarize make_set(IPAddress);
  //See if any of these IPs have sucessfully logged into *nix hosts
  let linux_logons =
  Syslog
  | where Facility contains "auth" and ProcessName != "sudo"
  | where SyslogMessage has "Accepted"
  | extend SourceIP = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
  | where SourceIP in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | project TimeGenerated, Computer, HostIP, IpAddress = SourceIP, SyslogMessage, Facility, ProcessName, Reason;
  //See if any of these IPs have sucessfully logged into Windows hosts
  let win_logons = (union isfuzzy=true
  (SecurityEvent
  | where EventID == 4624
  | where LogonType in (10, 7, 3)
  | where IpAddress != "-"
  | where IpAddress in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, LogonTypeName, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
  ),
  (WindowsEvent
  | where EventID == 4624 and has_any_ipv4(EventData, toscalar(suspicious_signins))
  | extend LogonType = tostring(EventData.LogonType)
  | where LogonType in (10, 7, 3)
  | extend  IpAddress = tostring(EventData.IpAddress)
  | where IpAddress != "-"
  | where IpAddress in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | extend Activity = "4624 - An account was successfully logged on."
  | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
  | extend Account =  strcat(TargetDomainName,"\\", TargetUserName)
  | extend TargetUserSid = tostring(EventData.TargetUserSid)
  | extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
  | extend AccountType =case(Account endswith "$" or TargetUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(TargetUserSid), "", "User")
  | extend LogonProcessName = tostring(EventData.LogonProcessName)
  | project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
  )
  );
  union isfuzzy=true linux_logons,win_logons
  | extend timestamp = TimeGenerated
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex+1), Computer)
  };
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt  
metadata:
  support:
    tier: Community
  categories:
    domains:
    - Security - Others
    - Identity
  source:
    kind: Community
  author:
    name: Microsoft Security Research
{
  "$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/8ee967a2-a645-4832-85f4-72b635bcb3a6')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/8ee967a2-a645-4832-85f4-72b635bcb3a6')]",
      "properties": {
        "alertRuleTemplateName": "8ee967a2-a645-4832-85f4-72b635bcb3a6",
        "customDetails": null,
        "description": "'Identifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Microsoft Entra ID.\nUses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.'\n",
        "displayName": "Failed AzureAD logons but success logon to host",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Account",
                "identifier": "FullName"
              },
              {
                "columnName": "TargetUserName",
                "identifier": "Name"
              },
              {
                "columnName": "TargetDomainName",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "Computer",
                "identifier": "FullName"
              },
              {
                "columnName": "HostName",
                "identifier": "HostName"
              },
              {
                "columnName": "HostNameDomain",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "_ResourceId",
                "identifier": "AzureID"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IpAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/AADHostLoginCorrelation.yaml",
        "query": "//Adjust this threshold to fit the environment\nlet signin_threshold = 5;\n//Make a list of all IPs with failed signins to AAD above our threshold\nlet aadFunc = (tableName:string){\nlet suspicious_signins =\ntable(tableName)\n| where ResultType !in (\"0\", \"50125\", \"50140\")\n| where IPAddress !in ('127.0.0.1', '::1')\n| summarize count() by IPAddress\n| where count_ > signin_threshold\n| summarize make_set(IPAddress);\n//See if any of these IPs have sucessfully logged into *nix hosts\nlet linux_logons =\nSyslog\n| where Facility contains \"auth\" and ProcessName != \"sudo\"\n| where SyslogMessage has \"Accepted\"\n| extend SourceIP = extract(\"(([0-9]{1,3})\\\\.([0-9]{1,3})\\\\.([0-9]{1,3})\\\\.(([0-9]{1,3})))\",1,SyslogMessage)\n| where SourceIP in (suspicious_signins)\n| extend Reason = \"Multiple failed AAD logins from IP address\"\n| project TimeGenerated, Computer, HostIP, IpAddress = SourceIP, SyslogMessage, Facility, ProcessName, Reason;\n//See if any of these IPs have sucessfully logged into Windows hosts\nlet win_logons = (union isfuzzy=true\n(SecurityEvent\n| where EventID == 4624\n| where LogonType in (10, 7, 3)\n| where IpAddress != \"-\"\n| where IpAddress in (suspicious_signins)\n| extend Reason = \"Multiple failed AAD logins from IP address\"\n| project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, LogonTypeName, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason\n),\n(WindowsEvent\n| where EventID == 4624 and has_any_ipv4(EventData, toscalar(suspicious_signins))\n| extend LogonType = tostring(EventData.LogonType)\n| where LogonType in (10, 7, 3)\n| extend  IpAddress = tostring(EventData.IpAddress)\n| where IpAddress != \"-\"\n| where IpAddress in (suspicious_signins)\n| extend Reason = \"Multiple failed AAD logins from IP address\"\n| extend Activity = \"4624 - An account was successfully logged on.\"\n| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)\n| extend Account =  strcat(TargetDomainName,\"\\\\\", TargetUserName)\n| extend TargetUserSid = tostring(EventData.TargetUserSid)\n| extend TargetAccount = strcat(EventData.TargetDomainName,\"\\\\\", EventData.TargetUserName)\n| extend AccountType =case(Account endswith \"$\" or TargetUserSid in (\"S-1-5-18\", \"S-1-5-19\", \"S-1-5-20\"), \"Machine\", isempty(TargetUserSid), \"\", \"User\")\n| extend LogonProcessName = tostring(EventData.LogonProcessName)\n| project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason\n)\n);\nunion isfuzzy=true linux_logons,win_logons\n| extend timestamp = TimeGenerated\n| extend HostName = tostring(split(Computer, \".\")[0]), DomainIndex = toint(indexof(Computer, '.'))\n| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex+1), Computer)\n};\nlet aadSignin = aadFunc(\"SigninLogs\");\nlet aadNonInt = aadFunc(\"AADNonInteractiveUserSignInLogs\");\nunion isfuzzy=true aadSignin, aadNonInt\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess",
          "InitialAccess"
        ],
        "techniques": [
          "T1078",
          "T1110"
        ],
        "templateVersion": "1.3.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}