Entra ID OAuth User Impersonation to Microsoft Graph

Identifies potential session hijacking or token replay in Microsoft Entra ID. This rule detects cases where a user signs in and subsequently accesses Microsoft Graph from a different IP address using the same session ID. This may indicate a successful OAuth phishing attack, session hijacking, or token replay attack, where an adversary has stolen a session cookie or refresh/access token and is impersonating the user from an alternate host or location.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2025/05/08"
  3integration = ["azure"]
  4maturity = "production"
  5updated_date = "2026/03/23"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies potential session hijacking or token replay in Microsoft Entra ID. This rule detects cases where a user signs
 11in and subsequently accesses Microsoft Graph from a different IP address using the same session ID. This may indicate a
 12successful OAuth phishing attack, session hijacking, or token replay attack, where an adversary has stolen a session
 13cookie or refresh/access token and is impersonating the user from an alternate host or location.
 14"""
 15false_positives = [
 16    """
 17    This pattern may occur during legitimate device switching or roaming between networks (e.g., corporate to mobile).
 18    Developers or power users leveraging multiple environments may also trigger this detection if session persistence
 19    spans IP ranges. Still, this behavior is rare and warrants investigation when rapid IP switching and Graph access
 20    are involved.
 21    """,
 22]
 23from = "now-31m"
 24interval = "30m"
 25language = "esql"
 26license = "Elastic License v2"
 27name = "Entra ID OAuth User Impersonation to Microsoft Graph"
 28note = """## Triage and analysis
 29
 30### Investigating Entra ID OAuth User Impersonation to Microsoft Graph
 31
 32Identifies potential phishing, session hijacking or token replay in Microsoft Entra ID. This rule detects cases where a user signs in and subsequently accesses Microsoft Graph from a different IP address using the same session ID and client application. This may indicate a successful OAuth phishing attack, session hijacking, or token replay attack, where an adversary has stolen a session cookie or refresh/access token and is impersonating the user from an alternate host or location.
 33
 34This rule uses ESQL aggregations and thus has dynamically generated fields. Correlation of the values in the alert document may need to be
 35performed to the original sign-in and Graph events for further context.
 36
 37### Possible investigation steps
 38
 39- This rule relies on an aggregation-based ESQL query, therefore the alert document will contain dynamically generated fields.
 40    - To pivot into the original events, it is recommended to use the values captured to filter in timeline or discovery for the original sign-in and Graph events.
 41- Review the session ID and user ID to identify the user account involved in the suspicious activity.
 42- Check the source addresses involved in the sign-in and Graph access to determine if they are known or expected locations for the user.
 43    - The sign-in source addresses should be two, one for the initial phishing sign-in and the other when exchanging the auth code for a token by the adversary.
 44    - The Graph API source address should identify the IP address used by the adversary to access Microsoft Graph.
 45- Review the user agent strings for the sign-in and Graph access events to identify any anomalies or indicators of compromise.
 46- Analyze the Graph permission scopes to identify what resources were accessed and whether they align with the user's expected behavior.
 47- Check the timestamp difference between the sign-in and Graph access events to determine if they occurred within a reasonable time frame that would suggest successful phishing to token issuance and then Graph access.
 48- Identify the original sign-in event to investigation if conditional access policies were applied, such as requiring multi-factor authentication or blocking access from risky locations. In phishing scenarios, these policies likely were applied as the victim user would have been prompted to authenticate.
 49
 50### False positive analysis
 51- This pattern may occur during legitimate device switching or roaming between networks (e.g., corporate to mobile).
 52- Developers or power users leveraging multiple environments may also trigger this detection if session persistence spans IP ranges. Still, this behavior is rare and warrants investigation when rapid IP switching and Graph access are involved.
 53
 54### Response and remediation
 55
 56- If confirmed malicious, revoke all refresh/access tokens for the user principal.
 57- Block the source IP(s) involved in the Graph access.
 58- Notify the user and reset credentials.
 59- Review session control policies and conditional access enforcement.
 60- Monitor for follow-on activity, such as lateral movement or privilege escalation.
 61- Review conditional access policies to ensure they are enforced correctly.
 62"""
 63references = [
 64    "https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/",
 65    "https://github.com/dirkjanm/ROADtools",
 66    "https://attack.mitre.org/techniques/T1078/004/",
 67    "https://pushsecurity.com/blog/consentfix",
 68]
 69risk_score = 47
 70rule_id = "0d3d2254-2b4a-11f0-a019-f661ea17fbcc"
 71setup = """#### Required Microsoft Entra ID Sign-In and Graph Activity Logs
 72This rule requires the Microsoft Entra ID Sign-In Logs and Microsoft Graph Activity Logs integration to be enabled and configured to collect audit and activity logs via Azure Event Hub.
 73"""
 74severity = "medium"
 75tags = [
 76    "Domain: Cloud",
 77    "Domain: Identity",
 78    "Domain: API",
 79    "Data Source: Azure",
 80    "Data Source: Microsoft Entra ID",
 81    "Data Source: Microsoft Entra ID Sign-In Logs",
 82    "Data Source: Microsoft Graph",
 83    "Data Source: Microsoft Graph Activity Logs",
 84    "Use Case: Identity and Access Audit",
 85    "Use Case: Threat Detection",
 86    "Resources: Investigation Guide",
 87    "Tactic: Defense Evasion",
 88    "Tactic: Initial Access",
 89]
 90timestamp_override = "event.ingested"
 91type = "esql"
 92
 93query = '''
 94from logs-azure.signinlogs-*, logs-azure.graphactivitylogs-* metadata _id, _version, _index
 95| where
 96    (event.dataset == "azure.signinlogs"
 97     and source.`as`.organization.name != "MICROSOFT-CORP-MSN-AS-BLOCK"
 98     and azure.signinlogs.properties.session_id is not null)
 99    or
100    (event.dataset == "azure.graphactivitylogs"
101     and source.`as`.organization.name != "MICROSOFT-CORP-MSN-AS-BLOCK"
102     and azure.graphactivitylogs.properties.c_sid is not null)
103
104| eval
105    Esql.azure_signinlogs_properties_session_id_coalesce = coalesce(azure.signinlogs.properties.session_id, azure.graphactivitylogs.properties.c_sid),
106    Esql.azure_signinlogs_properties_user_id_coalesce = coalesce(azure.signinlogs.properties.user_id, azure.graphactivitylogs.properties.user_principal_object_id),
107    Esql.azure_signinlogs_properties_app_id_coalesce = coalesce(azure.signinlogs.properties.app_id, azure.graphactivitylogs.properties.app_id),
108    Esql.source_ip = source.ip,
109    Esql.@timestamp = @timestamp,
110    Esql.event_type_case = case(
111        event.dataset == "azure.signinlogs", "signin",
112        event.dataset == "azure.graphactivitylogs", "graph",
113        "other"
114    ),
115    Esql.signin_source_asn = case(event.dataset == "azure.signinlogs", source.`as`.organization.name, null),
116    Esql.graph_source_asn = case(event.dataset == "azure.graphactivitylogs", source.`as`.organization.name, null)
117
118| where Esql.azure_signinlogs_properties_app_id_coalesce not in (
119    "4354e225-50c9-4423-9ece-2d5afd904870",  // Augmentation Loop
120    "cc15fd57-2c6c-4117-a88c-83b1d56b4bbe",  // Microsoft Teams Services
121    "ecd6b820-32c2-49b6-98a6-444530e5a77a",  // Microsoft Edge [Community Contributed]
122    "e8be65d6-d430-4289-a665-51bf2a194bda",  // Microsoft 365 App Catalog Services
123    "ab9b8c07-8f02-4f72-87fa-80105867a763",  // OneDrive SyncEngine
124    "394866fc-eedb-4f01-8536-3ff84b16be2a",  // Microsoft People Cards Service
125    "66a88757-258c-4c72-893c-3e8bed4d6899",  // Office 365 Search Service
126    "9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7",  // Bing
127    "d7b530a4-7680-4c23-a8bf-c52c121d2e87",  // Microsoft Edge Enterprise New Tab Page [Community Contributed]
128    "6f7e0f60-9401-4f5b-98e2-cf15bd5fd5e3",  // Microsoft Application Command Service [Community Contributed]
129    "52c2e0b5-c7b6-4d11-a89c-21e42bcec444",  // Graph Files Manager
130    "27922004-5251-4030-b22d-91ecd9a37ea4",  // Outlook Mobile
131    "bb893c22-978d-4cd4-a6f7-bb6cc0d6e6ce",  // Olympus [Community Contributed]
132    "26a7ee05-5602-4d76-a7ba-eae8b7b67941",  // Windows Search
133    "00000007-0000-0000-c000-000000000000",  // Dataverse
134    "6bc3b958-689b-49f5-9006-36d165f30e00",  // Teams CMD Services Artifacts
135    "0ec893e0-5785-4de6-99da-4ed124e5296c",  // Office UWP PWA [Community Contributed]
136    "fc108d3f-543d-4374-bbff-c7c51f651fe5",  // Zoom
137    "01fc33a7-78ba-4d2f-a4b7-768e336e890e",  // MS PIM
138    "7ab7862c-4c57-491e-8a45-d52a7e023983"   // Power Automate / Logic Apps Graph Connector
139    ) and Esql.signin_source_asn IS NOT NULL and Esql.graph_source_asn IS NOT NULL
140
141| keep
142    Esql.azure_signinlogs_properties_session_id_coalesce,
143    Esql.source_ip,
144    Esql.@timestamp,
145    Esql.event_type_case,
146    Esql.azure_signinlogs_properties_user_id_coalesce,
147    Esql.azure_signinlogs_properties_app_id_coalesce,
148    Esql.signin_source_asn,
149    Esql.graph_source_asn,
150    source.`as`.organization.name,
151    user_agent.original,
152    url.original,
153    azure.graphactivitylogs.properties.scopes,
154    azure.signinlogs.properties.user_principal_name
155
156| stats
157    Esql.azure_signinlogs_properties_user_id_coalesce_values = values(Esql.azure_signinlogs_properties_user_id_coalesce),
158    Esql.azure_signinlogs_properties_session_id_coalesce_values = values(Esql.azure_signinlogs_properties_session_id_coalesce),
159    Esql_priv.azure_signinlogs_properties_user_principal_name_values = values(azure.signinlogs.properties.user_principal_name),
160    Esql.source_ip_values = values(Esql.source_ip),
161    Esql.source_ip_count_distinct = count_distinct(Esql.source_ip),
162    Esql.source_as_organization_name_values = values(source.`as`.organization.name),
163    Esql.source_as_organization_name_count_distinct = count_distinct(source.`as`.organization.name),
164    Esql.signin_source_asn_values = values(Esql.signin_source_asn),
165    Esql.signin_source_asn_count_distinct = count_distinct(Esql.signin_source_asn),
166    Esql.graph_source_asn_values = values(Esql.graph_source_asn),
167    Esql.graph_source_asn_count_distinct = count_distinct(Esql.graph_source_asn),
168    Esql.user_agent_original_values = values(user_agent.original),
169    Esql.azure_signinlogs_properties_app_id_coalesce_values = values(Esql.azure_signinlogs_properties_app_id_coalesce),
170    Esql.azure_signinlogs_properties_app_id_coalesce_count_distinct = count_distinct(Esql.azure_signinlogs_properties_app_id_coalesce),
171    Esql.event_type_case_values = values(Esql.event_type_case),
172    Esql.event_type_case_count_distinct = count_distinct(Esql.event_type_case),
173    Esql.signin_time_min = min(case(Esql.event_type_case == "signin", Esql.@timestamp, null)),
174    Esql.graph_time_min = min(case(Esql.event_type_case == "graph", Esql.@timestamp, null)),
175    Esql.url_original_values = values(url.original),
176    Esql.azure_graphactivitylogs_properties_scopes_values = values(azure.graphactivitylogs.properties.scopes),
177    Esql.event_count = count()
178  by
179    Esql.azure_signinlogs_properties_session_id_coalesce,
180    Esql.azure_signinlogs_properties_app_id_coalesce,
181    Esql.azure_signinlogs_properties_user_id_coalesce
182
183| eval
184    Esql.event_signin_to_graph_delay_minutes_date_diff = date_diff("minutes", Esql.signin_time_min, Esql.graph_time_min),
185    Esql.event_signin_to_graph_delay_days_date_diff = date_diff("days", Esql.signin_time_min, Esql.graph_time_min)
186
187| where
188    Esql.event_type_case_count_distinct > 1 and
189    Esql.source_ip_count_distinct > 1 and
190    Esql.source_as_organization_name_count_distinct > 1 and
191    Esql.azure_signinlogs_properties_app_id_coalesce_count_distinct == 1 and
192    Esql.signin_time_min is not null and
193    Esql.graph_time_min is not null and
194    Esql.event_signin_to_graph_delay_minutes_date_diff > 0 and
195    Esql.event_signin_to_graph_delay_days_date_diff == 0 and
196    (Esql.signin_source_asn_count_distinct + Esql.graph_source_asn_count_distinct) == Esql.source_as_organization_name_count_distinct
197'''
198
199
200[[rule.threat]]
201framework = "MITRE ATT&CK"
202[[rule.threat.technique]]
203id = "T1078"
204name = "Valid Accounts"
205reference = "https://attack.mitre.org/techniques/T1078/"
206[[rule.threat.technique.subtechnique]]
207id = "T1078.004"
208name = "Cloud Accounts"
209reference = "https://attack.mitre.org/techniques/T1078/004/"
210
211
212
213[rule.threat.tactic]
214id = "TA0001"
215name = "Initial Access"
216reference = "https://attack.mitre.org/tactics/TA0001/"
217[[rule.threat]]
218framework = "MITRE ATT&CK"
219[[rule.threat.technique]]
220id = "T1550"
221name = "Use Alternate Authentication Material"
222reference = "https://attack.mitre.org/techniques/T1550/"
223[[rule.threat.technique.subtechnique]]
224id = "T1550.001"
225name = "Application Access Token"
226reference = "https://attack.mitre.org/techniques/T1550/001/"
227
228
229
230[rule.threat.tactic]
231id = "TA0005"
232name = "Defense Evasion"
233reference = "https://attack.mitre.org/tactics/TA0005/"

Triage and analysis

Investigating Entra ID OAuth User Impersonation to Microsoft Graph

Identifies potential phishing, session hijacking or token replay in Microsoft Entra ID. This rule detects cases where a user signs in and subsequently accesses Microsoft Graph from a different IP address using the same session ID and client application. This may indicate a successful OAuth phishing attack, session hijacking, or token replay attack, where an adversary has stolen a session cookie or refresh/access token and is impersonating the user from an alternate host or location.

This rule uses ESQL aggregations and thus has dynamically generated fields. Correlation of the values in the alert document may need to be performed to the original sign-in and Graph events for further context.

Possible investigation steps

  • This rule relies on an aggregation-based ESQL query, therefore the alert document will contain dynamically generated fields.
    • To pivot into the original events, it is recommended to use the values captured to filter in timeline or discovery for the original sign-in and Graph events.
  • Review the session ID and user ID to identify the user account involved in the suspicious activity.
  • Check the source addresses involved in the sign-in and Graph access to determine if they are known or expected locations for the user.
    • The sign-in source addresses should be two, one for the initial phishing sign-in and the other when exchanging the auth code for a token by the adversary.
    • The Graph API source address should identify the IP address used by the adversary to access Microsoft Graph.
  • Review the user agent strings for the sign-in and Graph access events to identify any anomalies or indicators of compromise.
  • Analyze the Graph permission scopes to identify what resources were accessed and whether they align with the user's expected behavior.
  • Check the timestamp difference between the sign-in and Graph access events to determine if they occurred within a reasonable time frame that would suggest successful phishing to token issuance and then Graph access.
  • Identify the original sign-in event to investigation if conditional access policies were applied, such as requiring multi-factor authentication or blocking access from risky locations. In phishing scenarios, these policies likely were applied as the victim user would have been prompted to authenticate.

False positive analysis

  • This pattern may occur during legitimate device switching or roaming between networks (e.g., corporate to mobile).
  • Developers or power users leveraging multiple environments may also trigger this detection if session persistence spans IP ranges. Still, this behavior is rare and warrants investigation when rapid IP switching and Graph access are involved.

Response and remediation

  • If confirmed malicious, revoke all refresh/access tokens for the user principal.
  • Block the source IP(s) involved in the Graph access.
  • Notify the user and reset credentials.
  • Review session control policies and conditional access enforcement.
  • Monitor for follow-on activity, such as lateral movement or privilege escalation.
  • Review conditional access policies to ensure they are enforced correctly.

References

Related rules

to-top