Detection rules › Kusto

Potential Password Spray Attack

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

'This query searches for failed attempts to log in from more than 15 various users within a 5 minutes timeframe from the same source. This is a potential indication of a password spray attack.'

MITRE ATT&CK coverage

TacticTechniques
Credential AccessT1110 Brute Force

Rule body kusto

id: 64d16e62-1a17-4a35-9ea7-2b9fe6f07118
name: Potential Password Spray Attack
description: |
  'This query searches for failed attempts to log in from more than 15 various users within a 5 minutes timeframe from the same source. This is a potential indication of a password spray attack.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SalesforceServiceCloudCCPDefinition
    dataTypes:
      - SalesforceServiceCloud
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let FailureThreshold = 15;  
  SalesforceServiceCloud
  | where EventType =~ 'Login' and  LoginStatus != 'LOGIN_NO_ERROR'
  | where LoginStatus  in~ ('LOGIN_ERROR_INVALID_PASSWORD', 'LOGIN_ERROR_SSO_PWD_INVALID')
  | extend TimestampDerived = todatetime(TimestampDerived)
  | summarize UserCount=dcount(UserId), Users = make_set(UserId,100) by ClientIp, bin(TimestampDerived, 5m)
  | where UserCount > FailureThreshold
customDetails:
  Users: Users
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

let FailureThreshold = 15;

Stage 1: source

SalesforceServiceCloud

Stage 2: where

| where EventType =~ 'Login' and  LoginStatus != 'LOGIN_NO_ERROR'

Stage 3: where

| where LoginStatus  in~ ('LOGIN_ERROR_INVALID_PASSWORD', 'LOGIN_ERROR_SSO_PWD_INVALID')

Stage 4: extend

| extend TimestampDerived = todatetime(TimestampDerived)

Stage 5: summarize

| summarize UserCount=dcount(UserId), Users = make_set(UserId,100) by ClientIp, bin(TimestampDerived, 5m)
Threshold
gt 15

Stage 6: where

| where UserCount > FailureThreshold

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
EventTypeeq
  • Login
LoginStatusin
  • LOGIN_ERROR_INVALID_PASSWORD
  • LOGIN_ERROR_SSO_PWD_INVALID
LoginStatusne
  • LOGIN_NO_ERROR transforms: cased
UserCountgt
  • 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
ClientIpsummarize
UserCountsummarize
Userssummarize