A threat actor may add themselves as an owner to an Entra ID service principal to gain full credential management privileges and deploy malicious credentials. SOC teams should proactively hunt for this behavior to detect potential credential compromise and unauthorized access in their Azure Sentinel environment.
KQL Query
let timeframe = 1d;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Add owner to service principal"
| where Result =~ "success"
| mv-apply T = TargetResources on (
summarize
TargetSpName = take_anyif(tostring(T.displayName), tostring(T.type) =~ "ServicePrincipal"),
TargetSpId = take_anyif(tostring(T.id), tostring(T.type) =~ "ServicePrincipal"),
NewOwnerUpn = take_anyif(tostring(T.userPrincipalName), tostring(T.type) =~ "User"),
NewOwnerId = take_anyif(tostring(T.id), tostring(T.type) =~ "User")
)
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| extend AccountName = iff(NewOwnerUpn has "@",
tostring(split(NewOwnerUpn, "@")[0]), NewOwnerUpn)
| extend AccountUPNSuffix = iff(NewOwnerUpn has "@",
tostring(split(NewOwnerUpn, "@")[1]), "")
| project
TimeGenerated,
TargetSpName,
TargetSpId,
NewOwnerUpn,
AccountName,
AccountUPNSuffix,
NewOwnerId,
Actor,
ActorIp,
CorrelationId
| sort by TimeGenerated desc
id: a15c8f25-9e6c-4d1f-b685-b5113fe58307
name: New owner added to Entra ID service principal
description: Identifies additions of new owners to Entra ID service principals. SP ownership grants full credential management capability; an attacker who adds themselves as owner can subsequently add credentials and authenticate as the SP.
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
relevantTechniques:
- T1098.001
query: |
let timeframe = 1d;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Add owner to service principal"
| where Result =~ "success"
| mv-apply T = TargetResources on (
summarize
TargetSpName = take_anyif(tostring(T.displayName), tostring(T.type) =~ "ServicePrincipal"),
TargetSpId = take_anyif(tostring(T.id), tostring(T.type) =~ "ServicePrincipal"),
NewOwnerUpn = take_anyif(tostring(T.userPrincipalName), tostring(T.type) =~ "User"),
NewOwnerId = take_anyif(tostring(T.id), tostring(T.type) =~ "User")
)
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| extend AccountName = iff(NewOwnerUpn has "@",
tostring(split(NewOwnerUpn, "@")[0]), NewOwnerUpn)
| extend AccountUPNSuffix = iff(NewOwnerUpn has "@",
tostring(split(NewOwnerUpn, "@")[1]), "")
| project
TimeGenerated,
TargetSpName,
TargetSpId,
NewOwnerUpn,
AccountName,
AccountUPNSuffix,
NewOwnerId,
Actor,
ActorIp,
CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: NewOwnerUpn
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ActorIp
version: 1.0.0
metadata:
source:
kind: Community
author:
name: descambiado
support:
tier: Community
categories:
domains: [ "Security - Threat Protection", "Identity" ]
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: A system administrator adds a new owner to an Entra ID service principal as part of routine access management for a third-party application.
Filter/Exclusion: Exclude users with the UserPrincipalName containing admin or svcadmin and filter by ServicePrincipalName matching known third-party apps (e.g., AzureADPasswordCredential, MicrosoftGraph).
Scenario: A scheduled job or automation tool (e.g., Azure DevOps pipeline or Terraform) provisions a new service principal and assigns ownership during infrastructure deployment.
Filter/Exclusion: Exclude events where the OperationName is CreateServicePrincipal or UpdateServicePrincipal and filter by Caller matching known automation tools (e.g., AzureDevOps, Terraform).
Scenario: A user is added as an owner to an Entra ID service principal during a security audit or compliance check to review access controls.
Filter/Exclusion: Exclude users with the UserPrincipalName containing audit, compliance, or security and filter by ServicePrincipalName matching audit-related service principals.
Scenario: A service account or managed identity is granted ownership of an Entra ID service principal to enable automated credential management in a CI/CD pipeline.
Filter/Exclusion: Exclude users with the UserPrincipalName containing ci, cd, pipeline, or managedidentity and filter by ServicePrincipalName matching CI/CD tools (e.g., AzureDevOps, GitHubActions).
Scenario: An IT support team member adds a temporary owner to an Entra ID service principal to troubleshoot an application issue.
Filter/Exclusion: Exclude users with the UserPrincipalName containing support, it, or helpdesk and filter by ServicePrincipalName matching