Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
[metadata]
creation_date = "2026/05/22"
integration = ["azure"]
maturity = "production"
updated_date = "2026/05/2"
Comment thread
terrancedejesus marked this conversation as resolved.
Outdated

[rule]
author = ["Elastic"]
description = """
Identifies Azure AD Graph (graph.windows.net) requests where the combination of calling OAuth client
("azure.aadgraphactivitylogs.properties.app_id") and signed-in user ("user.id") has not been observed in the tenant in
a historical window. A user appearing against AAD Graph under an OAuth client that has not previously authenticated
that user is a sign of a FOCI swap, a phished refresh token being redeemed for a new client, or an adversary running
tooling under a client identity the user does not normally use.
"""
false_positives = [
"""
First-time use of a legitimate first-party or sanctioned client by the user (newly installed app, first sign-in to a
workload, fresh PowerShell module install). Validate by `azure.aadgraphactivitylogs.properties.app_id` and the
user's history.
""",
" Authorized red team or audit activity. Add exceptions on the calling user after review.\n ",
]
from = "now-9m"
index = ["logs-azure.aadgraphactivitylogs-*"]
language = "kuery"
license = "Elastic License v2"
name = "Azure AD Graph Access with Unusual Client and User"
note = """## Triage and analysis

### Investigating Entra ID AAD Graph Access by Unusual Client and User

A (client, user) pair appearing on AAD Graph for the first time in 14 days is a high-signal indicator. Legacy
AAD Graph traffic is dominated by a small set of recognised first-party callers per user; new combinations
suggest one of:

- A FOCI refresh-token swap landed under a new client identity.
- The user newly consented to (or was phished into consenting to) an OAuth application.
- An adversary is using stolen tokens / credentials under a client identity the user does not normally use.

### Possible investigation steps

- Identify the calling client and confirm whether the app is sanctioned.
- `azure.aadgraphactivitylogs.properties.app_id`. Pivot to Azure portal → Enterprise Applications to check whether it is first-party, sanctioned third-party, or unfamiliar.
- Identify the user and check the surrounding auth context.
- `user.id`, then pivot to sign-in logs (`logs-azure.signinlogs-*`) for the same user around the same time. Look for unusual sign-in geography, MFA bypass, or risky session signals.
- Review source posture.
- `user_agent.original`, `source.ip`, `source.as.organization.name`. Residential / VPS / anonymising-network egress raises priority.
- Review what was queried.
- `url.path`. Bulk recon or User-collection access via internal API versions raises triage priority.
- Check tenant-wide blast radius for the client.
- Is the same client ID hitting AAD Graph for many other users? That pattern points to large-scale consent abuse 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}`.
- If the client is not a sanctioned application, revoke its OAuth consent.
- `GET /v1.0/oauth2PermissionGrants?$filter=clientId eq '{servicePrincipalId}'`, then `DELETE /v1.0/oauth2PermissionGrants/{grantId}`.
- 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.
"""
references = [
"https://github.com/secureworks/family-of-client-ids-research",
"https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-overview",
"https://dirkjanm.io/phishing-for-microsoft-entra-primary-refresh-tokens/",
]
risk_score = 47
rule_id = "fd9d2933-f0f9-4aac-810c-a31f6a4a7890"
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.
"""
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: Defense Evasion",
"Tactic: Discovery",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "new_terms"

query = '''
data_stream.dataset:"azure.aadgraphactivitylogs" and
azure.aadgraphactivitylogs.properties.actor_type : "User" and
azure.aadgraphactivitylogs.properties.app_id: (* and not (
"74658136-14ec-4630-ad9b-26e160ff0fc6" or
"bb8f18b0-9c38-48c9-a847-e1ef3af0602d" or
"00000006-0000-0ff1-ce00-000000000000" or
"18ed3507-a475-4ccb-b669-d66bc9f2a36e")
) and user.id:*
'''


[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1550"
name = "Use Alternate Authentication Material"
reference = "https://attack.mitre.org/techniques/T1550/"
[[rule.threat.technique.subtechnique]]
id = "T1550.001"
name = "Application Access Token"
reference = "https://attack.mitre.org/techniques/T1550/001/"



[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[[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.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",
"azure.tenant_id",
]

[rule.new_terms]
field = "new_terms_fields"
value = ["azure.aadgraphactivitylogs.properties.app_id", "user.id"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-7d"


Loading