Azure Entra Sign-in Brute Force against Microsoft 365 Accounts

edit

Azure Entra Sign-in Brute Force against Microsoft 365 Accounts

edit

Identifies potential brute-force attempts against Microsoft 365 user accounts by detecting a high number of failed interactive or non-interactive login attempts within a 30-minute window. Attackers may attempt to brute force user accounts to gain unauthorized access to Microsoft 365 services via different services such as Exchange, SharePoint, or Teams.

Rule type: esql

Rule indices: None

Severity: medium

Risk score: 47

Runs every: 10m

Searches indices from: now-60m (Date Math format, see also Additional look-back time)

Maximum alerts per execution: 100

References:

Tags:

  • Domain: Cloud
  • Domain: SaaS
  • Data Source: Azure
  • Data Source: Entra ID
  • Data Source: Entra ID Sign-in
  • Use Case: Identity and Access Audit
  • Use Case: Threat Detection
  • Tactic: Credential Access

Version: 2

Rule authors:

  • Elastic

Rule license: Elastic License v2

Investigation guide

edit

This rule relies on Azure Entra ID sign-in logs, but filters for Microsoft 365 resources.

Rule query

edit
from logs-azure.signinlogs*
// truncate the timestamp to a 30-minute window
| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp)
| WHERE
  event.dataset == "azure.signinlogs"
  and event.category == "authentication"
  and to_lower(azure.signinlogs.properties.resource_display_name) rlike "(.*)365(.*)"
  and azure.signinlogs.category in ("NonInteractiveUserSignInLogs", "SignInLogs")
  and event.outcome != "success"
  // for tuning review azure.signinlogs.properties.status.error_code
  // https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes

// keep only relevant fields
| keep target_time_window, event.dataset, event.category, azure.signinlogs.properties.resource_display_name, azure.signinlogs.category, event.outcome, azure.signinlogs.properties.user_principal_name, source.ip

// count the number of login sources and failed login attempts
| stats
  login_source_count = count(source.ip),
  failed_login_count = count(*) by target_time_window, azure.signinlogs.properties.user_principal_name

// filter for users with more than 20 login sources or failed login attempts
| where (login_source_count >= 20 or failed_login_count >= 20)

Framework: MITRE ATT&CKTM