↳ Source GitHubRègle analytiqueMedium

Service Principal Assigned App Role With Sensitive Access

Description

'Detects a Service Principal being assigned an app role that has sensitive access such as Mail.Read. A threat actor who compromises a Service Principal may assign it an app role to allow it to access sensitive data, or to perform other actions. Ensure that any assignment to a Service Principal is valid and appropriate. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-granted-highly-privileged-permissions'
Type de règle
Scheduled
Version
2.0.0
Fréquence
1d
Période analysée
1d
Déclenchement
gt 0

Couverture MITRE déclarée

Sources déclarées

Métadonnées du fichier source. Aucune dépendance déduite du KQL.

Connecteurs

Types de données

Requête KQL

Requête originale, sans modification.

// Add other permissions to this list as needed
let permissions = dynamic([".All", "ReadWrite", "Mail.", "offline_access", "Files.Read", "Notes.Read", "ChannelMessage.Read", "Chat.Read", "TeamsActivity.Read",
"Group.Read", "EWS.AccessAsUser.All", "EAS.AccessAsUser.All"]);
let auditList = 
AuditLogs
| where OperationName =~ "Add app role assignment to service principal"
| mv-expand TargetResources[0].modifiedProperties
| extend TargetResources_0_modifiedProperties = column_ifexists("TargetResources_0_modifiedProperties", '')
| where isnotempty(TargetResources_0_modifiedProperties)
;
let detailsList = auditList
| where TargetResources_0_modifiedProperties.displayName =~ "AppRole.Value" or TargetResources_0_modifiedProperties.displayName =~ "DelegatedPermissionGrant.Scope"
| extend Permissions = split((parse_json(tostring(TargetResources_0_modifiedProperties.newValue))), " ")
| where Permissions has_any (permissions)
| summarize AddedPermissions=make_set(Permissions,200) by CorrelationId
| join kind=inner auditList on CorrelationId
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend InitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName),InitiatingUserPrincipalName, InitiatingAppName))
| extend displayName = tostring(TargetResources_0_modifiedProperties.displayName), newValue = tostring(parse_json(tostring(TargetResources_0_modifiedProperties.newValue)))
| where displayName == "ServicePrincipal.ObjectID" or displayName == "ServicePrincipal.DisplayName"
| extend displayName = case(displayName == "ServicePrincipal.ObjectID", "ServicePrincipalObjectID", displayName == "ServicePrincipal.DisplayName", "ServicePrincipalDisplayName", displayName)
| project TimeGenerated, CorrelationId, Id, AddedPermissions = tostring(AddedPermissions), InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIPAddress, InitiatingUserPrincipalName, InitiatedBy, displayName, newValue
;
detailsList | project Id, displayName, newValue
| evaluate pivot(displayName, make_set(newValue))
| join kind=inner detailsList on Id
| extend ServicePrincipalObjectID = todynamic(column_ifexists("ServicePrincipalObjectID", "")), ServicePrincipalDisplayName = todynamic(column_ifexists("ServicePrincipalDisplayName", ""))
| mv-expand ServicePrincipalObjectID, ServicePrincipalDisplayName
| project-away Id1, displayName, newValue
| extend ServicePrincipalObjectID = tostring(ServicePrincipalObjectID), ServicePrincipalDisplayName = tostring(ServicePrincipalDisplayName)
| summarize FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), EventIds = make_set(Id,200) by CorrelationId, AddedPermissions, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIPAddress, InitiatingUserPrincipalName, InitiatedBy, ServicePrincipalDisplayName, ServicePrincipalObjectID
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])

Entités déclarées

AccountIP

Contenus associés

Liens établis à partir des identifiants déclarés et des manifests des solutions.

Traçabilité de la source

GitHub

Les valeurs affichées proviennent des fichiers du dépôt Azure/Azure-Sentinel. Elles décrivent le modèle publié, pas la configuration de votre workspace.

Identifiant source
dd78a122-d377-415a-afe9-f22e08d2112c
Autres fichiers source 1Detections/AuditLogs/ServicePrincipalAssignedAppRoleWithSensitiveAccess.yamlsource ↗
GSTEP / SUIVI DU CATALOGUE

Ajouté au catalogue : 16 sept. 2026 · 05:49 UTC
Dernier changement observé : 16 sept. 2026 · 05:49 UTC

Dates de synchronisation GSTEP, distinctes des dates de publication du contenu source.