Detection rules › Kusto

Password spray attack against ADFSSignInLogs

Status
available
Severity
medium
Time window
1h
Group by
IPAddress
Source
github.com/Azure/Azure-Sentinel

'Identifies evidence of password spray activity against Connect Health for AD FS sign-in events by looking for failures from multiple accounts from the same IP address within a time window. Reference: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/ADFSSignInLogsPasswordSpray.md'

MITRE ATT&CK coverage

TacticTechniques
Credential Access

Rule body

id: 5533fe80-905e-49d5-889a-df27d2c3976d
name: Password spray attack against ADFSSignInLogs
description: |
  'Identifies evidence of password spray activity against Connect Health for AD FS sign-in events by looking for failures from multiple accounts from the same IP address within a time window. Reference: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/ADFSSignInLogsPasswordSpray.md'
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - ADFSSignInLogs
severity: Medium
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let queryfrequency = 30m;
  let accountthreshold = 10;
  let successCodes = dynamic([0, 50144]);
  ADFSSignInLogs
  | extend IngestionTime = ingestion_time()
  | where IngestionTime > ago(queryfrequency)
  | where not(todynamic(AuthenticationDetails)[0].authenticationMethod == "Integrated Windows Authentication")
  | summarize
      DistinctFailureCount = dcountif(UserPrincipalName, ResultType !in (successCodes)),
      DistinctSuccessCount = dcountif(UserPrincipalName, ResultType in (successCodes)),
      SuccessAccounts = make_set_if(UserPrincipalName, ResultType in (successCodes), 250),
      arg_min(TimeGenerated, *)
      by IPAddress
  | where DistinctFailureCount > DistinctSuccessCount and DistinctFailureCount >= accountthreshold
  //| extend SuccessAccounts = iff(array_length(SuccessAccounts) != 0, SuccessAccounts, dynamic(["null"]))
  //| mv-expand SuccessAccounts
  | project TimeGenerated, Category, OperationName, IPAddress, DistinctFailureCount, DistinctSuccessCount, SuccessAccounts, AuthenticationRequirement, ConditionalAccessStatus, IsInteractive, UserAgent, NetworkLocationDetails, DeviceDetail, TokenIssuerType, TokenIssuerName, ResourceIdentity
entityMappings:
- entityType: IP
  fieldMappings:
    - identifier: Address
      columnName: IPAddress
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let queryfrequency = 30m;
let accountthreshold = 10;
let successCodes = dynamic([0, 50144]);

Stage 1: source

ADFSSignInLogs

Stage 2: extend

| extend IngestionTime = ingestion_time()

Stage 3: where

| where IngestionTime > ago(queryfrequency)

Stage 4: where

| where not(todynamic(AuthenticationDetails)[0].authenticationMethod == "Integrated Windows Authentication")

Stage 5: summarize

| summarize
    DistinctFailureCount = dcountif(UserPrincipalName, ResultType !in (successCodes)),
    DistinctSuccessCount = dcountif(UserPrincipalName, ResultType in (successCodes)),
    SuccessAccounts = make_set_if(UserPrincipalName, ResultType in (successCodes), 250),
    arg_min(TimeGenerated, *)
    by IPAddress

Stage 6: where

| where DistinctFailureCount > DistinctSuccessCount and DistinctFailureCount >= accountthreshold

Stage 7: project

| project TimeGenerated, Category, OperationName, IPAddress, DistinctFailureCount, DistinctSuccessCount, SuccessAccounts, AuthenticationRequirement, ConditionalAccessStatus, IsInteractive, UserAgent, NetworkLocationDetails, DeviceDetail, TokenIssuerType, TokenIssuerName, ResourceIdentity

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
authenticationMethodeqIntegrated Windows Authenticationexcludes:authenticationMethod field:"authenticationMethod" value:"Integrated Windows Authentication"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
AuthenticationRequirementproject
Categoryproject
ConditionalAccessStatusproject
DeviceDetailproject
DistinctFailureCountproject
DistinctSuccessCountproject
IPAddressproject
IsInteractiveproject
NetworkLocationDetailsproject
OperationNameproject
ResourceIdentityproject
SuccessAccountsproject
TimeGeneratedproject
TokenIssuerNameproject
TokenIssuerTypeproject
UserAgentproject