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

Sign-ins from IPs that attempt sign-ins to disabled accounts

Back
Id500c103a-0319-4d56-8e99-3cec8d860757
RulenameSign-ins from IPs that attempt sign-ins to disabled accounts
DescriptionIdentifies IPs with failed attempts to sign in to one or more disabled accounts using the IP through which successful signins from other accounts have happened.

This could indicate an attacker who obtained credentials for a list of accounts and is attempting to login with those accounts, some of which may have already been disabled.

References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes

50057 - User account is disabled. The account has been disabled by an administrator.

This query has also been updated to include UEBA logs IdentityInfo and BehaviorAnalytics for contextual information around the results.
SeverityMedium
TacticsInitialAccess
Persistence
TechniquesT1078
T1098
Required data connectorsAzureActiveDirectory
BehaviorAnalytics
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/SigninAttemptsByIPviaDisabledAccounts.yaml
Version2.1.3
Arm template500c103a-0319-4d56-8e99-3cec8d860757.json
Deploy To Azure
let aadFunc = (tableName: string) {
let failed_signins = table(tableName)
| where ResultType == "50057"
| where ResultDescription == "User account is disabled. The account has been disabled by an administrator.";
let disabled_users = failed_signins | summarize by UserPrincipalName;
table(tableName)
  | where ResultType == 0
  | where isnotempty(UserPrincipalName)
  | where UserPrincipalName !in (disabled_users)
| summarize
        successfulAccountsTargettedCount = dcount(UserPrincipalName),
        successfulAccountSigninSet = make_set(UserPrincipalName, 100),
        successfulApplicationSet = make_set(AppDisplayName, 100)
    by IPAddress, Type
    // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe
    | where successfulAccountsTargettedCount < 50
    | where isnotempty(successfulAccountsTargettedCount)
  | join kind=inner (failed_signins
| summarize
    StartTime = min(TimeGenerated),
    EndTime = max(TimeGenerated),
    totalDisabledAccountLoginAttempts = count(),
    disabledAccountsTargettedCount = dcount(UserPrincipalName),
    applicationsTargeted = dcount(AppDisplayName),
    disabledAccountSet = make_set(UserPrincipalName, 100),
    disabledApplicationSet = make_set(AppDisplayName, 100)
by IPAddress, Type
| order by totalDisabledAccountLoginAttempts desc) on IPAddress
| project StartTime, EndTime, IPAddress, totalDisabledAccountLoginAttempts, disabledAccountsTargettedCount, disabledAccountSet, disabledApplicationSet, successfulApplicationSet, successfulAccountsTargettedCount, successfulAccountSigninSet, Type
| order by totalDisabledAccountLoginAttempts};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
| join kind=leftouter (
    BehaviorAnalytics
    | where ActivityType in ("FailedLogOn", "LogOn")
    | where EventSource =~ "Azure AD"
    | project UsersInsights, DevicesInsights, ActivityInsights, InvestigationPriority, SourceIPAddress, UserPrincipalName
    | project-rename IPAddress = SourceIPAddress
    | summarize
        Users = make_set(UserPrincipalName, 100),
        UsersInsights = make_set(UsersInsights, 100),
        DevicesInsights = make_set(DevicesInsights, 100),
        IPInvestigationPriority = sum(InvestigationPriority)
    by IPAddress
) on IPAddress
| extend SFRatio = toreal(toreal(disabledAccountsTargettedCount)/toreal(successfulAccountsTargettedCount))
| where SFRatio >= 0.5
| sort by IPInvestigationPriority desc
tactics:
- InitialAccess
- Persistence
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/SigninAttemptsByIPviaDisabledAccounts.yaml
description: |
  'Identifies IPs with failed attempts to sign in to one or more disabled accounts using the IP through which successful signins from other accounts have happened.
  This could indicate an attacker who obtained credentials for a list of accounts and is attempting to login with those accounts, some of which may have already been disabled.
  References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
  50057 - User account is disabled. The account has been disabled by an administrator.
  This query has also been updated to include UEBA logs IdentityInfo and BehaviorAnalytics for contextual information around the results.'  
kind: Scheduled
severity: Medium
triggerThreshold: 0
query: |
  let aadFunc = (tableName: string) {
  let failed_signins = table(tableName)
  | where ResultType == "50057"
  | where ResultDescription == "User account is disabled. The account has been disabled by an administrator.";
  let disabled_users = failed_signins | summarize by UserPrincipalName;
  table(tableName)
    | where ResultType == 0
    | where isnotempty(UserPrincipalName)
    | where UserPrincipalName !in (disabled_users)
  | summarize
          successfulAccountsTargettedCount = dcount(UserPrincipalName),
          successfulAccountSigninSet = make_set(UserPrincipalName, 100),
          successfulApplicationSet = make_set(AppDisplayName, 100)
      by IPAddress, Type
      // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe
      | where successfulAccountsTargettedCount < 50
      | where isnotempty(successfulAccountsTargettedCount)
    | join kind=inner (failed_signins
  | summarize
      StartTime = min(TimeGenerated),
      EndTime = max(TimeGenerated),
      totalDisabledAccountLoginAttempts = count(),
      disabledAccountsTargettedCount = dcount(UserPrincipalName),
      applicationsTargeted = dcount(AppDisplayName),
      disabledAccountSet = make_set(UserPrincipalName, 100),
      disabledApplicationSet = make_set(AppDisplayName, 100)
  by IPAddress, Type
  | order by totalDisabledAccountLoginAttempts desc) on IPAddress
  | project StartTime, EndTime, IPAddress, totalDisabledAccountLoginAttempts, disabledAccountsTargettedCount, disabledAccountSet, disabledApplicationSet, successfulApplicationSet, successfulAccountsTargettedCount, successfulAccountSigninSet, Type
  | order by totalDisabledAccountLoginAttempts};
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt
  | join kind=leftouter (
      BehaviorAnalytics
      | where ActivityType in ("FailedLogOn", "LogOn")
      | where EventSource =~ "Azure AD"
      | project UsersInsights, DevicesInsights, ActivityInsights, InvestigationPriority, SourceIPAddress, UserPrincipalName
      | project-rename IPAddress = SourceIPAddress
      | summarize
          Users = make_set(UserPrincipalName, 100),
          UsersInsights = make_set(UsersInsights, 100),
          DevicesInsights = make_set(DevicesInsights, 100),
          IPInvestigationPriority = sum(InvestigationPriority)
      by IPAddress
  ) on IPAddress
  | extend SFRatio = toreal(toreal(disabledAccountsTargettedCount)/toreal(successfulAccountsTargettedCount))
  | where SFRatio >= 0.5
  | sort by IPInvestigationPriority desc  
