Azure AD Graph High 4xx Error Ratio from User

Last updated a month ago on 2026-05-20
Created a month ago on 2026-05-20

About

Detects an unusually high ratio of 4xx HTTP responses from Azure AD Graph (graph.windows.net) per calling identity in a short window. Post-identity compromise leading to recon often leaves a tail of 403s and 404s as tooling walks endpoints it does not have permission for, asks for object IDs it does not have, or uses an OAuth client that has been pulled off the AAD Graph allow-list. Surges or an unexpected ratio of 4xx responses concentrated on a single (user and ASN) pair are characteristic of automated tooling rather than human or first-party traffic.
Tags
Domain: CloudData Source: AzureData Source: Azure AD GraphData Source: Azure AD Graph Activity LogsUse Case: Threat DetectionTactic: DiscoveryLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

Discovery (TA0007)(external, opens in a new tab or window)

False Positive Examples
Legitimate first-party clients occasionally hit 4xx responses as part of conditional access flows, transient permission changes, or stale token retries. Tune the threshold for your tenant baseline. Authorized red team activity. Document and add exceptions on the user, app ID, or source IP. Legacy tooling may still be using AAD Graph. Validate and add exceptions on the calling app ID after review.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

azure(external, opens in a new tab or window)

Query
text code block:
from logs-azure.aadgraphactivitylogs-* metadata _id, _version, _index | where data_stream.dataset == "azure.aadgraphactivitylogs" | eval Esql.is_4xx = case( http.response.status_code >= 400 and http.response.status_code < 500, 1, 0 ) | eval Esql.time_window = date_trunc(2 minutes, @timestamp) | stats Esql.total_calls = count(*), Esql.azure_tenants = values(azure.tenant_id), Esql.errors = sum(Esql.is_4xx), Esql.url_path_count = count_distinct(url.path), Esql.api_versions = values(azure.aadgraphactivitylogs.properties.api_version), Esql.app_ids = values(azure.aadgraphactivitylogs.properties.app_id), Esql.source_ips = values(source.ip), Esql.source_asn_name = values(source.as.organization.name), Esql.user_agents = values(user_agent.original), Esql.first_seen = min(@timestamp), Esql.last_seen = max(@timestamp) by user.id, source.as.number, Esql.time_window | eval Esql.error_rate = round(Esql.errors * 1.0 / Esql.total_calls, 2) | where Esql.total_calls > 20 and Esql.errors >= 10 and Esql.error_rate >= 0.4 and Esql.url_path_count >= 15 | keep user.id, source.as.number, Esql.*

Install detection rules in Elastic Security

Detect Azure AD Graph High 4xx Error Ratio from User in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).