Collectors/ConsentGrants.ps1
|
@{ Name = 'ConsentGrants' FileName = 'consentGrants' ApiVersion = 'v1.0' RequiredScopes = @('Application.Read.All', 'Directory.Read.All') Description = 'Actually granted Graph application roles (including third-party apps) and delegated OAuth2 consent grants.' Collect = { # Well-known Microsoft Graph application role ids $roleNames = @{ '19dbc75e-c2e2-444c-a770-ec69d8559fc7' = 'Directory.ReadWrite.All' '7ab1d382-f21e-4acd-a863-ba3e13f7da61' = 'Directory.Read.All' '1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9' = 'Application.ReadWrite.All' '9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8' = 'RoleManagement.ReadWrite.Directory' '06b708a9-e830-4db3-a914-8e69da51d44f' = 'AppRoleAssignment.ReadWrite.All' '741f803b-c850-494e-b5df-cde7c675a1ca' = 'User.ReadWrite.All' 'df021288-bdef-4463-88db-98f22de89214' = 'User.Read.All' 'e2a3a72e-5f79-4c64-b1b1-878b674786c9' = 'Mail.ReadWrite' 'b633e1c5-b582-4048-a93e-9f11b44c7e96' = 'Mail.Send' '810c84a8-4a9e-49e6-bf7d-12d183f40d01' = 'Mail.Read' '75359482-378d-4052-8f01-80520e7db3cd' = 'Files.ReadWrite.All' '01d4889c-1287-42c6-ac1f-5d1e02578ef6' = 'Files.Read.All' '62a82d76-70ea-41e2-9197-370581804d09' = 'Group.ReadWrite.All' '5b567255-7703-4780-807c-7be8301ae99b' = 'Group.Read.All' '9492366f-7969-46a4-8d15-ed1a20078fff' = 'Sites.ReadWrite.All' '332a536c-c7ef-4017-ab91-336970924f0d' = 'Sites.Read.All' } $highPrivilegeIds = @( '19dbc75e-c2e2-444c-a770-ec69d8559fc7' # Directory.ReadWrite.All '1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9' # Application.ReadWrite.All '9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8' # RoleManagement.ReadWrite.Directory '06b708a9-e830-4db3-a914-8e69da51d44f' # AppRoleAssignment.ReadWrite.All '741f803b-c850-494e-b5df-cde7c675a1ca' # User.ReadWrite.All 'e2a3a72e-5f79-4c64-b1b1-878b674786c9' # Mail.ReadWrite 'b633e1c5-b582-4048-a93e-9f11b44c7e96' # Mail.Send '75359482-378d-4052-8f01-80520e7db3cd' # Files.ReadWrite.All '62a82d76-70ea-41e2-9197-370581804d09' # Group.ReadWrite.All '9492366f-7969-46a4-8d15-ed1a20078fff' # Sites.ReadWrite.All ) $graphFilter = [uri]::EscapeDataString("appId eq '00000003-0000-0000-c000-000000000000'") $graphServicePrincipal = @(Invoke-TLGraphRequest -Uri ('/v1.0/servicePrincipals?$filter=' + $graphFilter + '&$select=id,appId,displayName') -All) | Select-Object -First 1 $appRoleGrants = @() if ($graphServicePrincipal) { $appRoleGrants = @(Invoke-TLGraphRequest -Uri ('/v1.0/servicePrincipals/' + $graphServicePrincipal.id + '/appRoleAssignedTo?$top=999') -All) } $highPrivilegeCount = 0 foreach ($grant in $appRoleGrants) { $roleId = [string]$grant.appRoleId $permission = $(if ($roleNames.ContainsKey($roleId)) { $roleNames[$roleId] } else { $roleId }) $isHighPrivilege = [bool]($highPrivilegeIds -contains $roleId) if ($isHighPrivilege) { $highPrivilegeCount++ } Add-Member -InputObject $grant -NotePropertyName '_tlPermission' -NotePropertyValue $permission -Force Add-Member -InputObject $grant -NotePropertyName '_tlIsHighPrivilege' -NotePropertyValue $isHighPrivilege -Force } $delegatedGrants = @(Invoke-TLGraphRequest -Uri '/v1.0/oauth2PermissionGrants?$top=999' -All) # Resolve client service principals to names AND owner tenant, so # Microsoft first-party apps (normal platform behavior) can be told # apart from third-party consent-grant risk. $microsoftOwnerIds = @( 'f8cdef31-a31e-4b4a-93e4-5f571e91255a' # Microsoft first-party services '72f988bf-86f1-41af-91ab-2d7cd011db47' # Microsoft corporate tenant ) $clientIds = @($delegatedGrants | ForEach-Object { [string]$_.clientId } | Where-Object { $_ } | Sort-Object -Unique) $servicePrincipalMap = @{} for ($offset = 0; $offset -lt $clientIds.Count; $offset += 1000) { $chunk = @($clientIds[$offset..([Math]::Min($offset + 999, $clientIds.Count - 1))]) $found = @() try { $found = @(Invoke-TLGraphRequest -Method POST -Uri '/v1.0/directoryObjects/getByIds' -Body @{ ids = @($chunk); types = @('servicePrincipal') } -All) } catch { Write-Verbose ("Service principal lookup for delegated grants failed: {0}" -f $_.Exception.Message) } foreach ($servicePrincipal in $found) { $servicePrincipalMap[[string]$servicePrincipal.id] = $servicePrincipal } } foreach ($grant in $delegatedGrants) { $clientName = [string]$grant.clientId $ownerId = $null $servicePrincipal = $null if ($servicePrincipalMap.ContainsKey([string]$grant.clientId)) { $servicePrincipal = $servicePrincipalMap[[string]$grant.clientId] } if ($servicePrincipal) { if ($servicePrincipal.PSObject.Properties['displayName'] -and $servicePrincipal.displayName) { $clientName = [string]$servicePrincipal.displayName } if ($servicePrincipal.PSObject.Properties['appOwnerOrganizationId']) { $ownerId = [string]$servicePrincipal.appOwnerOrganizationId } } Add-Member -InputObject $grant -NotePropertyName '_tlClientName' -NotePropertyValue $clientName -Force Add-Member -InputObject $grant -NotePropertyName '_tlAppOwnerOrganizationId' -NotePropertyValue $ownerId -Force Add-Member -InputObject $grant -NotePropertyName '_tlIsMicrosoftApp' -NotePropertyValue ([bool]($ownerId -and ($microsoftOwnerIds -contains $ownerId))) -Force } @{ graphServicePrincipalId = $(if ($graphServicePrincipal) { [string]$graphServicePrincipal.id } else { $null }) summary = [ordered]@{ appRoleGrantCount = @($appRoleGrants).Count highPrivilegeGrantCount = $highPrivilegeCount delegatedGrantCount = @($delegatedGrants).Count } appRoleGrants = $appRoleGrants delegatedGrants = $delegatedGrants } } } |