Microsoft Graph Application Directory Reconnaissance with Denied Requests

Identifies a Microsoft Graph application querying multiple directory collections and encountering access denials. Adversaries with compromised application credentials may enumerate users, groups, applications, service principals, directory roles, role assignments, and OAuth grants to identify privileged identities and opportunities for further access.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/11/03"
  3integration = ["azure"]
  4maturity = "production"
  5updated_date = "2026/11/04"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies a Microsoft Graph application querying multiple directory collections and encountering access denials.
 11Adversaries with compromised application credentials may enumerate users, groups, applications, service principals,
 12directory roles, role assignments, and OAuth grants to identify privileged identities and opportunities for further
 13access.
 14"""
 15false_positives = [
 16    """
 17    Application onboarding, directory synchronization, inventory tools, and permission-validation scripts may query
 18    multiple directory collections and encounter expected access denials.
 19    """,
 20]
 21from = "now-30m"
 22interval = "5m"
 23language = "esql"
 24license = "Elastic License v2"
 25name = "Microsoft Graph Application Directory Reconnaissance with Denied Requests"
 26note = """## Triage and analysis
 27
 28### Investigating Microsoft Graph Application Directory Reconnaissance with Denied Requests
 29
 30Microsoft Graph exposes directory information that can help an adversary identify accounts, privileged identities, and applications to target. This rule identifies app-only requests to at least three common directory discovery collections (applications, service principals, users, groups, directory roles, role assignments, and OAuth permission grants) within a 15-minute window, with at least one HTTP 403 response. The denial requirement reduces noise from authorized inventory applications that succeed across the collections they are granted. A denied request shows attempted discovery, not successful retrieval of that collection.
 31
 32### Possible investigation steps
 33
 34- Identify the calling principal using `azure.tenant_id` and `azure.graphactivitylogs.properties.service_principal_id`. Review `Esql.app_ids` and resolve the application owner and expected directory permissions.
 35- Review `Esql.time_window`, `Esql.request_count`, `Esql.category_count`, and `Esql.denied_count` to establish the scope of the discovery. Use `Esql.paths`, `Esql.status_codes`, and `Esql.source_ips` to review the targeted collections, outcomes, and sources.
 36- Examine the original Graph events to distinguish successfully accessed collections from denied requests. Determine whether the queried collections are needed for the application's documented purpose.
 37- Correlate the principal with service-principal sign-ins, recent application credential additions, and changes to application permissions. Where available, compare `azure.signinlogs.properties.service_principal_credential_key_id` with the credential added in audit records.
 38- Look for related role-assignment attempts, successful privilege changes, secret access, and access to other services. Distinguish the calling application from applications returned or targeted by its requests.
 39- Confirm whether an inventory, directory synchronization, onboarding, or permission-validation workflow explains the activity.
 40
 41### False positive analysis
 42
 43- Directory inventory and synchronization applications may access several collections and encounter expected permission denials. Validate the activity with the application owner and compare it with normal sources and access patterns.
 44- Onboarding and permission-validation scripts may generate similar requests. Scope any justified exception to the tenant, principal, and expected workflow, and reassess it when permissions or ownership change.
 45
 46### Response and remediation
 47
 48- If compromise is confirmed, restrict the affected principal and remove the specific compromised credential and unnecessary permissions according to the incident response process.
 49- Review and remediate unauthorized privilege changes or downstream resource access associated with the principal.
 50- Verify that subsequent access is denied after containment; credential removal alone does not establish that previously issued access tokens are unusable.
 51- Preserve the original Graph activity, audit, and sign-in events for investigation.
 52"""
 53references = [
 54    "https://www.microsoft.com/en-us/security/blog/2026/10/21/storm-0940-uses-artex-agentic-ai-connectwise-supply-chain-compromise/",
 55    "https://learn.microsoft.com/en-us/graph/microsoft-graph-activity-logs-overview",
 56]
 57risk_score = 47
 58rule_id = "ed66918f-5fb0-4cb2-a684-47e5d72e0a50"
 59setup = """#### Microsoft Graph Activity Logs
 60
 61Enable Microsoft Graph Activity Logs and ingest them through the Azure integration into
 62`logs-azure.graphactivitylogs-*`. Retain the tenant and service-principal IDs, HTTP method, URL path, and response
 63status. The `azure.graphactivitylogs.properties.c_idtyp` field must be populated with the token actor type;
 64this rule selects `app` and does not infer app-only authentication from an application ID alone.
 65
 66The rule evaluates a 30-minute ingestion lookback every five minutes and groups requests into fixed 15-minute
 67event-time windows. Requests split across window boundaries or ingested more than 30 minutes apart may not be
 68combined. Review delivery latency and repeated aggregate alerts when tuning the schedule. The rule covers
 69the direct v1.0 collection paths in the query; beta endpoints and other request paths are outside its scope.
 70"""
 71severity = "medium"
 72tags = [
 73    "Domain: Cloud",
 74    "Domain: Identity",
 75    "Data Source: Azure",
 76    "Data Source: Microsoft Graph Activity Logs",
 77    "Use Case: Threat Detection",
 78    "Tactic: Discovery",
 79    "Resources: Investigation Guide",
 80    "Rule Type: ES|QL",
 81    "Platform: Microsoft Graph",
 82    "Platform: Azure",
 83]
 84timestamp_override = "event.ingested"
 85type = "esql"
 86
 87query = '''
 88from logs-azure.graphactivitylogs-* metadata _id, _index
 89| where data_stream.dataset == "azure.graphactivitylogs"
 90  and azure.graphactivitylogs.properties.c_idtyp == "app"
 91  and azure.tenant_id is not null and azure.tenant_id != ""
 92  and azure.graphactivitylogs.properties.service_principal_id is not null
 93  and azure.graphactivitylogs.properties.service_principal_id != ""
 94  and http.request.method == "GET"
 95  and http.response.status_code in (200, 403)
 96  and url.path in (
 97      "/v1.0/applications",
 98      "/v1.0/servicePrincipals",
 99      "/v1.0/users",
100      "/v1.0/groups",
101      "/v1.0/directoryRoles",
102      "/v1.0/roleManagement/directory/roleAssignments",
103      "/v1.0/oauth2PermissionGrants"
104)
105| eval Esql.time_window = date_trunc(15 minutes, @timestamp)
106| stats
107    Esql.request_count = count(*),
108    Esql.category_count = count_distinct(url.path),
109    Esql.denied_count = sum(case(http.response.status_code == 403, 1, 0)),
110    Esql.paths = values(url.path),
111    Esql.status_codes = values(http.response.status_code),
112    Esql.app_ids = values(azure.graphactivitylogs.properties.app_id),
113    Esql.source_ips = values(source.ip),
114    Esql.first_seen = min(@timestamp),
115    Esql.last_seen = max(@timestamp)
116  by Esql.time_window, azure.tenant_id, azure.graphactivitylogs.properties.service_principal_id
117| where Esql.category_count >= 3 and Esql.denied_count >= 1
118| keep azure.tenant_id, azure.graphactivitylogs.properties.service_principal_id, Esql.*
119'''
120
121[rule.alert_suppression]
122group_by = [
123    "azure.tenant_id",
124    "azure.graphactivitylogs.properties.service_principal_id",
125]
126missing_fields_strategy = "suppress"
127
128[rule.alert_suppression.duration]
129unit = "m"
130value = 30
131
132[[rule.threat]]
133framework = "MITRE ATT&CK"
134[[rule.threat.technique]]
135id = "T1069"
136name = "Permission Groups Discovery"
137reference = "https://attack.mitre.org/techniques/T1069/"
138[[rule.threat.technique.subtechnique]]
139id = "T1069.003"
140name = "Cloud Groups"
141reference = "https://attack.mitre.org/techniques/T1069/003/"
142
143
144[[rule.threat.technique]]
145id = "T1087"
146name = "Account Discovery"
147reference = "https://attack.mitre.org/techniques/T1087/"
148[[rule.threat.technique.subtechnique]]
149id = "T1087.004"
150name = "Cloud Account"
151reference = "https://attack.mitre.org/techniques/T1087/004/"
152
153
154
155[rule.threat.tactic]
156id = "TA0007"
157name = "Discovery"
158reference = "https://attack.mitre.org/tactics/TA0007/"
159
160[rule.investigation_fields]
161field_names = [
162    "azure.tenant_id",
163    "azure.graphactivitylogs.properties.service_principal_id",
164    "Esql.paths",
165    "Esql.status_codes",
166    "Esql.source_ips",
167    "Esql.time_window",
168]

Triage and analysis

Investigating Microsoft Graph Application Directory Reconnaissance with Denied Requests

Microsoft Graph exposes directory information that can help an adversary identify accounts, privileged identities, and applications to target. This rule identifies app-only requests to at least three common directory discovery collections (applications, service principals, users, groups, directory roles, role assignments, and OAuth permission grants) within a 15-minute window, with at least one HTTP 403 response. The denial requirement reduces noise from authorized inventory applications that succeed across the collections they are granted. A denied request shows attempted discovery, not successful retrieval of that collection.

Possible investigation steps

  • Identify the calling principal using azure.tenant_id and azure.graphactivitylogs.properties.service_principal_id. Review Esql.app_ids and resolve the application owner and expected directory permissions.
  • Review Esql.time_window, Esql.request_count, Esql.category_count, and Esql.denied_count to establish the scope of the discovery. Use Esql.paths, Esql.status_codes, and Esql.source_ips to review the targeted collections, outcomes, and sources.
  • Examine the original Graph events to distinguish successfully accessed collections from denied requests. Determine whether the queried collections are needed for the application's documented purpose.
  • Correlate the principal with service-principal sign-ins, recent application credential additions, and changes to application permissions. Where available, compare azure.signinlogs.properties.service_principal_credential_key_id with the credential added in audit records.
  • Look for related role-assignment attempts, successful privilege changes, secret access, and access to other services. Distinguish the calling application from applications returned or targeted by its requests.
  • Confirm whether an inventory, directory synchronization, onboarding, or permission-validation workflow explains the activity.

False positive analysis

  • Directory inventory and synchronization applications may access several collections and encounter expected permission denials. Validate the activity with the application owner and compare it with normal sources and access patterns.
  • Onboarding and permission-validation scripts may generate similar requests. Scope any justified exception to the tenant, principal, and expected workflow, and reassess it when permissions or ownership change.

Response and remediation

  • If compromise is confirmed, restrict the affected principal and remove the specific compromised credential and unnecessary permissions according to the incident response process.
  • Review and remediate unauthorized privilege changes or downstream resource access associated with the principal.
  • Verify that subsequent access is denied after containment; credential removal alone does not establish that previously issued access tokens are unusable.
  • Preserve the original Graph activity, audit, and sign-in events for investigation.

References

Related rules

to-top