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

to-top