Query Details

Added Ownership To Workload Identity Workload Identity Info

Query

id: 8cd003d2-7a8b-4eba-b50a-39287757120d
name: Added Ownership to workload identity (WorkloadIdentityInfo)
version: 1.0.0
kind: Scheduled
description: 'Detects changes to the ownership of application and service principal. Alert will be increased to high if assigned owner is unprivileged or lower privileged. Monitor these changes to avoid privileged escalation paths and breach of tiering model. Avoid using ownership and assign delegated permissions by using object- and permission-scoped Entra ID roles (based on your requirements). Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#new-owner'
severity: Medium
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1078
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,
      Severity

suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Added {{OperationAlertTitle}} with privileges on {{WorkloadIdClassification}} '
  alertDescriptionFormat: |-
    {{AddedOwnerClassification}} user has been added as owner to {{WorkloadIdentityName}} with privileges on {{WorkloadIdClassification}}. Avoid using ownership and assign delegated permissions by using object- and permission-scoped Entra ID roles (based on your requirements). Verify the assignment to prevent tiering breach and permanent privileged access to a workload identity.
      Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#new-owner
  alertSeverityColumnName: Severity
  alertDynamicProperties: []
customDetails:
  WorkloadIdentityName: WorkloadIdentityName
  WorkloadIdentityType: WorkloadIdentityType
  ServicePrincipalId: ServicePrincipalObjectId
  ApplicationId: ApplicationId
  IsFirstPartyApp: IsFirstPartyApp
  PrivilegedAccess: WorkloadIdClassification
  EntraDirectoryRoles: EntraIdRoles
  MSGraphRoles: AppRolePermissions
  TieringBreach: TieringBreach
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: InitiatingUserOrAppId
- entityType: Account
  fieldMappings:
  - identifier: AadUserId
    columnName: AddedOwnerId
- entityType: CloudApplication
  fieldMappings:
  - identifier: AppId
    columnName: ApplicationId
- entityType: CloudApplication
  fieldMappings:
  - identifier: Name
    columnName: WorkloadIdentityName
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: InitiatingIpAddress
suppressionDuration: 5h

Explanation

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 profile picture

Thomas Naunheim

Released: November 19, 2023

Tables

AuditLogsPrivilegedWorkloadIdentityInfoUnifiedIdentityInfo

Keywords

Devices,Intune,User,Ownership,WorkloadIdentityInfo,AuditLogs,OperationName,InitiatingUserOrApp,InitiatingUserOrAppId,InitiatingIpAddress,TargetResources,ApplicationId,AddedOwnerId,PrivilegedWorkloadIdentityInfo,UnifiedIdentityInfo,TieringBreach,Severity,TimeGenerated,OperationAlertTitle,WorkloadIdentityName,WorkloadIdentityType,WorkloadIdClassification,ApplicationObjectId,ServicePrincipalObjectId,AddedOwnerClassification,AddedOwnerDisplayName,EntraIdRoles,AppRolePermissions,IsFirstPartyApp

Operators

whereextendiffisnotemptytostringmv-expandjoinprojectreplace_stringparse_jsoncontains"True""False"TimeGeneratedTimeGeneratedOperationNameOperationAlertTitleWorkloadIdentityNameWorkloadIdentityTypeWorkloadIdClassificationApplicationObjectIdApplicationIdServicePrincipalObjectIdInitiatingUserOrAppInitiatingUserOrAppIdInitiatingIpAddressAddedOwnerIdAddedOwnerClassificationAddedOwnerDisplayNameEntraIdRolesAppRolePermissionsIsFirstPartyAppTieringBreachSeverity

Actions