entityMappings:
- fieldMappings:
  - identifier: Address
    columnName: IPAddress
  entityType: IP
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
- connectorId: AzureActiveDirectory
  dataTypes:
  - AADNonInteractiveUserSignInLogs
- connectorId: BehaviorAnalytics
  dataTypes:
  - BehaviorAnalytics
queryPeriod: 1d
status: Available
queryFrequency: 1d
name: Sign-ins from IPs that attempt sign-ins to disabled accounts
relevantTechniques:
- T1078
- T1098
id: 500c103a-0319-4d56-8e99-3cec8d860757
triggerOperator: gt
version: 2.1.3
{
  "$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/500c103a-0319-4d56-8e99-3cec8d860757')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/500c103a-0319-4d56-8e99-3cec8d860757')]",
      "properties": {
        "alertRuleTemplateName": "500c103a-0319-4d56-8e99-3cec8d860757",
        "customDetails": null,
        "description": "'Identifies IPs with failed attempts to sign in to one or more disabled accounts using the IP through which successful signins from other accounts have happened.\nThis could indicate an attacker who obtained credentials for a list of accounts and is attempting to login with those accounts, some of which may have already been disabled.\nReferences: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes\n50057 - User account is disabled. The account has been disabled by an administrator.\nThis query has also been updated to include UEBA logs IdentityInfo and BehaviorAnalytics for contextual information around the results.'\n",
        "displayName": "Sign-ins from IPs that attempt sign-ins to disabled accounts",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/SigninAttemptsByIPviaDisabledAccounts.yaml",
        "query": "let aadFunc = (tableName: string) {\nlet failed_signins = table(tableName)\n| where ResultType == \"50057\"\n| where ResultDescription == \"User account is disabled. The account has been disabled by an administrator.\";\nlet disabled_users = failed_signins | summarize by UserPrincipalName;\ntable(tableName)\n  | where ResultType == 0\n  | where isnotempty(UserPrincipalName)\n  | where UserPrincipalName !in (disabled_users)\n| summarize\n        successfulAccountsTargettedCount = dcount(UserPrincipalName),\n        successfulAccountSigninSet = make_set(UserPrincipalName, 100),\n        successfulApplicationSet = make_set(AppDisplayName, 100)\n    by IPAddress, Type\n    // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe\n    | where successfulAccountsTargettedCount < 50\n    | where isnotempty(successfulAccountsTargettedCount)\n  | join kind=inner (failed_signins\n| summarize\n    StartTime = min(TimeGenerated),\n    EndTime = max(TimeGenerated),\n    totalDisabledAccountLoginAttempts = count(),\n    disabledAccountsTargettedCount = dcount(UserPrincipalName),\n    applicationsTargeted = dcount(AppDisplayName),\n    disabledAccountSet = make_set(UserPrincipalName, 100),\n    disabledApplicationSet = make_set(AppDisplayName, 100)\nby IPAddress, Type\n| order by totalDisabledAccountLoginAttempts desc) on IPAddress\n| project StartTime, EndTime, IPAddress, totalDisabledAccountLoginAttempts, disabledAccountsTargettedCount, disabledAccountSet, disabledApplicationSet, successfulApplicationSet, successfulAccountsTargettedCount, successfulAccountSigninSet, Type\n| order by totalDisabledAccountLoginAttempts};\nlet aadSignin = aadFunc(\"SigninLogs\");\nlet aadNonInt = aadFunc(\"AADNonInteractiveUserSignInLogs\");\nunion isfuzzy=true aadSignin, aadNonInt\n| join kind=leftouter (\n    BehaviorAnalytics\n    | where ActivityType in (\"FailedLogOn\", \"LogOn\")\n    | where EventSource =~ \"Azure AD\"\n    | project UsersInsights, DevicesInsights, ActivityInsights, InvestigationPriority, SourceIPAddress, UserPrincipalName\n    | project-rename IPAddress = SourceIPAddress\n    | summarize\n        Users = make_set(UserPrincipalName, 100),\n        UsersInsights = make_set(UsersInsights, 100),\n        DevicesInsights = make_set(DevicesInsights, 100),\n        IPInvestigationPriority = sum(InvestigationPriority)\n    by IPAddress\n) on IPAddress\n| extend SFRatio = toreal(toreal(disabledAccountsTargettedCount)/toreal(successfulAccountsTargettedCount))\n| where SFRatio >= 0.5\n| sort by IPInvestigationPriority desc\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "InitialAccess",
          "Persistence"
        ],
        "techniques": [
          "T1078",
          "T1098"
        ],
        "templateVersion": "2.1.3",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}