Detection rules › Kusto
Failed logon attempts in authpriv
'Identifies failed logon attempts from unknown users in Syslog authpriv logs. The unknown user means the account that tried to log in isn't provisioned on the machine. A few hits could indicate someone attempting to access a machine they aren't authorized to access. If there are many of hits, especially from outside your network, it could indicate a brute force attack. Default threshold for logon attempts is 15.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | T1110 Brute Force |
Rule body kusto
id: e7ec9fa6-e7f7-41ed-a34b-b956837a3ee6
name: Failed logon attempts in authpriv
description: |
'Identifies failed logon attempts from unknown users in Syslog authpriv logs. The unknown user means the account that tried to log in isn't provisioned on the machine. A few hits could indicate someone attempting to access a machine they aren't authorized to access.
If there are many of hits, especially from outside your network, it could indicate a brute force attack.
Default threshold for logon attempts is 15.'
severity: Medium
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
let threshold = 15;
// Below pulls messages from syslog-authpriv logs where there was an authentication failure with an unknown user.
// IP address of system attempting logon is also extracted from the SyslogMessage field. Some of these messages
// are aggregated.
Syslog
| where Facility =~ "authpriv"
| where SyslogMessage has "authentication failure" and SyslogMessage has " uid=0"
| extend RemoteIP = extract(@".*?rhost=([\d.]+).*?", 1,SyslogMessage)
| project TimeGenerated, Computer, ProcessName, HostIP, RemoteIP, ProcessID
| join kind=innerunique (
// Below pulls messages from syslog-authpriv logs that show each instance an unknown user tried to logon.
Syslog
| where Facility =~ "authpriv"
| where SyslogMessage has "user unknown"
| project Computer, HostIP, ProcessID
) on Computer, HostIP, ProcessID
// Count the number of failed logon attempts by External IP and internal machine
| summarize FirstLogonAttempt = min(TimeGenerated), LatestLogonAttempt = max(TimeGenerated), TotalLogonAttempts = count() by Computer, HostIP, RemoteIP
// Calculate the time between first and last logon attempt (AttemptPeriodLength)
| extend TimeBetweenLogonAttempts = LatestLogonAttempt - FirstLogonAttempt
| where TotalLogonAttempts >= threshold
| project FirstLogonAttempt, LatestLogonAttempt, TimeBetweenLogonAttempts, TotalLogonAttempts, SourceAddress = RemoteIP, Computer, HostIP
| sort by Computer asc nulls last
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- entityType: IP
fieldMappings:
- identifier: Address
columnName: HostIP
version: 1.0.5
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 15;
Stage 1: source
Syslog
Stage 2: where
| where Facility =~ "authpriv"
Stage 3: where
| where SyslogMessage has "authentication failure" and SyslogMessage has " uid=0"
Stage 4: extend
| extend RemoteIP = extract(@".*?rhost=([\d.]+).*?", 1,SyslogMessage)
Stage 5: project
| project TimeGenerated, Computer, ProcessName, HostIP, RemoteIP, ProcessID
Stage 6: join
| join kind=innerunique (
Syslog
| where Facility =~ "authpriv"
| where SyslogMessage has "user unknown"
| project Computer, HostIP, ProcessID
) on Computer, HostIP, ProcessID
Stage 7: summarize
| summarize FirstLogonAttempt = min(TimeGenerated), LatestLogonAttempt = max(TimeGenerated), TotalLogonAttempts = count() by Computer, HostIP, RemoteIP
Stage 8: extend
| extend TimeBetweenLogonAttempts = LatestLogonAttempt - FirstLogonAttempt
Stage 9: where
| where TotalLogonAttempts >= threshold
Stage 10: project
| project FirstLogonAttempt, LatestLogonAttempt, TimeBetweenLogonAttempts, TotalLogonAttempts, SourceAddress = RemoteIP, Computer, HostIP
Stage 11: sort
| sort by Computer asc nulls last
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
Facility | eq |
|
SyslogMessage | match |
|
TotalLogonAttempts | ge |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
Computer | project |
FirstLogonAttempt | project |
HostIP | project |
LatestLogonAttempt | project |
SourceAddress | project |
TimeBetweenLogonAttempts | project |
TotalLogonAttempts | project |