LoFP LoFP / authorized red team activity. document and add exceptions on the user, app id, or source ip.

Techniques

Sample rules

Azure AD Graph High 4xx Error Ratio from User

Description

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.

Detection logic

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.*