Loading

Suspicious Microsoft 365 UserLoggedIn via OAuth Code

Identifies sign-ins on behalf of a principal user to the Microsoft Graph API from multiple IPs using the Microsoft Authentication Broker or Visual Studio Code application. This behavior may indicate an adversary using a phished OAuth refresh token.

Rule type: esql
Rule indices:

Rule Severity: high
Risk Score: 73
Runs every: 59m
Searches indices from: now-60m
Maximum alerts per execution: ?
References:

Tags:

  • Domain: Cloud
  • Domain: Email
  • Domain: Identity
  • Data Source: Microsoft 365
  • Data Source: Microsoft 365 Audit Logs
  • Use Case: Identity and Access Audit
  • Use Case: Threat Detection
  • Resources: Investigation Guide
  • Tactic: Defense Evasion

Version: ?
Rule authors:

  • Elastic

Rule license: Elastic License v2

The Office 365 Logs Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.

  • o365.audit.UserId: The identity value the application is acting on behalf of principal user.
  • unique_ips: Analyze the list of unique IP addresses used within the 30-minute window. Determine whether these originate from different geographic regions, cloud providers, or anonymizing infrastructure (e.g., Tor or VPNs).
  • target_time_window: Use the truncated time window to pivot into raw events to reconstruct the full sequence of resource access events, including exact timestamps and service targets.
  • azure.auditlogs to check for device join or registration events around the same timeframe.
  • azure.identityprotection to identify correlated risk detections, such as anonymized IP access or token replay.
  • Any additional sign-ins from the ips involved, even outside the broker, to determine if tokens have been reused elsewhere.
  • Developers or IT administrators working across environments may also produce similar behavior.
  • If confirmed unauthorized, revoke all refresh tokens for the affected user and remove any devices registered during this session.
  • Notify the user and determine whether the device join or authentication activity was expected.
  • Audit Conditional Access and broker permissions (29d9ed98-a469-4536-ade2-f981bc1d605e) to ensure policies enforce strict access controls.
  • Consider blocking token-based reauthentication to Microsoft Graph and DRS from suspicious locations or user agents.
  • Continue monitoring for follow-on activity like lateral movement or privilege escalation.
from logs-o365.audit-*
| where
    event.dataset == "o365.audit" and
    event.action == "UserLoggedIn" and
    source.ip is not null and
    o365.audit.UserId is not null and
    o365.audit.ApplicationId is not null and
    o365.audit.UserType in ("0", "2", "3", "10") and
    o365.audit.ApplicationId in ("aebc6443-996d-45c2-90f0-388ff96faa56", "29d9ed98-a469-4536-ade2-f981bc1d605e") and
    o365.audit.ObjectId in ("00000003-0000-0000-c000-000000000000")
| eval
    Esql.time_window_date_trunc = date_trunc(30 minutes, @timestamp),
    Esql.oauth_authorize_user_id_case = case(
        o365.audit.ExtendedProperties.RequestType == "OAuth2:Authorize" and o365.audit.ExtendedProperties.ResultStatusDetail == "Redirect",
        o365.audit.UserId,
        null
    ),
    Esql.oauth_token_user_id_case = case(
        o365.audit.ExtendedProperties.RequestType == "OAuth2:Token",
        o365.audit.UserId,
        null
    )
| stats
    Esql.source_ip_count_distinct = count_distinct(source.ip),
    Esql.source_ip_values = values(source.ip),
    Esql.o365_audit_ApplicationId_values = values(o365.audit.ApplicationId),
    Esql.source_as_organization_name_values = values(source.`as`.organization.name),
    Esql.oauth_token_count_distinct = count_distinct(Esql.oauth_token_user_id_case),
    Esql.oauth_authorize_count_distinct = count_distinct(Esql.oauth_authorize_user_id_case)
  by
    o365.audit.UserId,
    Esql.time_window_date_trunc,
    o365.audit.ApplicationId,
    o365.audit.ObjectId
| keep
    Esql.time_window_date_trunc,
    Esql.source_ip_values,
    Esql.source_ip_count_distinct,
    Esql.o365_audit_ApplicationId_values,
    Esql.source_as_organization_name_values,
    Esql.oauth_token_count_distinct,
    Esql.oauth_authorize_count_distinct
| where
    Esql.source_ip_count_distinct >= 2 and
    Esql.oauth_token_count_distinct > 0 and
    Esql.oauth_authorize_count_distinct > 0
		

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK

Framework: MITRE ATT&CK