|
| 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