Detection rules › Kusto

Failed logon attempts in authpriv

Severity
medium
Time window
1d
Group by
Computer, HostIP, RemoteIP
Source
github.com/Azure/Azure-Sentinel

'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

TacticTechniques
Credential AccessT1110 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
Threshold
ge 15

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.

FieldKindValues
Facilityeq
  • authpriv
SyslogMessagematch
  • uid=0 transforms: term
  • authentication failure transforms: term
  • user unknown transforms: term
TotalLogonAttemptsge
  • 15 transforms: cased

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.

FieldSource
Computerproject
FirstLogonAttemptproject
HostIPproject
LatestLogonAttemptproject
SourceAddressproject
TimeBetweenLogonAttemptsproject
TotalLogonAttemptsproject