Query Details

Added Owner To Application For Unprivileged

Query

  // Owner added to high privileged application to unprivileged or lower privileged user
  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(14d)
  | where OperationName in ("Add owner to application","Add owner to service principal")
  | 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))
  | mv-expand TargetResources
  | mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "Application.ObjectID" | extend ApplicationObjectID = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
  | mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "Application.DisplayName" | extend ApplicationDisplayName = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
  | mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "Application.AppId" | extend ApplicationAppId = replace_string(tostring(TargetResources_modifiedProperties.newValue),'"','')
  | extend AddedOwnerId = tostring(TargetResources.id)
  | 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.ApplicationObjectID == $right.APPObjectId  
  | join kind=inner (
    PrivilegedEAM_CL
        | where TimeGenerated > ago(14d)
        | summarize arg_max(TimeGenerated, *) by ObjectId
        | extend AddedOwnerClassification = iff(isnotempty(parse_json(Classification)), parse_json(Classification), "UserAccess")
        | project ObjectId, ObjectAdminTierLevel, ObjectAdminTierLevelName, AddedOwnerClassification, AddedOwnerUserPrincipalName = ObjectUserPrincipalName, AddedOwnerDisplayName = ObjectDisplayName
    ) on $left.AddedOwnerId == $right.ObjectId
| where parse_json(tostring(parse_json(AddedOwnerClassification))) !contains AppClassification
| project-reorder OperationName, ApplicationObjectID, ApplicationDisplayName, ApplicationAppId, AppClassification, InitiatingUserOrApp, InitiatingUserOrAppId, InitiatingIpAddress, AddedOwnerId, AddedOwnerClassification, AddedOwnerUserPrincipalName, AddedOwnerDisplayName

Explanation

This query is looking for instances where an owner is added to a high privileged application and assigned to an unprivileged or lower privileged user. It retrieves data from external sources to classify the permissions and roles involved. It then joins this data with audit logs and privileged access logs to identify the application, user, and their respective classifications. The query filters out instances where the application's classification is not contained in the added owner's classification. The final result includes information about the operation, application, user or app initiating the operation, IP address, added owner, and their respective classifications.

Details

Thomas Naunheim profile picture

Thomas Naunheim

Released: September 16, 2023

Tables

AuditLogsAADServicePrincipals_CLSensitiveMsGraphPermissionsSensitiveAadDirectoryRolesPrivilegedEAM_CL

Keywords

Devices,Intune,User

Operators

externaldatawithformatwhereagoinextendiffisnotemptytostringmv-expandreplace_stringjoinkindprojectiifdistinctsummarizearg_maxbycontainsparse_jsoncontainsproject-reorder

Actions