public/maester/entra/Test-MtHighRiskAppPermissions.ps1
|
<# .SYNOPSIS Check if any applications or service principals have high risk Graph permissions that can lead to direct or indirect paths to Global Admin and full tenant takeover. The permissions are based on the research published at https://github.com/emiliensocchi/azure-tiering/tree/main. .DESCRIPTION Applications that use Graph API permissions with a risk of having a direct or indirect path to Global Admin and full tenant takeover. .EXAMPLE Test-MtHighRiskAppPermissions Returns true if no application has Tier-0 graph permissions .LINK https://maester.dev/docs/commands/Test-MtHighRiskAppPermissions #> function Test-MtHighRiskAppPermissions { [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'This test checks multiple permissions.')] [OutputType([bool])] param( # Check for direct path to Global Admin or indirect path through a combination of permissions. Default is "All". [ValidateSet('All', 'Direct', 'Indirect')] [String] $AttackPath = 'All' ) if (-not (Test-MtConnection Graph)) { Add-MtTestResultDetail -SkippedBecause NotConnectedGraph return $null } $allCriticalGraphPermissions = @( [pscustomobject]@{ Id = '2f6817f8-7b12-4f0f-bc18-eeaf60705a9e' Name = 'PrivilegedAccess.ReadWrite.AzureADGroup' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = '32531c59-1f32-461f-b8df-6f8a3b89f73b' Name = 'PrivilegedAccess.ReadWrite.AzureADGroup' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = '41202f2c-f7ab-45be-b001-85c9728b9d69' Name = 'PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = '06dbc45d-6708-4ef0-a797-f797ee68bf4b' Name = 'PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = 'dd199f4a-f148-40a4-a2ec-f0069cc799ec' Name = 'RoleAssignmentSchedule.ReadWrite.Directory' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = '8c026be3-8e26-4774-9372-8d5d6f21daff' Name = 'RoleAssignmentSchedule.ReadWrite.Directory' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = '9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8' Name = 'RoleManagement.ReadWrite.Directory' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = 'd01b97e9-cbc0-49fe-810a-750afd5527a3' Name = 'RoleManagement.ReadWrite.Directory' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = 'eccc023d-eccf-4e7b-9683-8813ab36cecc' Name = 'User.DeleteRestore.All' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = '4bb440cd-2cf2-4f90-8004-aa2acd2537c5' Name = 'User.DeleteRestore.All' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = '3011c876-62b7-4ada-afa2-506cbbecc68c' Name = 'User.EnableDisableAccount.All' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = 'f92e74e7-2563-467f-9dd0-902688cb5863' Name = 'User.EnableDisableAccount.All' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = '50483e42-d915-4231-9639-7fdb7fd190e5' Name = 'UserAuthenticationMethod.ReadWrite.All' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = 'b7887744-6746-4312-813d-72daeaee7e2d' Name = 'UserAuthenticationMethod.ReadWrite.All' Type = 'Delegated' Path = 'Direct' } [pscustomobject]@{ Id = '5eb59dd3-1da2-4329-8733-9dabdc435916' Name = 'AdministrativeUnit.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '7b8a2d34-6b3f-4542-a343-54651608ad81' Name = 'AdministrativeUnit.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9' Name = 'Application.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = 'bdfbf15f-ee85-4955-8675-146e8e5296b5' Name = 'Application.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '18a4783c-866b-4cc7-a460-3d5e5662c884' Name = 'Application.ReadWrite.OwnedBy' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '06b708a9-e830-4db3-a914-8e69da51d44f' Name = 'AppRoleAssignment.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '84bccea3-f856-4a8a-967b-dbe0a3d53a64' Name = 'AppRoleAssignment.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '9241abd9-d0e6-425a-bd4f-47ba86e767a4' Name = 'DeviceManagementConfiguration.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0883f392-0a7a-443d-8c76-16a6d39c7b63' Name = 'DeviceManagementConfiguration.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'e330c4f0-4170-414e-a55a-2f022ec2b57b' Name = 'DeviceManagementRBAC.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0c5e8a55-87a6-4556-93ab-adc52c4d862d' Name = 'DeviceManagementRBAC.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '19dbc75e-c2e2-444c-a770-ec69d8559fc7' Name = 'Directory.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = 'c5366453-9fb0-48a5-a156-24f0c49a4b84' Name = 'Directory.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '9acd699f-1e81-4958-b001-93b1d2506e19' Name = 'EntitlementManagement.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = 'ae7a573d-81d7-432b-ad44-4ed5c9d89038' Name = 'EntitlementManagement.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '62a82d76-70ea-41e2-9197-370581804d09' Name = 'Group.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '4e46008b-f24c-477d-8fff-7bb4ec7aafe0' Name = 'Group.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'dbaae8cf-10b5-4b86-a4a1-f871c94c6695' Name = 'GroupMember.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = 'f81125ac-d3b7-4573-a3b2-7099cc39df9e' Name = 'GroupMember.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '29c18626-4985-4dcd-85c0-193eef327366' Name = 'Policy.ReadWrite.AuthenticationMethod' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '7e823077-d88e-468f-a337-e18f1f0e6c7c' Name = 'Policy.ReadWrite.AuthenticationMethod' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'a402ca1c-2696-4531-972d-6e5ee4aa11ea' Name = 'Policy.ReadWrite.PermissionGrant' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '2672f8bb-fd5e-42e0-85e1-ec764dd2614e' Name = 'Policy.ReadWrite.PermissionGrant' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '618b6020-bca8-4de6-99f6-ef445fa4d857' Name = 'PrivilegedEligibilitySchedule.ReadWrite.AzureADGroup' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = 'ba974594-d163-484e-ba39-c330d5897667' Name = 'PrivilegedEligibilitySchedule.ReadWrite.AzureADGroup' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'fee28b28-e1f3-4841-818e-2704dc62245f' Name = 'RoleEligibilitySchedule.ReadWrite.Directory' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '62ade113-f8e0-4bf9-a6ba-5acb31db32fd' Name = 'RoleEligibilitySchedule.ReadWrite.Directory' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'b38dcc4d-a239-4ed6-aa84-6c65b284f97c' Name = 'RoleManagementPolicy.ReadWrite.AzureADGroup' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0da165c7-3f15-4236-b733-c0b0f6abe41d' Name = 'RoleManagementPolicy.ReadWrite.AzureADGroup' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '31e08e0a-d3f7-4ca2-ac39-7343fb83e8ad' Name = 'RoleManagementPolicy.ReadWrite.Directory' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '1ff1be21-34eb-448c-9ac9-ce1f506b2a68' Name = 'RoleManagementPolicy.ReadWrite.Directory' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '741f803b-c850-494e-b5df-cde7c675a1ca' Name = 'User.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '204e0828-b5ca-4ad8-b9f3-f32a958e7cc4' Name = 'User.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'cc117bb9-00cf-4eb8-b580-ea2a878fe8f7' Name = 'User-PasswordProfile.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '56760768-b641-451f-8906-e1b8ab31bca7' Name = 'User-PasswordProfile.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '9241abd9-d0e6-425a-bd4f-47ba86e767a4' Name = 'DeviceManagementConfiguration.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0883f392-0a7a-443d-8c76-16a6d39c7b63' Name = 'DeviceManagementConfiguration.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = 'e330c4f0-4170-414e-a55a-2f022ec2b57b' Name = 'DeviceManagementRBAC.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0c5e8a55-87a6-4556-93ab-adc52c4d862d' Name = 'DeviceManagementRBAC.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '7e05723c-0bb0-42da-be95-ae9f08a6e53c' Name = 'Domain.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '0b5d694c-a244-4bde-86e6-eb5cd07730fe' Name = 'Domain.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '292d869f-3427-49a8-9dab-8c70152b74e9' Name = 'Organization.ReadWrite.All' Type = 'Application' Path = 'Indirect' } [pscustomobject]@{ Id = '46ca0847-7e6b-426e-9775-ea810a948356' Name = 'Organization.ReadWrite.All' Type = 'Delegated' Path = 'Indirect' } [pscustomobject]@{ Id = '01c0a623-fc9b-48e9-b794-0756f8e8f067' Name = 'Policy.ReadWrite.ConditionalAccess' Type = 'Application' Path = 'Direct' } [pscustomobject]@{ Id = 'ad902697-1014-4ef5-81ef-2b4301988e8c' Name = 'Policy.ReadWrite.ConditionalAccess' Type = 'Delegated' Path = 'Direct' } ) $return = $true Write-Verbose 'Test-MtHighRiskAppPermissions: Checking applications for high-risk permissions' try { $allApiAssignments = [System.Collections.Generic.List[PSCustomObject]]::new() $allServicePrincipals = Invoke-MtGraphRequest -RelativeUri 'servicePrincipals' foreach ($sp in $allServicePrincipals) { if (([string]::IsNullOrEmpty($sp.Id))) { continue } $spUrl = "https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ManagedAppMenuBlade/~/Overview/objectId/$($sp.id)/appId/$($sp.appId)" $spAppRoleAssignments = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($sp.Id)/appRoleAssignments" -Method GET $spAppRoleAssignments.value | ForEach-Object { $allApiAssignments.Add([PSCustomObject]@{ appDisplayName = $sp.appDisplayName objectId = $sp.Id appId = $sp.appId appUrl = $spUrl permissionId = $_.appRoleId permissionName = $null type = 'Application' }) } $spOauth2PermissionGrants = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($sp.Id)/oauth2PermissionGrants" -Method GET $spOauth2PermissionGrants.value | ForEach-Object { $_.scope.Split(' ') | ForEach-Object { $allApiAssignments.Add([PSCustomObject]@{ appDisplayName = $sp.appDisplayName objectId = $sp.Id appId = $sp.appId appUrl = $spUrl permissionId = $null permissionName = $_.Trim() type = 'Delegated' }) } } } if ($attackPath -ne 'All') { $allCriticalGraphPermissionsToCheck = $allCriticalGraphPermissions | Where-Object { $_.Path -eq $attackPath } $attackPathStr = $attackPath.ToLower() } else { $attackPathStr = 'direct or indirect' $allCriticalGraphPermissionsToCheck = $allCriticalGraphPermissions } $allAssignedCriticalPermissions = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($apiAssignment in $allApiAssignments) { foreach ($criticalGraphPermission in $allCriticalGraphPermissionsToCheck) { $compareAssignment = if ($apiAssignment.type -eq 'Application') { $apiAssignment.permissionId } else { $apiAssignment.permissionName } $compareGraphPermission = if ($apiAssignment.type -eq 'Application') { $criticalGraphPermission.Id } else { $criticalGraphPermission.Name } if (($compareAssignment -eq $compareGraphPermission) -and ($apiAssignment.type -eq $criticalGraphPermission.Type)) { $allAssignedCriticalPermissions.Add([PSCustomObject]@{ ApplicationName = $apiAssignment.appDisplayName ApplicationId = $apiAssignment.appId ApplicationUrl = $apiAssignment.appUrl PermissionName = $criticalGraphPermission.Name PermissionType = $criticalGraphPermission.Type AttackPath = $criticalGraphPermission.Path }) } } } $return = if (($allAssignedCriticalPermissions | Measure-Object).Count -eq 0) { $true } else { $false } if ($return) { $testResultMarkdown = "Well done. No application has graph permissions with a risk of having a $($attackPathStr) path to Global Admin and full tenant takeover." } else { $testResultMarkdown = "At least one application has graph permissions with a risk of having a $($attackPathStr) path to Global Admin and full tenant takeover.`n`n%TestResult%" $result = "| ApplicationName | ApplicationId | PermissionName | PermissionType | AttackPath |`n" $result += "| --- | --- | --- | --- | --- |`n" foreach ($assignedCriticalPermission in $allAssignedCriticalPermissions) { $appMdLink = "[$($assignedCriticalPermission.ApplicationName)]($($assignedCriticalPermission.ApplicationUrl))" $result += "| $($appMdLink) | $($assignedCriticalPermission.ApplicationId) | $($assignedCriticalPermission.PermissionName) | $($assignedCriticalPermission.PermissionType) | $($assignedCriticalPermission.AttackPath) |`n" } $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $result } Add-MtTestResultDetail -Result $testResultMarkdown return $return } catch { Add-MtTestResultDetail -SkippedBecause Error -SkippedError $_ return $null } } |