Added Ownership To Workload Identity Workload Identity Info
Query
AuditLogs
| where OperationName in ("Add owner to application", "Add owner to service principal")
| extend InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend InitiatingUserOrAppId = iff(isnotempty(InitiatedBy.user.id), tostring(InitiatedBy.user.id), tostring(InitiatedBy.app.id))
| extend InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| where TargetResources_modifiedProperties.displayName == "Application.AppId" or TargetResources_modifiedProperties.displayName == "ServicePrincipal.AppId"
| extend ApplicationId = replace_string(tostring(TargetResources_modifiedProperties.newValue), '"', '')
| extend AddedOwnerId = tostring(TargetResources.id)
| join kind=leftouter(
PrivilegedWorkloadIdentityInfo
| project
WorkloadIdentityName,
WorkloadIdentityType,
ApplicationObjectId,
ServicePrincipalObjectId,
ApplicationId,
IsFirstPartyApp,
EntraIdRoles,
AppRolePermissions,
WorkloadIdClassification = EnterpriseAccessModelTiering
)
on ApplicationId
| join kind=leftouter (
UnifiedIdentityInfo
| project ObjectId, InitiatingUserOrAppClassification = Classification
)
on $left.InitiatingUserOrAppId == $right.ObjectId
| join kind=leftouter (
UnifiedIdentityInfo
| project
AddedOwnerClassification = Classification,
AddedOwnerDisplayName = ObjectDisplayName,
ObjectId
)
on $left.AddedOwnerId == $right.ObjectId
// Compare Classification of Application with Owner to detect "Tiering" breach, Allowlist all Control Plane roles
| extend TieringBreach = iff(parse_json(tostring(parse_json(AddedOwnerClassification))) !contains WorkloadIdClassification and (parse_json(tostring(parse_json(AddedOwnerClassification))) !contains "ControlPlane"), "True", "False")
| extend Severity = iff(TieringBreach == "True", "High", "Medium")
| extend OperationAlertTitle = replace_string(OperationName,"Add ","")
| project
TimeGenerated,
OperationName,
OperationAlertTitle,
WorkloadIdentityName,
WorkloadIdentityType,
WorkloadIdClassification,
ApplicationObjectId,
ApplicationId,
ServicePrincipalObjectId,
InitiatingUserOrApp,
InitiatingUserOrAppId,
InitiatingIpAddress,
AddedOwnerId,
AddedOwnerClassification,
AddedOwnerDisplayName,
EntraIdRoles,
AppRolePermissions,
IsFirstPartyApp,
TieringBreach,
SeverityExplanation
This query detects changes to the ownership of an application or service principal. It checks if the assigned owner is unprivileged or lower privileged and increases the alert severity to high in such cases. The query retrieves information about the operation, workload identity, initiating user or app, initiating IP address, added owner, and other relevant details. It also performs joins with other tables to gather additional information. The query includes logic to compare the classification of the application with the owner to detect any breach in tiering. The severity of the alert is determined based on this comparison. The query is scheduled to run every hour and has a trigger threshold of 0. The results are used to create incidents and the incident grouping configuration is enabled. The alert details are overridden to include relevant information and a link to documentation. The query also includes custom details and entity mappings for better incident management.
Details

Thomas Naunheim
Released: November 19, 2023
Tables
Keywords
Operators
Severity
MediumTactics
MITRE Techniques
Frequency: 1h
Period: 1h