Adversaries may use application discovery techniques to identify and target running applications within the environment, leveraging T1136 to gather information about installed software. SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential lateral movement or persistence attempts early.
KQL Query
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let auditLookback = starttime - 14d;
// Setting threshold to 3 as a default, change as needed. Any operation that has been initiated by a user or app more than 3 times in the past 30 days will be exluded
let threshold = 3;
// Helper function to extract relevant fields from AuditLog events
let auditLogEvents = view (startTimeSpan:timespan) {
AuditLogs | where TimeGenerated >= auditLookback
| extend ModProps = TargetResources.[0].modifiedProperties
| extend IpAddress = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)),
tostring(parse_json(tostring(InitiatedBy.user)).ipAddress), tostring(parse_json(tostring(InitiatedBy.app)).ipAddress))
| extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)),
tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| mvexpand ModProps
| extend PropertyName = tostring(ModProps.displayName), newValue = replace('\"',"",tostring(ModProps.newValue));
};
// Get just the InitiatedBy and CorrleationId so we can look at associated audit activity
// 2 other operations that can be part of malicious activity in this situation are
// "Add OAuth2PermissionGrant" and "Add service principal", replace the below if you are interested in those as starting points for OperationName
let HistoricalConsent = auditLogEvents(auditLookback)
| where OperationName == "Consent to application"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count()
by Type, InitiatedBy, IpAddress, TargetResourceName, Category, OperationName, PropertyName, newValue, CorrelationId, Id
// Remove comment below to only include operations initiated by a user or app that is above the threshold for the last 30 days
//| where OperationCount > threshold
;
let Correlate = HistoricalConsent
| summarize by InitiatedBy, CorrelationId;
// 2 other operations that can be part of malicious activity in this situation are
// "Add OAuth2PermissionGrant" and "Add service principal", replace the below if you changed the starting OperationName above
let allOtherEvents = auditLogEvents(auditLookback)
| where OperationName != "Consent to application";
// Gather associated activity based on audit activity for "Consent to application" and InitiatedBy and CorrleationId
let CorrelatedEvents = Correlate
| join allOtherEvents on InitiatedBy, CorrelationId
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
by Type, InitiatedBy, IpAddress, TargetResourceName, Category, OperationName, PropertyName, newValue, CorrelationId, Id
;
// Union the results
let Results = (union isfuzzy=true HistoricalConsent,CorrelatedEvents);
// newValues that are simple semi-colon separated, make those dynamic for easy viewing and Aggregate into the PropertyUpdate set based on CorrelationId and Id(DirectoryId)
Results
| extend newValue = split(newValue, ";")
| extend PropertyUpdate = pack(PropertyName, newValue, "Id", Id)
// Extract scope requested
| extend perms = tostring(parse_json(tostring(PropertyUpdate.["ConsentAction.Permissions"]))[0])
| extend scope = extract('Scope:\\s*([^,\\]]*)',1, perms)
// Filter out some common openid, and low privilege request scopes - uncomment line below to filter out where no scope is requested
//| where isnotempty(scope)
| where scope !contains 'openid' and scope !in ('user_impersonation','User.Read')
| summarize StartTime = min(StartTimeUtc), EndTime = max(EndTimeUtc), PropertyUpdateSet = make_bag(PropertyUpdate) , make_set(scope)
by InitiatedBy, IpAddress, TargetResourceName, OperationName, CorrelationId
| extend timestamp = StartTime, AccountCustomEntity = InitiatedBy, IPCustomEntity = IpAddress
// uncommnet below to summarize by app if many results
//| summarize make_set(InitiatedBy), make_set(IpAddress), make_set(PropertyUpdateSet) by TargetResourceName, tostring(set_scope)
id: b09d6e57-c48b-491d-9c2b-ab73018e6534
name: Consent to Application discovery
description: |
'This query looks at the last 14 days for "Consent to application" operation by a user/app which could potentially mean unauthorized access. Additional context is added from AuditLogs based on CorrleationId from the same account that performed the action.'
description_detailed: |
'This query looks at the last 14 days for any "Consent to application" operation
occurs by a user or app. This could indicate that permissions to access the listed AzureApp
was provided to a malicious actor. Consent to appliction, Add service principal and
Add OAuth2PermissionGrant events should be rare. If available, additional context is added
from the AuditLogs based on CorrleationId from the same account that performed "Consent to
application".
For further information on AuditLogs please see
https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities
This may help detect the Oauth2 attack that can be initiated by this publicly available tool
https://github.com/fireeye/PwnAuth'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
relevantTechniques:
- T1136
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let auditLookback = starttime - 14d;
// Setting threshold to 3 as a default, change as needed. Any operation that has been initiated by a user or app more than 3 times in the past 30 days will be exluded
let threshold = 3;
// Helper function to extract relevant fields from AuditLog events
let auditLogEvents = view (startTimeSpan:timespan) {
AuditLogs | where TimeGenerated >= auditLookback
| extend ModProps = TargetResources.[0].modifiedProperties
| extend IpAddress = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)),
tostring(parse_json(tostring(InitiatedBy.user)).ipAddress), tostring(parse_json(tostring(InitiatedBy.app)).ipAddress))
| extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)),
tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName))
| mvexpand ModProps
| extend PropertyName = tostring(ModProps.displayName), newValue = replace('\"',"",tostring(ModProps.newValue));
};
// Get just the InitiatedBy and CorrleationId so we can look at associated audit activity
// 2 other operations that can be part of malicious activity in this situation are
// "Add OAuth2PermissionGrant" and "Add service principal", replace the below if you are interested in those as starting points for OperationName
let HistoricalConsent = auditLogEvents(auditLookback)
| where OperationName == "Consent to application"
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: A system administrator is performing a scheduled backup using Veeam Backup & Replication that temporarily discovers application data.
Filter/Exclusion: Exclude processes related to veeambackup.exe or any known backup tools, or filter by process name or command line arguments containing “backup” or “snapshot”.
Scenario: An IT admin is running a Microsoft Intune device compliance scan that includes application discovery as part of the policy check.
Filter/Exclusion: Exclude processes associated with intunewin32.exe or any processes with “Intune” in their name, or filter by the user account used for Intune tasks.
Scenario: A PowerShell script is executed by a DevOps team to inventory installed applications across the environment as part of a deployment pipeline.
Filter/Exclusion: Exclude processes with powershell.exe where the command line includes “Get-InstalledApplication” or similar inventory scripts, or filter by the user account used for DevOps tasks.
Scenario: A Windows Task Scheduler job is configured to run Process Explorer or Process Monitor to collect application discovery data for troubleshooting.
Filter/Exclusion: Exclude processes launched by the Task Scheduler (e.g., schtasks.exe or TaskScheduler), or filter by the command line containing “Process Explorer” or “Process Monitor”.
Scenario: A Microsoft Endpoint Manager (MEM) compliance scan is initiated to check application compliance, which includes discovery of installed applications.
Filter/Exclusion: Exclude processes related to MicrosoftIntuneManagementExtension.exe or any processes with “MEM” or “Compliance” in their name, or filter by the user account used for MEM tasks.