Microsoft 365 password spray audit log

Severity highCloud & SaaSThresholdstablecloud.identity.m365_password_spray_audit_log · v1.0.0

MANY DIFFERENT accounts failing to sign in from ONE address — password spray, as it appears in the Unified Audit Log.

What it detects

MANY DIFFERENT accounts failing to sign in from ONE address — password spray, as it appears in the Unified Audit Log.

The pack already had a spray rule, and it could not see this. entra_password_spray keys on native_event_id: 50126, the AADSTS failure code, which exists only in the Entra SIGN-IN log. In the Unified Audit Log the same attack arrives as UserLoginFailed with no numeric code at all — native_event_id there is the RecordType — so a tenant that exported audit logs instead of sign-in logs got silence. Same attack, different evidence shape, and the rule only knew one.

Counts DISTINCT USERS rather than failures, which is what separates spray from brute force: brute force is many attempts against one account, spray is one or two attempts against many. A per-address failure count cannot tell them apart and needs a high threshold to avoid one user with a stale password; counting distinct victims is both more precise and fires earlier — real tooling (MSOLSpray, o365spray) walks a user list one attempt at a time, exactly to stay under per-account lockout.

MITRE ATT&CK mapping

  • T1110.003Brute Force: Password Spraying· Credential Access

False positives and triage

A shared egress address — VPN concentrator, office NAT, a mail gateway — pools unrelated users' failures and is the main false positive. Check the spread and the pacing: spray tools walk an alphabetical or list-ordered set of accounts at a machine-steady rate, while genuine failures behind a NAT cluster around a few repeat offenders at human intervals. Also check whether any account in the set later SUCCEEDED from the same address — that turns this from a nuisance into an intrusion, and is the first thing to answer.

The rule

Declarative YAML read by the detection engine — data, never executed. Confidence 65%. Source: rules/cloud/m365_password_spray_audit_log.yaml, licensed under the Apache License 2.0.

id: cloud.identity.m365_password_spray_audit_log
version: 1.0.0
status: stable
severity: high
confidence: 0.65
platforms: [application]
mitre_techniques: [T1110.003]
description: |
  MANY DIFFERENT accounts failing to sign in from ONE address — password spray,
  as it appears in the Unified Audit Log.

  The pack already had a spray rule, and it could not see this. `entra_password_spray`
  keys on `native_event_id: 50126`, the AADSTS failure code, which exists only in
  the Entra SIGN-IN log. In the Unified Audit Log the same attack arrives as
  `UserLoginFailed` with no numeric code at all — `native_event_id` there is the
  RecordType — so a tenant that exported audit logs instead of sign-in logs got
  silence. Same attack, different evidence shape, and the rule only knew one.

  Counts DISTINCT USERS rather than failures, which is what separates spray from
  brute force: brute force is many attempts against one account, spray is one or
  two attempts against many. A per-address failure count cannot tell them apart and
  needs a high threshold to avoid one user with a stale password; counting distinct
  victims is both more precise and fires earlier — real tooling (MSOLSpray,
  o365spray) walks a user list one attempt at a time, exactly to stay under
  per-account lockout.
match:
  event_category: authentication
  event_outcome: failure
  event_action_any:
    - UserLoginFailed
threshold:
  count: 5
  window_seconds: 3600
  distinct: user_name
  group_by: [source_ip]
correlation_keys: [source_ip]
false_positive_guidance: |
  A shared egress address — VPN concentrator, office NAT, a mail gateway — pools
  unrelated users' failures and is the main false positive. Check the spread and
  the pacing: spray tools walk an alphabetical or list-ordered set of accounts at a
  machine-steady rate, while genuine failures behind a NAT cluster around a few
  repeat offenders at human intervals. Also check whether any account in the set
  later SUCCEEDED from the same address — that turns this from a nuisance into an
  intrusion, and is the first thing to answer.

Related detections

Run this rule on your own logs — offline

KIROSEC runs this rule and the rest of the library over Windows, Linux, macOS and cloud audit logs on the analyst's own machine. Every alert links to the events that raised it, and nothing leaves the device.

Browse all detections