A threat hunter should investigate service principal credential additions followed by immediate sign-ins, as this behavior aligns with post-compromise staging tactics used by adversaries to maintain persistence and access. Proactively hunting for this pattern in Azure Sentinel can help identify potential lateral movement or unauthorized access attempts in the environment.
KQL Query
let timeframe = 1d;
let correlationWindow = 30m;
let CredAdded =
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName in~ (
"Add service principal credentials"
)
| where Result =~ "success"
| project
CredAddedTime = TimeGenerated,
SpId = tostring(TargetResources[0].id),
SpName = tostring(TargetResources[0].displayName),
ActorUpn = tostring(InitiatedBy.user.userPrincipalName),
ActorApp = tostring(InitiatedBy.app.displayName),
AuditCorrelationId = CorrelationId;
AADServicePrincipalSignInLogs
| where TimeGenerated >= ago(timeframe)
| where ResultType == 0
| join kind=inner CredAdded on $left.ServicePrincipalId == $right.SpId
| where TimeGenerated between (CredAddedTime .. (CredAddedTime + correlationWindow))
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| project
TimeGenerated,
ServicePrincipalId,
ServicePrincipalName = SpName,
AppId,
IPAddress,
Location,
ResourceDisplayName,
CredAddedTime,
Actor,
AuditCorrelationId,
SignInCorrelationId = CorrelationId
| sort by TimeGenerated desc
id: 02a6931e-0a49-4f78-b535-1947273c5384
name: Service principal credential addition followed by immediate sign-in
description: |
Identifies service principal sign-ins occurring within 30 minutes of a credential
addition to the same service principal. The tight correlation is consistent with
post-compromise staging where credentials are added and used immediately.
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- AADServicePrincipalSignInLogs
tactics:
- Persistence
- CredentialAccess
relevantTechniques:
- T1098.001
- T1528
query: |
let timeframe = 1d;
let correlationWindow = 30m;
let CredAdded =
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName in~ (
"Add service principal credentials"
)
| where Result =~ "success"
| project
CredAddedTime = TimeGenerated,
SpId = tostring(TargetResources[0].id),
SpName = tostring(TargetResources[0].displayName),
ActorUpn = tostring(InitiatedBy.user.userPrincipalName),
ActorApp = tostring(InitiatedBy.app.displayName),
AuditCorrelationId = CorrelationId;
AADServicePrincipalSignInLogs
| where TimeGenerated >= ago(timeframe)
| where ResultType == 0
| join kind=inner CredAdded on $left.ServicePrincipalId == $right.SpId
| where TimeGenerated between (CredAddedTime .. (CredAddedTime + correlationWindow))
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| project
TimeGenerated,
ServicePrincipalId,
ServicePrincipalName = SpName,
AppId,
IPAddress,
Location,
ResourceDisplayName,
CredAddedTime,
Actor,
AuditCorrelationId,
SignInCorrelationId = CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: ServicePrincipalName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
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: Scheduled Job Credential Rotation
Description: A scheduled job runs nightly to rotate credentials for a service principal used by a monitoring tool (e.g., Azure Monitor or Datadog). The credential update is followed by a sign-in to validate the new credentials.
Filter/Exclusion: Exclude sign-ins that occur within 5 minutes of a credential update if the service principal is associated with a known monitoring or automation tool (e.g., servicePrincipalName = "monitoring", clientAppId = "datadog", or jobName = "credential-rotation").
Scenario: Admin Task to Update Service Principal Credentials
Description: An admin manually updates the credentials for a service principal used by a DevOps tool (e.g., Jenkins or Azure DevOps) and then immediately signs in to test the new credentials.
Filter/Exclusion: Exclude sign-ins that occur within 10 minutes of a credential update if the service principal is associated with a DevOps tool (e.g., servicePrincipalName = "jenkins", clientAppId = "azure-devops", or userPrincipalName = "[email protected]").
Scenario: Azure Automation Runbook Execution
Description: An Azure Automation runbook is configured to update credentials for a service principal and then immediately sign in to ensure the credentials are valid.
Filter/Exclusion: Exclude sign-ins that occur within 15 minutes of a credential update if the service principal is associated with an Azure Automation runbook (e.g., jobName = "update-credentials", runbookName = "credential-validation").
Scenario: Service Principal Used for API Integration
Description: A service principal is used to authenticate with an external API (e.g., Salesforce or Okta) and the credentials are updated, followed by a sign-in to test