Unusual audit activity initiated by an application may indicate an adversary attempting to manipulate user or group access through Azure Apps. SOC teams should proactively hunt for this behavior to detect potential privilege escalation or unauthorized access modifications in their Azure Sentinel environment.
KQL Query
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let auditLookback = starttime - 14d;
let propertyIgnoreList = dynamic(["TargetId.UserType", "StsRefreshTokensValidFrom", "LastDirSyncTime", "DeviceOSVersion", "CloudDeviceOSVersion", "DeviceObjectVersion"]);
let appIgnoreList = dynamic(["Microsoft Azure AD Group-Based Licensing"]);
let AuditTrail = AuditLogs
| where TimeGenerated between(auditLookback..starttime)
| where isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend InitiatedByApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ModProps = TargetResources.[0].modifiedProperties
| extend InitiatedByIpAddress = tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)
| extend TargetUserPrincipalName = tolower(tostring(TargetResources.[0].userPrincipalName))
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| mv-expand ModProps
| where isnotempty(tostring(parse_json(tostring(ModProps.newValue))[0]))
| extend PropertyName = tostring(ModProps.displayName), newValue = tostring(parse_json(tostring(ModProps.newValue))[0])
| where PropertyName !in~ (propertyIgnoreList) and (PropertyName !~ "Action Client Name" and newValue !~ "DirectorySync") and (PropertyName !~ "Included Updated Properties" and newValue !~ "LastDirSyncTime")
| where InitiatedByApp !in~ (appIgnoreList) and OperationName !~ "Change user license"
| summarize by OperationName, InitiatedByApp, TargetUserPrincipalName, InitiatedByIpAddress, TargetResourceName, PropertyName;
let AccountMods = AuditLogs
| where TimeGenerated >= starttime
| where isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend InitiatedByApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ModProps = TargetResources.[0].modifiedProperties
| extend InitiatedByIpAddress = tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)
| extend TargetUserPrincipalName = tolower(tostring(TargetResources.[0].userPrincipalName))
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| mv-expand ModProps
| where isnotempty(tostring(parse_json(tostring(ModProps.newValue))[0]))
| extend PropertyName = tostring(ModProps.displayName), newValue = tostring(parse_json(tostring(ModProps.newValue))[0])
| where PropertyName !in~ (propertyIgnoreList) and (PropertyName !~ "Action Client Name" and newValue !~ "DirectorySync") and (PropertyName !~ "Included Updated Properties" and newValue !~ "LastDirSyncTime")
| where InitiatedByApp !in~ (appIgnoreList) and OperationName !~ "Change user license"
| extend ModifiedProps = pack("PropertyName",PropertyName,"newValue",newValue, "Id", Id, "CorrelationId", CorrelationId)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), Activity = make_bag(ModifiedProps) by Type, InitiatedByApp, TargetUserPrincipalName, InitiatedByIpAddress, TargetResourceName, Category, OperationName, PropertyName;
let RareAudits = AccountMods | join kind= leftanti (
AuditTrail
) on OperationName, InitiatedByApp, InitiatedByIpAddress, TargetUserPrincipalName;//, PropertyName; //uncomment if you want to see Rare Property changes.
RareAudits
| summarize StartTime = min(StartTimeUtc), EndTime = max(EndTimeUtc), make_set(Activity), make_set(PropertyName) by InitiatedByApp, OperationName, TargetUserPrincipalName, InitiatedByIpAddress, TargetResourceName
| order by TargetUserPrincipalName asc, StartTime asc
| extend timestamp = StartTime, AccountCustomEntity = TargetUserPrincipalName, HostCustomEntity = iff(set_PropertyName has_any ('DeviceOSType', 'CloudDeviceOSType'), TargetResourceName, ''), IPCustomEntity = InitiatedByIpAddress
id: 5c799718-b361-4a91-9cb2-0c291e602707
name: Rare Audit activity initiated by App
description: |
'Compares the current day to last 14 days of audits to identify new audit activities. Useful for tracking malicious activity related to user/group additions/removals by Azure Apps and automated approvals.'
description_detailed: |
'Compares the current day to the last 14 days of audits to identify new audit activities by
OperationName, InitiatedByApp, UserPrincipalName, PropertyName, newValue
This can be useful when attempting to track down malicious activity related to additions of new users,
additions to groups, removal from groups by Azure Apps and automated approvals.'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
- LateralMovement
relevantTechniques:
- T1136
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let auditLookback = starttime - 14d;
let propertyIgnoreList = dynamic(["TargetId.UserType", "StsRefreshTokensValidFrom", "LastDirSyncTime", "DeviceOSVersion", "CloudDeviceOSVersion", "DeviceObjectVersion"]);
let appIgnoreList = dynamic(["Microsoft Azure AD Group-Based Licensing"]);
let AuditTrail = AuditLogs
| where TimeGenerated between(auditLookback..starttime)
| where isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend InitiatedByApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ModProps = TargetResources.[0].modifiedProperties
| extend InitiatedByIpAddress = tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)
| extend TargetUserPrincipalName = tolower(tostring(TargetResources.[0].userPrincipalName))
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| mv-expand ModProps
| where isnotempty(tostring(parse_json(tostring(ModProps.newValue))[0]))
| extend PropertyName = tostring(ModProps.displayName), newValue = tostring(parse_json(tostring(ModProps.newValue))[0])
| where PropertyName !in~ (propertyIgnoreList) and (PropertyName !~ "Action Client Name" and newValue !~ "DirectorySync") and (PropertyName !~ "Included Updated Properties" and newValue !~ "LastDirSyncTime")
| where InitiatedByApp !in~ (appIgnoreList) and OperationName !~ "Change user license"
| summarize by OperationName, InitiatedByApp, TargetUserPrincipalName, InitiatedByIpAddress, TargetResourceName, PropertyName;
let AccountMods = AuditLogs
| where TimeGenerated >= starttime
| where isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend InitiatedByApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ModProps = TargetResources.[0].modifiedProperties
| extend InitiatedByIpAddress = tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)
| extend TargetUserPrincipalName = tolower(tostring(TargetResources.[0].userPrincipalName))
| extend TargetResourceNam
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: Scheduled Azure Automation Runbook Execution
Description: A legitimate Azure Automation runbook is configured to perform routine audit-related tasks, such as updating user roles or generating compliance reports. This activity may trigger the rule due to the audit-related nature of the task.
Filter/Exclusion: AzureActivityType = "AutomationRunbook" or ResourceGroup = "Compliance-Reports"
Scenario: Azure AD Privileged Role Management Trust Framework (TRUSTe) Audit Job
Description: The TRUSTe audit job, which is part of Azure AD Privileged Identity Management (PIM), periodically audits role assignments and access. This is a known legitimate activity that may be flagged by the rule.
Filter/Exclusion: ActivityName = "TRUSTe Audit Job" or OperationName = "AuditRoleAssignment"
Scenario: Azure Backup Vault Scheduled Backup Job
Description: A scheduled backup job initiated by Azure Backup Vault may generate audit logs related to resource access or configuration changes, which could be misinterpreted as suspicious activity.
Filter/Exclusion: ResourceType = "Microsoft.RecoveryServices/vaults" or OperationName = "Backup Vault Job Execution"
Scenario: Azure DevOps Pipeline Audit Logging
Description: A CI/CD pipeline in Azure DevOps may include audit logging steps that capture user activity or access to resources, which could be flagged by the rule.
Filter/Exclusion: OperationName = "AuditLogCapture" or ResourceGroup = "DevOps-Logs"
Scenario: Azure Security Center Automated Compliance Scan
Description: Azure Security Center performs regular compliance scans that may involve audit-related operations, such as checking access controls or resource configurations.
Filter/Exclusion: ResourceType = "Microsoft.Security/assessments" or OperationName = "ComplianceScan"