Query Details
// New access credential added to sensitive Application or Service Principal from non-privileged user
// Modified version from "New access credential added to Application or Service Principal" (79566f41-df67-4e10-a703-c38a6213afd8)
let SensitiveMsGraphPermissions = externaldata(EAMTierLevel: string, Category: string, APIPermission: string)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_MsGraphPermissions.json"] with(format='multijson');
let SensitiveAadDirectoryRoles = externaldata(EAMTierLevel: string, Category: string, id: string)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_AadDirectoryRoles.json"] with(format='multijson');
AuditLogs
| where TimeGenerated >ago(90d)
| where OperationName has_any ("Add service principal", "Certificates and secrets management") // captures "Add service principal", "Add service principal credentials", and "Update application - Certificates and secrets management" events
| where Result =~ "success"
| where tostring(InitiatedBy.user.userPrincipalName) has "@" or tostring(InitiatedBy.app.displayName) has "@"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "Application"
| extend AppDisplayName = tostring(TargetResource.displayName),
AppId = tostring(TargetResource.id),
AppType = tostring(TargetResource.type),
keyEvents = TargetResource.modifiedProperties
)
| mv-apply Property = keyEvents on
(
where Property.displayName =~ "KeyDescription"
| extend new_value_set = parse_json(tostring(Property.newValue)),
old_value_set = parse_json(tostring(Property.oldValue))
)
| where old_value_set != "[]"
| extend diff = set_difference(new_value_set, old_value_set)
| where isnotempty(diff)
| parse diff with * "KeyIdentifier=" keyIdentifier:string ",KeyType=" keyType:string ",KeyUsage=" keyUsage:string ",DisplayName=" keyDisplayName:string "]" *
| where keyUsage =~ "Verify"
| mv-apply AdditionalDetail = AdditionalDetails on
(
where AdditionalDetail.key =~ "User-Agent"
| extend UserAgent = tostring(AdditionalDetail.value)
)
| 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))
| project-away diff, new_value_set, old_value_set
| extend timestamp = TimeGenerated, Name = tostring(split(InitiatingUserOrApp,'@',0)[0]), UPNSuffix = tostring(split(InitiatingUserOrApp,'@',1)[0])
| join kind=inner(
AADServicePrincipals_CL
| mv-expand parse_json(APP_s), parse_json(SPAppRoleAssignments_s)
| mv-expand SPAppRoleAssignments_s.AppRolePermissionSensitivity, SPAppRoleAssignments_s.AppRolePermission, parse_json(SPAADRoleAssignments_s)
| mv-expand SPAADRoleAssignments_s.roleDefinitionName, SPAADRoleAssignments_s.roleDefinitionId
| extend AppRolePermission = tostring(SPAppRoleAssignments_s_AppRolePermission)
| extend AadDirectoryRoleId = tostring(SPAADRoleAssignments_s_roleDefinitionId)
| extend AadDirectoryRoleName = tostring(SPAADRoleAssignments_s_roleDefinitionName)
// Option A: Filter on classification from AadSpInsights
//| where SPAADRoleAssignments_s_roleDefinitionId == true or SPAppRoleAssignments_s_AppRolePermissionSensitivity == "critical"
// Option B: Filter on enrichment from external classification schema
| join kind=leftouter(
SensitiveMsGraphPermissions | project AppRolePermissionTierLevel = EAMTierLevel, AppRolePermissionCategory = Category, APIPermission
) on $left.AppRolePermission == $right.APIPermission
| join kind=leftouter(
SensitiveAadDirectoryRoles | project AadDirectoryRoleTierLevel = EAMTierLevel, AadDirectoryRoleTierCategory = Category, id
)on $left.AadDirectoryRoleId == $right.id
| extend AppClassification = iif((AppRolePermissionTierLevel == "ControlPlane" or AadDirectoryRoleTierLevel == "ControlPlane"), "ControlPlane", "ManagementDataPlane")
| project APPObjectId = tostring(parse_json(APP_s).APPObjectId), APPDisplayName = tostring(parse_json(APP_s).APPDisplayName), ObjectId_g, ObjectType_s, AppRolePermission, AadDirectoryRoleName, AppClassification, AppRolePermissionCategory, AadDirectoryRoleTierCategory, tostring(parse_json(APP_s).APPSignInAudience)
| distinct APPObjectId, APPDisplayName, ObjectId_g, ObjectType_s, AppClassification
) on $left.AppId == $right.APPObjectId
| join kind=inner (
AADPrivilegedEAM_CL
| where TimeGenerated > ago(14d)
| summarize arg_max(TimeGenerated, *) by ObjectId_g
| extend InitiatingUserOrAppClassification = iff(isnotempty(parse_json(Classification_s)), parse_json(Classification_s), "UserAccess")
| project ObjectId_g, AdminTierLevel_s, AdminTierLevelName_s, InitiatingUserOrAppClassification
)
on $left.InitiatingUserOrAppId == $right.ObjectId_g
| where parse_json(tostring(parse_json(InitiatingUserOrAppClassification))) !contains AppClassification
| project-reorder TimeGenerated, OperationName, InitiatingUserOrApp, InitiatingUserOrAppId, InitiatingIpAddress, InitiatingUserOrAppClassification, UserAgent, AppDisplayName, AppId, AppType, AppClassification, keyDisplayName, keyType, keyUsage, keyIdentifier, CorrelationId
This query is looking for instances where a new access credential is added to a sensitive application or service principal by a non-privileged user. It checks for specific operations related to adding service principals and managing certificates and secrets. It filters for successful operations initiated by users or apps with email addresses. It then extracts relevant information about the application or service principal, the key event, and the user agent. It joins this data with information about the application or service principal's classification and privileged access. Finally, it filters for instances where the initiating user or app's classification does not contain the application or service principal's classification. The resulting data includes information about the time, operation, initiating user or app, IP address, classification, user agent, application or service principal, key details, and correlation ID.

Thomas Naunheim
Released: August 23, 2023
Tables
Keywords
Operators