Skip to content

Commit d83e1c7

Browse files
[New Rule] Microsoft Entra Session Reuse with Suspicious Graph Access (#4711)
* new rule 'Microsoft Entra Session Reuse with Suspicious Graph Access' * fixed tags; linted * fixed mitre mappings * updated name and investigation guide
1 parent d30e65e commit d83e1c7

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
[metadata]
2+
creation_date = "2025/05/08"
3+
integration = ["azure"]
4+
maturity = "production"
5+
min_stack_comments = "Elastic ES|QL values aggregation is more performant in 8.16.5 and above."
6+
min_stack_version = "8.17.0"
7+
updated_date = "2025/05/08"
8+
9+
[rule]
10+
author = ["Elastic"]
11+
description = """
12+
Identifies potential session hijacking or token replay in Microsoft Entra ID. This rule detects cases where a user signs
13+
in and subsequently accesses Microsoft Graph from a different IP address using the same session ID within a short time
14+
window. This may indicate the use of a stolen refresh/access token or session cookie to impersonate the user and
15+
interact with Microsoft services.
16+
"""
17+
false_positives = [
18+
"""
19+
This pattern may occur during legitimate device switching or roaming between networks (e.g., corporate to mobile).
20+
Developers or power users leveraging multiple environments may also trigger this detection if session persistence
21+
spans IP ranges. Still, this behavior is rare and warrants investigation when rapid IP switching and Graph access
22+
are involved.
23+
""",
24+
]
25+
from = "now-1h"
26+
language = "esql"
27+
license = "Elastic License v2"
28+
name = "Microsoft Entra ID Session Reuse with Suspicious Graph Access"
29+
note = """## Triage and analysis
30+
31+
### Investigating Microsoft Entra ID Session Reuse with Suspicious Graph Access
32+
33+
This rule identifies when Microsoft Graph is accessed from a different IP than the one used for the original sign-in,
34+
but using the same session ID within 5 minutes. This may suggest an adversary has stolen a session cookie or refresh/access
35+
token and is impersonating the user from an alternate host or location.
36+
37+
This rule uses ES|QL aggregations and thus has dynamically generated fields. Correlation of the values in the alert document may need to be
38+
performed to the original sign-in and Graph events for further context.
39+
40+
### Investigation Steps
41+
42+
- Review the `user_id`, `session_id`, and `source_ip_list`. Confirm whether both IPs belong to the same user and geography.
43+
- Check for inconsistencies in `client_id_list` (e.g., unknown apps) or user agents across correlated events.
44+
- Investigate recent phishing reports or device infections for the `user_id`.
45+
- Pivot to Entra ID `auditlogs` to see if a device was registered or privileges were modified.
46+
- Review `graph_time` to determine what action was taken after the sign-in.
47+
- Use the `session_id` to correlate with other logs in the same time window to identify any additional suspicious activity.
48+
49+
### False Positive Analysis
50+
- This pattern may occur if the user is switching between networks (e.g., corporate to mobile) or using a VPN.
51+
- Developers or power users leveraging multiple environments may also trigger this detection if session persistence spans IP ranges.
52+
- However, this behavior is rare and warrants investigation when rapid IP switching and Graph access are involved.
53+
- If the user is a developer or automation engineer, validate if this behavior was for testing purposes.
54+
- If the user is a system administrator, validate if this behavior was for administrative purposes.
55+
56+
### Response Recommendations
57+
58+
- If confirmed malicious, revoke all refresh/access tokens for the `user_id`.
59+
- Block the source IP(s) involved in the Graph access.
60+
- Notify the user and reset credentials.
61+
- Review session control policies and conditional access enforcement.
62+
- Monitor for follow-on activity, such as lateral movement or privilege escalation.
63+
- Review conditional access policies to ensure they are enforced correctly.
64+
"""
65+
references = [
66+
"https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/",
67+
"https://github.com/dirkjanm/ROADtools",
68+
"https://attack.mitre.org/techniques/T1078/004/",
69+
]
70+
risk_score = 73
71+
rule_id = "0d3d2254-2b4a-11f0-a019-f661ea17fbcc"
72+
setup = """#### Required Microsoft Entra ID Sign-In and Graph Activity Logs
73+
This 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.
74+
"""
75+
severity = "high"
76+
tags = [
77+
"Domain: Cloud",
78+
"Data Source: Azure",
79+
"Data Source: Microsoft Entra ID",
80+
"Data Source: Microsoft Entra ID Sign-In Logs",
81+
"Data Source: Microsoft Graph",
82+
"Data Source: Microsoft Graph Activity Logs",
83+
"Use Case: Identity and Access Audit",
84+
"Use Case: Threat Detection",
85+
"Resources: Investigation Guide",
86+
"Tactic: Defense Evasion",
87+
"Tactic: Initial Access",
88+
]
89+
timestamp_override = "event.ingested"
90+
type = "esql"
91+
92+
query = '''
93+
FROM logs-azure.*
94+
| WHERE
95+
(event.dataset == "azure.signinlogs" AND source.`as`.organization.name != "MICROSOFT-CORP-MSN-AS-BLOCK" AND azure.signinlogs.properties.session_id IS NOT NULL)
96+
OR
97+
(event.dataset == "azure.graphactivitylogs" AND source.`as`.organization.name != "MICROSOFT-CORP-MSN-AS-BLOCK" AND azure.graphactivitylogs.properties.c_sid IS NOT NULL)
98+
| EVAL
99+
session_id = COALESCE(azure.signinlogs.properties.session_id, azure.graphactivitylogs.properties.c_sid),
100+
user_id = COALESCE(azure.signinlogs.properties.user_id, azure.graphactivitylogs.properties.user_principal_object_id),
101+
client_id = COALESCE(azure.signinlogs.properties.app_id, azure.graphactivitylogs.properties.app_id),
102+
source_ip = source.ip,
103+
event_time = @timestamp,
104+
event_type = CASE(
105+
event.dataset == "azure.signinlogs", "signin",
106+
event.dataset == "azure.graphactivitylogs", "graph",
107+
"other"
108+
),
109+
time_window = DATE_TRUNC(5 minutes, @timestamp)
110+
| KEEP session_id, source_ip, event_time, event_type, time_window, user_id, client_id
111+
| STATS
112+
user_id = VALUES(user_id),
113+
session_id = VALUES(session_id),
114+
source_ip_list = VALUES(source_ip),
115+
source_ip_count = COUNT_DISTINCT(source_ip),
116+
client_id_list = VALUES(client_id),
117+
application_count = COUNT_DISTINCT(client_id),
118+
event_type_list = VALUES(event_type),
119+
event_type_count = COUNT_DISTINCT(event_type),
120+
event_start = MIN(event_time),
121+
event_end = MAX(event_time),
122+
signin_time = MIN(CASE(event_type == "signin", event_time, NULL)),
123+
graph_time = MIN(CASE(event_type == "graph", event_time, NULL)),
124+
document_count = COUNT()
125+
BY session_id, time_window
126+
| EVAL
127+
duration_minutes = DATE_DIFF("minutes", event_start, event_end),
128+
signin_to_graph_delay_minutes = DATE_DIFF("minutes", signin_time, graph_time)
129+
| WHERE
130+
event_type_count > 1 AND
131+
source_ip_count > 1 AND
132+
duration_minutes <= 5 AND
133+
signin_time IS NOT NULL AND
134+
graph_time IS NOT NULL AND
135+
signin_to_graph_delay_minutes >= 0
136+
'''
137+
138+
139+
[[rule.threat]]
140+
framework = "MITRE ATT&CK"
141+
[[rule.threat.technique]]
142+
id = "T1078"
143+
name = "Valid Accounts"
144+
reference = "https://attack.mitre.org/techniques/T1078/"
145+
[[rule.threat.technique.subtechnique]]
146+
id = "T1078.004"
147+
name = "Cloud Accounts"
148+
reference = "https://attack.mitre.org/techniques/T1078/004/"
149+
150+
151+
152+
[rule.threat.tactic]
153+
id = "TA0001"
154+
name = "Initial Access"
155+
reference = "https://attack.mitre.org/tactics/TA0001/"
156+
[[rule.threat]]
157+
framework = "MITRE ATT&CK"
158+
[[rule.threat.technique]]
159+
id = "T1550"
160+
name = "Use Alternate Authentication Material"
161+
reference = "https://attack.mitre.org/techniques/T1550/"
162+
[[rule.threat.technique.subtechnique]]
163+
id = "T1550.001"
164+
name = "Application Access Token"
165+
reference = "https://attack.mitre.org/techniques/T1550/001/"
166+
167+
168+
169+
[rule.threat.tactic]
170+
id = "TA0005"
171+
name = "Defense Evasion"
172+
reference = "https://attack.mitre.org/tactics/TA0005/"
173+

0 commit comments

Comments
 (0)