Adversaries may be attempting to brute force access to a system by repeatedly failing to log in with multiple accounts before eventually succeeding. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential account compromise and prevent unauthorized access.
KQL Query
DeviceLogonEvents
| where isnotempty(RemoteIP)
and AccountName !endswith "$"
and RemoteIPType == "Public"
| extend Account=strcat(AccountDomain, "\\", AccountName)
| summarize
Successful=countif(ActionType == "LogonSuccess"),
Failed = countif(ActionType == "LogonFailed"),
FailedAccountsCount = dcountif(Account, ActionType == "LogonFailed"),
SuccessfulAccountsCount = dcountif(Account, ActionType == "LogonSuccess"),
FailedAccounts = makeset(iff(ActionType == "LogonFailed", Account, ""), 5),
SuccessfulAccounts = makeset(iff(ActionType == "LogonSuccess", Account, ""), 5)
by DeviceName, RemoteIP, RemoteIPType
| where Failed > 10 and Successful > 0 and FailedAccountsCount > 2 and SuccessfulAccountsCount == 1
id: ab619659-ab7c-4ca4-be0c-ca71a07bf4cd
name: Account brute force
description: |
Query #1: Look for public IP addresses that failed to logon to a computer multiple times, using multiple accounts, and eventually succeeded.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceLogonEvents
query: |
DeviceLogonEvents
| where isnotempty(RemoteIP)
and AccountName !endswith "$"
and RemoteIPType == "Public"
| extend Account=strcat(AccountDomain, "\\", AccountName)
| summarize
Successful=countif(ActionType == "LogonSuccess"),
Failed = countif(ActionType == "LogonFailed"),
FailedAccountsCount = dcountif(Account, ActionType == "LogonFailed"),
SuccessfulAccountsCount = dcountif(Account, ActionType == "LogonSuccess"),
FailedAccounts = makeset(iff(ActionType == "LogonFailed", Account, ""), 5),
SuccessfulAccounts = makeset(iff(ActionType == "LogonSuccess", Account, ""), 5)
by DeviceName, RemoteIP, RemoteIPType
| where Failed > 10 and Successful > 0 and FailedAccountsCount > 2 and SuccessfulAccountsCount == 1
| Sentinel Table | Notes |
|---|---|
DeviceLogonEvents | Ensure this data connector is enabled |
Scenario: A system administrator is using a script to test SSH access to a server for configuration validation.
Filter/Exclusion: Exclude IP addresses associated with known internal or trusted management tools (e.g., ssh-keygen, ansible, puppet), or add a filter for source_ip in (trusted_admin_ips).
Scenario: A scheduled job is attempting to connect to a remote server using multiple credentials to verify account lockout thresholds.
Filter/Exclusion: Exclude connections made by scheduled tasks (e.g., schtasks.exe, crontab, systemd) or use a filter for process_name in ('schtasks.exe', 'crontab', 'systemd').
Scenario: A developer is using a password manager to test login credentials for a local development environment.
Filter/Exclusion: Exclude local IP addresses (e.g., 127.0.0.1, ::1) or filter by source_ip not in (local_network_range).
Scenario: A security tool is performing a vulnerability scan using multiple credentials to check for weak passwords.
Filter/Exclusion: Exclude IP addresses associated with security tools (e.g., nmap, nessus, openvas) or use a filter for process_name in ('nmap.exe', 'nessus', 'openvas').
Scenario: A user is attempting to log in to a server using multiple accounts to troubleshoot a login issue.
Filter/Exclusion: Exclude logins from users with elevated privileges (e.g., Administrator, root) or filter by user_name in ('Administrator', 'root', 'svc_account').