From c078cc8268e286c63ac3702b547c672fafeca3db Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Thu, 21 May 2026 11:17:23 -0400 Subject: [PATCH 1/2] [New Rule] Azure AD Graph Access with Suspicious User-Agent --- ...overy_aad_graph_suspicious_user_agent.toml | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml diff --git a/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml b/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml new file mode 100644 index 00000000000..e55abb7a3fd --- /dev/null +++ b/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml @@ -0,0 +1,154 @@ +[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 user-agent strings associated with offensive +tooling, scripting libraries, or generic HTTP clients. First-party Microsoft components calling AAD Graph identify with +specific user agents such as `Microsoft Azure Graph Client Library`, `Microsoft ADO.NET Data Services`, or +`Microsoft.OData.Client`. Anything outside that recognised set is either a developer prototyping against the legacy API +or an enumeration tool walking the directory. +""" +false_positives = [ + """ + Developer activity prototyping against AAD Graph from a workstation may match. Validate via the calling + `azure.aadgraphactivitylogs.properties.app_id` and the signed-in user; legitimate developer use is rare in + production tenants since Microsoft has been steering callers off AAD Graph for years. + """, + """ + Authorized red team or penetration test activity using ROADrecon, ROADtools, or similar tooling can match. Add + exceptions on the source IP, signed-in 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 Suspicious User-Agent" +note = """## Triage and analysis + +### Investigating Entra ID AAD Graph Activity from Non-Microsoft User-Agent + +Azure AD Graph (graph.windows.net) is the legacy directory REST API that Microsoft has been retiring for years. +Legitimate first-party traffic against it is dominated by a small set of recognisable user agents (`Microsoft.OData.Client`, +`Microsoft Azure Graph Client Library`, `Microsoft ADO.NET Data Services`, the Azure portal with a Chrome user agent, +and an empty-UA tail from first-party AppIds). Traffic identifying as Python, aiohttp, curl, Go-http-client, or any +of the `*hound` enumeration families is almost always either a developer prototype or adversary tooling. This rule +flags any such request even at a single event, because tooling samples for AAD Graph are inherently low-volume in +normal tenants. + +### Possible investigation steps + +- Confirm the matching user agent. + - `user_agent.original` (e.g., `aiohttp`, `AADInternals`, `curl`, `bav2ropc`). +- Identify the caller and the calling client. + - `user.id` for the caller, `azure.aadgraphactivitylogs.properties.app_id` for the OAuth client. +- Review which directory object types were touched. + - `url.path` (e.g., `/users`, `/policies`, `/servicePrincipals`). +- Check the success / failure pattern. + - `http.response.status_code`. Many 4xx responses suggest permission probing. +- Cross-reference with the API version. + - `azure.aadgraphactivitylogs.properties.api_version`. A non-Microsoft UA combined with `1.6-internal` or `1.61-internal` is a stronger signal of offensive tooling. +- Pivot to sign-in logs (`logs-azure.signinlogs-*`) for the same user / source IP to understand how the token was obtained. +- 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`. +- If the calling application has no legitimate AAD Graph dependency, block further use by that app. + - `PATCH /beta/applications/{id}` with body `{"authenticationBehaviors": {"blockAzureADGraphAccess": true}}`. + - This property lives on the Graph beta endpoint, not v1.0. +- Apply Conditional Access targeting the AAD Graph audience for the affected user population. +""" +references = [ + "https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-overview", + "https://github.com/dirkjanm/ROADtools", + "https://www.sophos.com/en-us/research/tampering-with-conditional-access-policies-using-azure-ad-graph-api", + "https://github.com/gerenios/aadinternals", +] +risk_score = 47 +rule_id = "3aec394d-ed2a-4f3e-8ed3-4a2adea39f05" +setup = """#### Azure AD Graph Activity Logs +Requires Azure AD Graph Activity Logs ingested into `logs-azure.aadgraphactivitylogs-*` via the Elastic Azure +integration (Azure Event Hub). Enable the `AzureADGraphActivityLogs` diagnostic-settings category on Entra ID. +""" +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 = "query" + +query = ''' +data_stream.dataset:"azure.aadgraphactivitylogs" +and user_agent.original:( + *fasthttp* or *aiohttp* or *azurehound* or *bloodhound* or *AADInternals* + or *go-http-client* or python* or *curl/* or *okhttp* + or *axios* or *node-fetch* or *go-resty* or *bav2ropc* + or *undici* +) +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1069" +name = "Permission Groups Discovery" +reference = "https://attack.mitre.org/techniques/T1069/" +[[rule.threat.technique.subtechnique]] +id = "T1069.003" +name = "Cloud Groups" +reference = "https://attack.mitre.org/techniques/T1069/003/" + + +[[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.investigation_fields] +field_names = [ + "user.id", + "source.ip", + "source.as.organization.name", + "user_agent.original", + "azure.aadgraphactivitylogs.properties.app_id", + "azure.aadgraphactivitylogs.properties.api_version", + "url.path", + "http.response.status_code", + "azure.tenant_id", +] + From e07fb7d3015275b9a016b204f4049a05685d90c3 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus Date: Thu, 21 May 2026 11:21:03 -0400 Subject: [PATCH 2/2] [New Rule] Azure AD Graph Access with Suspicious User-Agent --- .../azure/discovery_aad_graph_suspicious_user_agent.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml b/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml index e55abb7a3fd..58f90395bac 100644 --- a/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml +++ b/rules/integrations/azure/discovery_aad_graph_suspicious_user_agent.toml @@ -20,9 +20,10 @@ false_positives = [ production tenants since Microsoft has been steering callers off AAD Graph for years. """, """ - Authorized red team or penetration test activity using ROADrecon, ROADtools, or similar tooling can match. Add + Authorized red team or penetration test activity using ROADrecon, ROADtools, AADInternalsor similar tooling can match. Add exceptions on the source IP, signed-in user, or app ID after validation. """, + ] from = "now-9m" index = ["logs-azure.aadgraphactivitylogs-*"] @@ -97,6 +98,7 @@ type = "query" query = ''' data_stream.dataset:"azure.aadgraphactivitylogs" +and azure.aadgraphactivitylogs.properties.actor_type: "User" and user_agent.original:( *fasthttp* or *aiohttp* or *azurehound* or *bloodhound* or *AADInternals* or *go-http-client* or python* or *curl/* or *okhttp*