Query Details

Graph API Audit Events Azure Hound

Query

# AzureHound Detection

## Query Information

#### MITRE ATT&CK Technique(s)

| Technique ID | Title    | Link    |
| ---  | --- | --- |
| T1087.004 | Account Discovery: Cloud Account | https://attack.mitre.org/techniques/T1087/004/ |
| T1069.003| Permission Groups Discovery: Cloud Groups | https://attack.mitre.org/techniques/T1069/003/ |

#### Description
The query below uses the *MicrosoftGraphActivityLogs* to collect potential AzureHound executions. This is done by filtering on GET requests with status 200 since AzureHound is a collector is submits GET requests to retrieve the data. Furthermore, statistics are applied to count the number of bytes retrieved and how many unique requests have been executed within the timeframe of one hour. Lastly, the stats are compared against the thresholds, if the results are bigger than the thresholds the results are returned and your analysis can begin. These thresholds depend on the size of your Entra ID tenant. My test environment has a limited set of accounts, thus the total amount of unique requests is limited. If your organisation has more than 1000 users, the *UniqueRequestThreshold* can easily be set above 5000.

#### Risk
An adversary executes AzureHound to get insights into your Azure Tenant

#### References
- https://github.com/BloodHoundAD/AzureHound
- https://www.cloudbrothers.info/detect-threats-microsoft-graph-logs-part-1/#azurehound
- https://kqlquery.com/posts/graphactivitylogs/

## Defender XDR
```KQL
let WhitelistedObjects = dynamic(["obj1", "obj2"]);
let UniqueRequestThreshold = 1000; // Depends on Entra ID tentant size. You can use the function 0.5 * TotalAzure Resources to get this number. KQL: arg("").Resources | count
let TotalResponseSizeTHreshold = 1000000; // Depends on Entra ID tentant size
let ResourceThreshold = 4;
let ReconResources = dynamic(["organization","groups","devices","applications","users","rolemanagement","serviceprincipals"]);
MicrosoftGraphActivityLogs
| where RequestMethod == "GET"
| where ResponseStatusCode == 200
| extend ParsedUri = tostring(parse_url(RequestUri).Path)
| extend GraphAPIPath = tolower(replace_string(ParsedUri, "//", "/"))
| extend GraphAPIResource = tostring(split(GraphAPIPath, "/")[2])
| where GraphAPIResource in (ReconResources)
| extend ObjectId = coalesce(UserId, ServicePrincipalId)
// Filter whitelist
| where not(ObjectId in (WhitelistedObjects))
| summarize TotalResponseSize = sum(ResponseSizeBytes), UniqueRequests = dcount(RequestId), Requests = make_set(RequestUri, 1000), Paths = make_set(GraphAPIPath), Resources = make_set(GraphAPIResource), UniqueResourceCount = dcount(GraphAPIResource) by UserId, bin(TimeGenerated, 1h), UserAgent, ObjectId
| where UniqueRequests >= UniqueRequestThreshold and TotalResponseSize >= TotalResponseSizeTHreshold and UniqueResourceCount >= ResourceThreshold
```

Explanation

This query is designed to detect potential executions of AzureHound, a tool used by adversaries to gather information about an Azure environment. Here's a simplified breakdown of what the query does:

  1. Data Source: It uses the MicrosoftGraphActivityLogs to analyze activity within an Azure environment.

  2. Filtering Criteria:

    • It looks for GET requests with a successful status code (200), as AzureHound uses GET requests to collect data.
    • It focuses on specific resources that are commonly targeted for reconnaissance, such as organizations, groups, devices, applications, users, role management, and service principals.
  3. Exclusions:

    • Certain objects are whitelisted and excluded from the analysis to reduce false positives.
  4. Data Aggregation:

    • The query calculates the total size of the responses, counts the number of unique requests, and identifies the different resources accessed within one-hour timeframes.
  5. Thresholds:

    • It compares the results against predefined thresholds for the number of unique requests, total response size, and the variety of resources accessed. These thresholds can be adjusted based on the size of the Azure environment.
  6. Detection:

    • If the activity exceeds these thresholds, it suggests potential AzureHound activity, indicating that an adversary might be trying to gather insights into the Azure tenant.

The query helps security analysts identify suspicious activity that could indicate reconnaissance efforts by adversaries using AzureHound.

Details

Bert-Jan Pals profile picture

Bert-Jan Pals

Released: August 14, 2025

Tables

MicrosoftGraphActivityLogs

Keywords

MicrosoftGraphActivityLogsOrganizationGroupsDevicesApplicationsUsersRolemanagementServiceprincipalsUserIdServicePrincipalIdRequestUriGraphAPIPathGraphAPIResourceObjectIdUserAgentTimeGenerated

Operators

letdynamicargcountwhereextendtostringparse_urltolowerreplace_stringsplitcoalescesummarizesumdcountmake_setbin

Actions