Suspicious Microsoft 365 UserLoggedIn via OAuth Code

Last updated 13 days ago on 2025-05-01
Created 13 days ago on 2025-05-01

About

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.
Tags
Domain: CloudData Source: Microsoft 365Use Case: Identity and Access AuditUse Case: Threat DetectionTactic: Defense EvasionLanguage: esql
Severity
high
Risk Score
73
MITRE ATT&CK™

Defense Evasion (TA0005)(opens in a new tab or window)

License
Elastic License v2(opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

o365(opens in a new tab or window)

Query
from logs-o365.audit-default*
| 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 

  // filter for successful logon to Microsoft Graph and from the Microsoft Authentication Broker or Visual Studio Code
  o365.audit.ApplicationId in ("aebc6443-996d-45c2-90f0-388ff96faa56", "29d9ed98-a469-4536-ade2-f981bc1d605e") and
  o365.audit.ObjectId in ("00000003-0000-0000-c000-000000000000")

// keep relevant fields only
| keep @timestamp, o365.audit.UserId, source.ip, o365.audit.ApplicationId, o365.audit.ObjectId, o365.audit.ExtendedProperties.RequestType, source.as.organization.name, o365.audit.ExtendedProperties.ResultStatusDetail

// case statements to track which are OAuth2 authorization request via redirect and which are related to OAuth2 code to token conversion
| eval oauth_authorize = case(o365.audit.ExtendedProperties.RequestType == "OAuth2:Authorize" and o365.audit.ExtendedProperties.ResultStatusDetail == "Redirect", o365.audit.UserId, null), oauth_token = case(o365.audit.ExtendedProperties.RequestType == "OAuth2:Token", o365.audit.UserId, null)

// split time to 30 minutes intervals
| eval target_time_window = DATE_TRUNC(30 minutes, @timestamp)

// aggregate by principal, applicationId, objectId and time window
| stats unique_ips = COUNT_DISTINCT(source.ip), source_ips = VALUES(source.ip), appIds = VALUES(o365.audit.ApplicationId), asn = values(`source.as.organization.name`), is_oauth_token = COUNT_DISTINCT(oauth_token), is_oauth_authorize = COUNT_DISTINCT(oauth_authorize) by o365.audit.UserId, target_time_window, o365.audit.ApplicationId, o365.audit.ObjectId

// filter for cases where the same appId is used by the same principal user to access the same object and from multiple addresses via OAuth2 token
| where unique_ips >= 2 and is_oauth_authorize > 0 and is_oauth_token > 0

Install detection rules in Elastic Security

Detect Suspicious Microsoft 365 UserLoggedIn via OAuth Code in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(opens in a new tab or window).