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.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/05/01"
3integration = ["o365"]
4maturity = "production"
5updated_date = "2025/07/16"
6
7[rule]
8author = ["Elastic"]
9description = """
10Identifies sign-ins on behalf of a principal user to the Microsoft Graph API from multiple IPs using the Microsoft
11Authentication Broker or Visual Studio Code application. This behavior may indicate an adversary using a phished OAuth
12refresh token.
13"""
14from = "now-60m"
15interval = "59m"
16language = "esql"
17license = "Elastic License v2"
18name = "Suspicious Microsoft 365 UserLoggedIn via OAuth Code"
19note = """## Triage and analysis
20
21### Investigating Suspicious Microsoft 365 UserLoggedIn via OAuth Code
22
23### Possible Investigation Steps:
24
25- `o365.audit.UserId`: The identity value the application is acting on behalf of principal user.
26- `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).
27- `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.
28- `azure.auditlogs` to check for device join or registration events around the same timeframe.
29- `azure.identityprotection` to identify correlated risk detections, such as anonymized IP access or token replay.
30- Any additional sign-ins from the `ips` involved, even outside the broker, to determine if tokens have been reused elsewhere.
31
32### False Positive Analysis
33
34- Developers or IT administrators working across environments may also produce similar behavior.
35
36### Response and Remediation
37
38- If confirmed unauthorized, revoke all refresh tokens for the affected user and remove any devices registered during this session.
39- Notify the user and determine whether the device join or authentication activity was expected.
40- Audit Conditional Access and broker permissions (`29d9ed98-a469-4536-ade2-f981bc1d605e`) to ensure policies enforce strict access controls.
41- Consider blocking token-based reauthentication to Microsoft Graph and DRS from suspicious locations or user agents.
42- Continue monitoring for follow-on activity like lateral movement or privilege escalation.
43"""
44references = [
45 "https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/",
46 "https://github.com/dirkjanm/ROADtools",
47 "https://dirkjanm.io/phishing-for-microsoft-entra-primary-refresh-tokens/",
48]
49risk_score = 73
50rule_id = "36188365-f88f-4f70-8c1d-0b9554186b9c"
51setup = """## Setup
52
53The Office 365 Logs Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule.
54"""
55severity = "high"
56tags = [
57 "Domain: Cloud",
58 "Domain: Email",
59 "Domain: Identity",
60 "Data Source: Microsoft 365",
61 "Data Source: Microsoft 365 Audit Logs",
62 "Use Case: Identity and Access Audit",
63 "Use Case: Threat Detection",
64 "Resources: Investigation Guide",
65 "Tactic: Defense Evasion",
66]
67timestamp_override = "event.ingested"
68type = "esql"
69
70query = '''
71from logs-o365.audit-*
72| where
73 event.dataset == "o365.audit" and
74 event.action == "UserLoggedIn" and
75 source.ip is not null and
76 o365.audit.UserId is not null and
77 o365.audit.ApplicationId is not null and
78 o365.audit.UserType in ("0", "2", "3", "10") and
79 o365.audit.ApplicationId in ("aebc6443-996d-45c2-90f0-388ff96faa56", "29d9ed98-a469-4536-ade2-f981bc1d605e") and
80 o365.audit.ObjectId in ("00000003-0000-0000-c000-000000000000")
81| eval
82 Esql.time_window_date_trunc = date_trunc(30 minutes, @timestamp),
83 Esql.oauth_authorize_user_id_case = case(
84 o365.audit.ExtendedProperties.RequestType == "OAuth2:Authorize" and o365.audit.ExtendedProperties.ResultStatusDetail == "Redirect",
85 o365.audit.UserId,
86 null
87 ),
88 Esql.oauth_token_user_id_case = case(
89 o365.audit.ExtendedProperties.RequestType == "OAuth2:Token",
90 o365.audit.UserId,
91 null
92 )
93| stats
94 Esql.source_ip_count_distinct = count_distinct(source.ip),
95 Esql.source_ip_values = values(source.ip),
96 Esql.o365_audit_ApplicationId_values = values(o365.audit.ApplicationId),
97 Esql.source_as_organization_name_values = values(source.`as`.organization.name),
98 Esql.oauth_token_count_distinct = count_distinct(Esql.oauth_token_user_id_case),
99 Esql.oauth_authorize_count_distinct = count_distinct(Esql.oauth_authorize_user_id_case)
100 by
101 o365.audit.UserId,
102 Esql.time_window_date_trunc,
103 o365.audit.ApplicationId,
104 o365.audit.ObjectId
105| keep
106 Esql.time_window_date_trunc,
107 Esql.source_ip_values,
108 Esql.source_ip_count_distinct,
109 Esql.o365_audit_ApplicationId_values,
110 Esql.source_as_organization_name_values,
111 Esql.oauth_token_count_distinct,
112 Esql.oauth_authorize_count_distinct
113| where
114 Esql.source_ip_count_distinct >= 2 and
115 Esql.oauth_token_count_distinct > 0 and
116 Esql.oauth_authorize_count_distinct > 0
117'''
118
119
120[[rule.threat]]
121framework = "MITRE ATT&CK"
122[[rule.threat.technique]]
123id = "T1550"
124name = "Use Alternate Authentication Material"
125reference = "https://attack.mitre.org/techniques/T1550/"
126[[rule.threat.technique.subtechnique]]
127id = "T1550.001"
128name = "Application Access Token"
129reference = "https://attack.mitre.org/techniques/T1550/001/"
130
131
132
133[rule.threat.tactic]
134id = "TA0005"
135name = "Defense Evasion"
136reference = "https://attack.mitre.org/tactics/TA0005/"
Triage and analysis
Investigating Suspicious Microsoft 365 UserLoggedIn via OAuth Code
Possible Investigation Steps:
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.
False Positive Analysis
- Developers or IT administrators working across environments may also produce similar behavior.
Response and Remediation
- 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.
References
Related rules
- Microsoft 365 Suspicious Inbox Rule to Delete or Move Emails
- Multiple Microsoft 365 User Account Lockouts in Short Time Window
- Potential Microsoft 365 User Account Brute Force
- Microsoft 365 or Entra ID Sign-in from a Suspicious Source
- TeamFiltration User-Agents Detected