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:
- https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/
- https://github.com/dirkjanm/ROADtools
- https://dirkjanm.io/phishing-for-microsoft-entra-primary-refresh-tokens/
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.auditlogsto check for device join or registration events around the same timeframe.azure.identityprotectionto identify correlated risk detections, such as anonymized IP access or token replay.- Any additional sign-ins from the
ipsinvolved, 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
Tactic:
- Name: Defense Evasion
- Id: TA0005
- Reference URL: https://attack.mitre.org/tactics/TA0005/
Technique:
- Name: Use Alternate Authentication Material
- Id: T1550
- Reference URL: https://attack.mitre.org/techniques/T1550/
Sub Technique:
- Name: Application Access Token
- Id: T1550.001
- Reference URL: https://attack.mitre.org/techniques/T1550/001/
Framework: MITRE ATT&CK
Tactic:
- Name: Credential Access
- Id: TA0006
- Reference URL: https://attack.mitre.org/tactics/TA0006/
Technique:
- Name: Steal Application Access Token
- Id: T1528
- Reference URL: https://attack.mitre.org/techniques/T1528/
Framework: MITRE ATT&CK
Tactic:
- Name: Initial Access
- Id: TA0001
- Reference URL: https://attack.mitre.org/tactics/TA0001/
Technique:
- Name: Phishing
- Id: T1566
- Reference URL: https://attack.mitre.org/techniques/T1566/
Sub Technique:
- Name: Spearphishing Link
- Id: T1566.002
- Reference URL: https://attack.mitre.org/techniques/T1566/002/