Query Details

HQ 001 Azure Hound Directory Enumeration

Query

// =========================================================================
// HQ-001: AzureHound / BloodHound – Directory Enumeration via Group Policy Reads
// =========================================================================
// Description : Detects burst reads of GroupLifecyclePolicies, a low-noise
//               signal produced by AzureHound and ROADtools when they map 
//               the tenant's group model. Five or more calls in one hour from
//               the same actor is abnormal for normal user/admin activity.
// MITRE ATT&CK: TA0007 Discovery
//               T1069.003 – Permission Groups Discovery: Cloud Groups
//               T1087.004 – Account Discovery: Cloud Account
// Tools       : AzureHound, BloodHoundAD, ROADtools
// Severity    : Medium
// Data Source : AuditLogs (Azure Active Directory)
// =========================================================================

AuditLogs
| where TimeGenerated > ago(1d)
| where ActivityDisplayName == "GroupLifecyclePolicies_Get"
| extend InitiatedByUPN  = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatedByUser = tostring(InitiatedBy.user.displayName)
| extend InitiatedByApp  = tostring(InitiatedBy.app.displayName)
| extend InitiatedBySPId = tostring(InitiatedBy.app.servicePrincipalId)
| extend Actor = iff(isnotempty(InitiatedByUPN), InitiatedByUPN, InitiatedByApp)
| summarize
    ActionCount    = count(),
    FirstSeen      = min(TimeGenerated),
    LastSeen       = max(TimeGenerated),
    Services       = make_set(LoggedByService),
    CorrelationIds = make_set(CorrelationId, 5)
    by Actor, InitiatedBySPId, AADTenantId, bin(TimeGenerated, 1h)
| where ActionCount >= 5
| extend DurationMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| extend RatePerMinute   = round(toreal(ActionCount) / iff(DurationMinutes == 0, 1.0, toreal(DurationMinutes)), 2)
| project-reorder TimeGenerated, Actor, ActionCount, RatePerMinute, DurationMinutes, FirstSeen, LastSeen
| order by ActionCount desc

Explanation

This query is designed to detect unusual activity related to reading Group Lifecycle Policies in Azure Active Directory. Here's a simple breakdown of what it does:

  1. Data Source: It uses the AuditLogs from Azure Active Directory to track activities.

  2. Time Frame: It looks at logs generated in the last day (1d).

  3. Activity Filter: It specifically filters for activities where the action was "GroupLifecyclePolicies_Get", which indicates reading group policies.

  4. Actor Identification: It identifies who initiated the action, whether it's a user or an application, and labels them as the "Actor".

  5. Summarization: The query groups the data by actor, service principal ID, tenant ID, and hourly time bins. It counts how many times the action was performed, notes the first and last time it was seen, and collects related services and correlation IDs.

  6. Abnormal Activity Detection: It flags cases where an actor performs this action five or more times within an hour, which is considered abnormal for typical user or admin behavior.

  7. Rate Calculation: It calculates how frequently the actions occur per minute during the detected period.

  8. Output: The results are ordered by the number of actions, showing the most active actors first, along with details like the rate of actions per minute and the duration of the activity.

Overall, this query helps identify potential misuse or reconnaissance activities using tools like AzureHound, BloodHoundAD, or ROADtools by detecting bursts of group policy reads that deviate from normal behavior.

Details

David Alonso profile picture

David Alonso

Released: April 6, 2026

Tables

AuditLogs

Keywords

AuditLogsTimeGeneratedActivityDisplayNameInitiatedByUPNInitiatedByUserInitiatedByAppInitiatedBySPIdActorActionCountFirstSeenLastSeenServicesCorrelationIdsAADTenantIdDurationMinutesRatePerMinute

Operators

whereextendiffisnotemptytostringsummarizecountminmaxmake_setbybinagodatetime_diffroundtorealproject-reorderorder by

Actions