Query Details
id: e8f4a6b7-c2d3-5e9f-0a1b-2c3d4e5f6a7b
name: Non-Interactive Brute Force - Single User Targeted by Multiple IPs
version: 1.0.0
kind: Scheduled
description: |
Detects targeted brute force / credential stuffing attacks against a single user account
via non-interactive sign-ins, where the attacker uses multiple IP addresses to avoid
rate limiting. The attack manifests as many authentication failures with wrong-password
or locked-account error codes across distributed source IPs targeting the same user.
MITRE ATT&CK: T1110 (Brute Force), T1110.001 (Password Guessing), T1110.004 (Credential Stuffing)
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- InitialAccess
relevantTechniques:
- T1110
query: |
let BruteForceErrors = dynamic([
"50126", // InvalidPasswordOrUsernameOrTenantInvalid
"50055", // InvalidPasswordExpiredPassword
"50056", // InvalidPasswordNullPassword
"50064", // CredentialAuthenticationError
"50053", // IdsLocked (account locked)
"50034", // UserAccountNotFound
"50057", // UserDisabled
"50128" // InvalidTenantName
]);
AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(1h)
| extend ErrorCode = tostring(ResultType)
| where ErrorCode in (BruteForceErrors)
| summarize
FailCount = count(),
UniqueIPs = dcount(IPAddress),
IPs = make_set(IPAddress, 20),
Countries = make_set(Location),
ErrorCodes = make_set(ErrorCode),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by UserPrincipalName
| where FailCount > 20
| extend IPAddress = tostring(IPs[0])
| order by FailCount desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
customDetails:
FailureCount: FailCount
UniqueIPs: UniqueIPs
Countries: Countries
ErrorCodes: ErrorCodes
alertDetailsOverride:
alertDisplayNameFormat: "Brute Force Attack - {{UserPrincipalName}} received {{FailCount}} failures from {{UniqueIPs}} IPs"
alertDescriptionFormat: "Account {{UserPrincipalName}} received {{FailCount}} non-interactive sign-in failures from {{UniqueIPs}} distinct IP addresses. Matches a distributed brute force or credential stuffing pattern."
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AnyAlert
groupByEntities:
- Account
groupByAlertDetails: []
groupByCustomDetails: []
This query is designed to detect potential brute force or credential stuffing attacks on a single user account. Here's a simple breakdown of what it does:
Purpose: It identifies situations where multiple IP addresses are used to attempt to log into a single user account unsuccessfully. This is a common tactic to bypass rate limiting and indicates a possible attack.
Data Source: The query uses logs from Azure Active Directory, specifically focusing on non-interactive sign-in attempts.
Error Codes: It looks for specific error codes that indicate failed login attempts, such as incorrect passwords or locked accounts.
Time Frame: The query checks for these events within the last hour.
Criteria for Alert:
Output: For each user account that meets the criteria, the query provides:
Alerting: If the criteria are met, an alert is generated. The alert includes details like the number of failures and the number of IPs involved, and it is designed to help security teams quickly identify and respond to potential attacks.
Incident Management: The system can automatically create an incident if such an attack pattern is detected, helping streamline the response process. It groups incidents by user account to manage related alerts effectively.
Overall, this query helps in identifying and responding to distributed brute force attacks targeting specific user accounts by analyzing failed login attempts from various IP addresses.

David Alonso
Released: May 29, 2026
Tables
Keywords
Operators