New service principal granted admin consent within one hour of creation
Description
Identifies service principals that received an app role assignment or admin consent within one hour of being registered in the tenant. Register-then-consent is a documented persistence pattern after privileged account compromise.
Query · kql
let timeframe = 1d;
let correlationWindow = 1h;
let NewSP =
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Add service principal"
| where Result =~ "success"
| extend CreatingActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend CreatingActorApp = tostring(InitiatedBy.app.displayName)
| mv-expand TargetResource = TargetResources
| where tostring(TargetResource.type) =~ "ServicePrincipal"
| project
SpCreatedTime = TimeGenerated,
SpId = tostring(TargetResource.id),
SpName = tostring(TargetResource.displayName),
CreatingActorUpn,
CreatingActorApp;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName in~ (
"Add app role assignment to service principal",
"Consent to application"
)
| where Result =~ "success"
| extend ConsentActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ConsentActorApp = tostring(InitiatedBy.app.displayName)
| extend ConsentActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| mv-expand TargetResource = TargetResources
| where tostring(TargetResource.type) =~ "ServicePrincipal"
| extend ConsentTargetId = tostring(TargetResource.id)
| join kind=inner NewSP on $left.ConsentTargetId == $right.SpId
| where TimeGenerated between (SpCreatedTime .. (SpCreatedTime + correlationWindow))
| extend CreatingActor = iff(isnotempty(CreatingActorUpn), CreatingActorUpn, CreatingActorApp)
| extend ConsentActor = iff(isnotempty(ConsentActorUpn), ConsentActorUpn, ConsentActorApp)
| extend AccountName = iff(ConsentActor has "@",
tostring(split(ConsentActor, "@")[0]), ConsentActor)
| extend AccountUPNSuffix = iff(ConsentActor has "@",
tostring(split(ConsentActor, "@")[1]), "")
| project
TimeGenerated,
SpName,
SpId,
OperationName,
CreatingActor,
ConsentActor,
AccountName,
AccountUPNSuffix,
ConsentActorIp,
SpCreatedTime,
CorrelationId
| sort by TimeGenerated desc