From 39725dd1dc815d2701fd70c5ab08d097b172cbb0 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Wed, 20 May 2026 17:34:49 -0400 Subject: [PATCH 1/6] [New Rule] Azure AD Graph Access with Unusual User and ASN --- .../discovery_aad_graph_unusual_asn.toml | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 rules/integrations/azure/discovery_aad_graph_unusual_asn.toml diff --git a/rules/integrations/azure/discovery_aad_graph_unusual_asn.toml b/rules/integrations/azure/discovery_aad_graph_unusual_asn.toml new file mode 100644 index 00000000000..3649768914f --- /dev/null +++ b/rules/integrations/azure/discovery_aad_graph_unusual_asn.toml @@ -0,0 +1,143 @@ +[metadata] +creation_date = "2026/05/20" +integration = ["azure"] +maturity = "production" +updated_date = "2026/05/20" + +[rule] +author = ["Elastic"] +description = """ +Identifies Azure AD Graph (graph.windows.net) requests originating from network sources outside the major +public-cloud and Microsoft ASNs that legitimate first-party callers normally come from. Adversary tooling +typically rides on commodity hosting (residential ISPs, VPS providers, anonymisers) which produces an ASN +distribution very different from the Microsoft / AWS / GCP / Akamai / Cloudflare ranges that dominate +legitimate AAD Graph traffic. +""" +false_positives = [ + """ + Users calling AAD Graph from corporate office networks or home ISPs with custom tooling. Tune the + excluded ASN organisation list to your environment. + """, + """ + Cloud-hosted internal automation running outside the major providers (smaller cloud or colo). Add + exceptions on the calling user or app ID after validation. + """, +] +from = "now-9m" +index = ["logs-azure.aadgraphactivitylogs-*"] +language = "kuery" +license = "Elastic License v2" +name = "Azure AD Graph Access with Unusual User and ASN" +note = """## Triage and analysis + +### Investigating Azure AD Graph Requests with Unusual User and ASN + +Legitimate AAD Graph callers in most tenants come from a small set of ASNs: Microsoft itself, the major +hyperscalers (AWS, GCP), and a handful of CDN / edge networks that proxy first-party traffic. AAD Graph +traffic originating from outside that set, especially from residential ISPs, generic VPS providers, or +anonymising networks, is a signal worth a closer look. This rule excludes the common Microsoft / AWS / +GCP / Akamai / Cloudflare ASN organisations and surfaces everything else. + +### Possible investigation steps + +- Identify the ASN and the geographic context. + - `source.as.organization.name`, `source.as.number`, `source.geo.country_name`, `source.geo.city_name`. +- Identify the user and whether the source matches normal behavior. + - `user.id` and recent legitimate sign-in geo / network for the same user. +- Cross-check user-agent and calling client for known offensive tooling fingerprints. + - `user_agent.original` (`aiohttp`, `AADInternals`, `curl`, etc.) and `azure.aadgraphactivitylogs.properties.app_id` (FOCI / first-party client IDs). +- Pivot to sign-in logs (`logs-azure.signinlogs-*`) for the same user / source IP to understand how the calling token was obtained. +- Check tenant-wide blast radius. + - Are other users in the tenant calling from the same ASN within the window? If so, treat as a systematic intrusion rather than a single account compromise. +- Confirm the activity is not attributable to authorized testing (red team engagement, penetration test, internal tooling validation) before treating as malicious. + +### Response and remediation + +- Revoke refresh tokens and active sessions for the calling user. + - `POST /v1.0/users/{id}/revokeSignInSessions`. +- Temporarily disable the user if the alert is high-confidence or you need to halt further activity while investigation continues. + - `PATCH /v1.0/users/{id}` with body `{"accountEnabled": false}`. +- Check for device registrations created by the user during or around the burst window and remove rogue devices. + - `GET /v1.0/users/{id}/registeredDevices` and `GET /v1.0/users/{id}/ownedDevices`, then `DELETE /v1.0/devices/{deviceObjectId}`. + - Do this BEFORE session revocation: device-bound PRTs survive `revokeSignInSessions`. +- Apply Conditional Access requiring compliant device or trusted network for AAD Graph access for the affected user population. +- If the ASN belongs to known abusive infrastructure, add it to a tenant block list (Named Locations / CA policy). +""" +references = [ + "https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-overview", + "https://github.com/dirkjanm/ROADtools", +] +risk_score = 47 +rule_id = "fb935960-d132-4bb5-853d-62f86cccc250" +setup = """#### Azure AD Graph Activity Logs +Requires Azure AD Graph Activity Logs ingested into `logs-azure.aadgraphactivitylogs-*` via the Elastic Azure +integration. Enable the `AzureADGraphActivityLogs` diagnostic-settings category on Entra ID. ASN enrichment +depends on the geoip / ASN ingest pipelines applied during integration ingestion. +""" +severity = "medium" +tags = [ + "Domain: Cloud", + "Data Source: Azure", + "Data Source: Azure AD Graph", + "Data Source: Azure AD Graph Activity Logs", + "Use Case: Threat Detection", + "Tactic: Discovery", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "new_terms" + +query = ''' +data_stream.dataset:"azure.aadgraphactivitylogs" +and user.id:* and source.as.number:* +and not source.as.organization.name:( + *Microsoft* or *Amazon* or *Google* or *Azure* or *AWS* +) +''' + +[rule.investigation_fields] +field_names = [ + "user.id", + "source.ip", + "source.as.number", + "source.as.organization.name", + "source.geo.country_name", + "source.geo.city_name", + "user_agent.original", + "azure.aadgraphactivitylogs.properties.app_id", + "azure.aadgraphactivitylogs.properties.api_version", + "azure.tenant_id", +] + +[[rule.threat]] +framework = "MITRE ATT&CK" + +[[rule.threat.technique]] +id = "T1087" +name = "Account Discovery" +reference = "https://attack.mitre.org/techniques/T1087/" +[[rule.threat.technique.subtechnique]] +id = "T1087.004" +name = "Cloud Account" +reference = "https://attack.mitre.org/techniques/T1087/004/" + + +[[rule.threat.technique]] +id = "T1526" +name = "Cloud Service Discovery" +reference = "https://attack.mitre.org/techniques/T1526/" + + +[rule.threat.tactic] +id = "TA0007" +name = "Discovery" +reference = "https://attack.mitre.org/tactics/TA0007/" + + +[rule.new_terms] +field = "new_terms_fields" +value = ["user.id", "source.as.number"] + +[[rule.new_terms.history_window_start]] +field = "history_window_start" +value = "now-7d" \ No newline at end of file From 2d70a67be03b716dd695cb5132dbd71d41159cd8 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Fri, 29 May 2026 12:28:10 -0400 Subject: [PATCH 2/6] updated mitre; adjusted query for ASNs --- ...initial_access_aad_graph_unusual_asn.toml} | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) rename rules/integrations/azure/{discovery_aad_graph_unusual_asn.toml => initial_access_aad_graph_unusual_asn.toml} (82%) diff --git a/rules/integrations/azure/discovery_aad_graph_unusual_asn.toml b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml similarity index 82% rename from rules/integrations/azure/discovery_aad_graph_unusual_asn.toml rename to rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml index 3649768914f..fdcdffa98bf 100644 --- a/rules/integrations/azure/discovery_aad_graph_unusual_asn.toml +++ b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml @@ -33,7 +33,7 @@ note = """## Triage and analysis ### Investigating Azure AD Graph Requests with Unusual User and ASN Legitimate AAD Graph callers in most tenants come from a small set of ASNs: Microsoft itself, the major -hyperscalers (AWS, GCP), and a handful of CDN / edge networks that proxy first-party traffic. AAD Graph +hyperscalers (AWS, GCP), and a handful of CDN / edge -networks that proxy first-party traffic. AAD Graph traffic originating from outside that set, especially from residential ISPs, generic VPS providers, or anonymising networks, is a signal worth a closer look. This rule excludes the common Microsoft / AWS / GCP / Akamai / Cloudflare ASN organisations and surfaces everything else. @@ -80,19 +80,27 @@ tags = [ "Data Source: Azure", "Data Source: Azure AD Graph", "Data Source: Azure AD Graph Activity Logs", + "Use Case: Identity and Access Audit", "Use Case: Threat Detection", - "Tactic: Discovery", + "Tactic: Initial Access", "Resources: Investigation Guide", ] timestamp_override = "event.ingested" type = "new_terms" query = ''' -data_stream.dataset:"azure.aadgraphactivitylogs" -and user.id:* and source.as.number:* -and not source.as.organization.name:( - *Microsoft* or *Amazon* or *Google* or *Azure* or *AWS* -) +data_stream.dataset:"azure.aadgraphactivitylogs" and + user.id:* and (source.as.number:* or source.as.asn:*) and + not source.as.number:( + 3598 or 8068 or 8069 or 8070 or 8071 or 8072 or 8073 or 8074 or 8075 or 12076 or + 16509 or 14618 or 7224 or 8987 or 39111 or + 15169 or 396982 or 19527 or 36040 or 36384 or 36385 or 36492 or 394089 + ) and + not source.as.asn:( + 3598 or 8068 or 8069 or 8070 or 8071 or 8072 or 8073 or 8074 or 8075 or 12076 or + 16509 or 14618 or 7224 or 8987 or 39111 or + 15169 or 396982 or 19527 or 36040 or 36384 or 36385 or 36492 or 394089 + ) ''' [rule.investigation_fields] @@ -113,25 +121,19 @@ field_names = [ framework = "MITRE ATT&CK" [[rule.threat.technique]] -id = "T1087" -name = "Account Discovery" -reference = "https://attack.mitre.org/techniques/T1087/" +id = "T1078" +name = "Valid Accounts" +reference = "https://attack.mitre.org/techniques/T1078/" [[rule.threat.technique.subtechnique]] -id = "T1087.004" -name = "Cloud Account" -reference = "https://attack.mitre.org/techniques/T1087/004/" - - -[[rule.threat.technique]] -id = "T1526" -name = "Cloud Service Discovery" -reference = "https://attack.mitre.org/techniques/T1526/" +id = "T1078.004" +name = "Cloud Accounts" +reference = "https://attack.mitre.org/techniques/T1078/004/" [rule.threat.tactic] -id = "TA0007" -name = "Discovery" -reference = "https://attack.mitre.org/tactics/TA0007/" +id = "TA0001" +name = "Initial Access" +reference = "https://attack.mitre.org/tactics/TA0001/" [rule.new_terms] From 75727e40aa3f323ead7b9c1aadfd2251cb4e5fa5 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Fri, 29 May 2026 12:32:26 -0400 Subject: [PATCH 3/6] remove source.as.asn --- .../azure/initial_access_aad_graph_unusual_asn.toml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml index fdcdffa98bf..c21c4e48d86 100644 --- a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml +++ b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml @@ -90,13 +90,8 @@ type = "new_terms" query = ''' data_stream.dataset:"azure.aadgraphactivitylogs" and - user.id:* and (source.as.number:* or source.as.asn:*) and - not source.as.number:( - 3598 or 8068 or 8069 or 8070 or 8071 or 8072 or 8073 or 8074 or 8075 or 12076 or - 16509 or 14618 or 7224 or 8987 or 39111 or - 15169 or 396982 or 19527 or 36040 or 36384 or 36385 or 36492 or 394089 - ) and - not source.as.asn:( + user.id:* and source.as.number:* and + not source.as.number: ( 3598 or 8068 or 8069 or 8070 or 8071 or 8072 or 8073 or 8074 or 8075 or 12076 or 16509 or 14618 or 7224 or 8987 or 39111 or 15169 or 396982 or 19527 or 36040 or 36384 or 36385 or 36492 or 394089 From debb40c8ebe11bf8b88d36310a0fc5cad983aefe Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Fri, 29 May 2026 14:48:14 -0400 Subject: [PATCH 4/6] query optimization --- .../initial_access_aad_graph_unusual_asn.toml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml index c21c4e48d86..6249914b1b7 100644 --- a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml +++ b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml @@ -89,12 +89,15 @@ timestamp_override = "event.ingested" type = "new_terms" query = ''' -data_stream.dataset:"azure.aadgraphactivitylogs" and - user.id:* and source.as.number:* and - not source.as.number: ( - 3598 or 8068 or 8069 or 8070 or 8071 or 8072 or 8073 or 8074 or 8075 or 12076 or - 16509 or 14618 or 7224 or 8987 or 39111 or - 15169 or 396982 or 19527 or 36040 or 36384 or 36385 or 36492 or 394089 +data_stream.dataset:azure.aadgraphactivitylogs and + user.id:* and source.as.number:(* and + not ( + 3598 or 7224 or 8068 or 8069 or 8070 or + 8071 or 8072 or 8073 or 8074 or 8075 or + 8987 or 12076 or 14618 or 15169 or 16509 or + 19527 or 36040 or 36384 or 36385 or 36492 or + 39111 or 394089 or 396982 + ) ) ''' From 52abff9ab57afe54d555a8e42a247ecaae0d7f97 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Fri, 29 May 2026 15:13:34 -0400 Subject: [PATCH 5/6] updated investigation guide --- .../azure/initial_access_aad_graph_unusual_asn.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml index 6249914b1b7..c4cdedd598a 100644 --- a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml +++ b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml @@ -30,7 +30,7 @@ license = "Elastic License v2" name = "Azure AD Graph Access with Unusual User and ASN" note = """## Triage and analysis -### Investigating Azure AD Graph Requests with Unusual User and ASN +### Investigating Azure AD Graph Access with Unusual User and ASN Legitimate AAD Graph callers in most tenants come from a small set of ASNs: Microsoft itself, the major hyperscalers (AWS, GCP), and a handful of CDN / edge -networks that proxy first-party traffic. AAD Graph From 69e7d0d0ccbbe555edb0addafa87b66873570a7f Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Mon, 1 Jun 2026 09:08:23 -0400 Subject: [PATCH 6/6] bump to 9.3.0 --- .../azure/initial_access_aad_graph_unusual_asn.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml index c4cdedd598a..d2f03c6badb 100644 --- a/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml +++ b/rules/integrations/azure/initial_access_aad_graph_unusual_asn.toml @@ -2,6 +2,8 @@ creation_date = "2026/05/20" integration = ["azure"] maturity = "production" +min_stack_version = "9.3.0" +min_stack_comments = "azure.aadgraphactivitylogs backports to 8.19.10 only." updated_date = "2026/05/20" [rule]