├── Azure-AccessPermissions.ps1 ├── Readme.md └── img ├── first-run-error.png ├── sample_output1.png └── sample_output2.png /Azure-AccessPermissions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script to enumerate access permissions of a user's Azure Active Directory home tenant. 4 | 5 | .NOTES 6 | Author: 0xcsandker 7 | Creation Date: 19.10.2022 8 | 9 | .EXAMPLE 10 | PS:> . .\Azure-AccessPermissions.ps1 11 | PS:> Invoke-AccessCheckForCurrentUser 12 | 13 | .EXAMPLE 14 | PS:> . .\Azure-AccessPermissions.ps1 15 | PS:> Invoke-AccessCheckForAllServicePrincipals 16 | 17 | .EXAMPLE 18 | PS:> . .\Azure-AccessPermissions.ps1 19 | PS:> Invoke-AccessCheckForServicePrincipal -ServicePrincipalIdentifier 94fc9712-01e1-4115-ad4e-56a428e438b0 20 | #> 21 | 22 | #----------------------------------------------------------[Declarations]---------------------------------------------------------- 23 | 24 | # Script Version 25 | $sScriptVersion = "0.2.2" 26 | 27 | $banner=@" 28 | _ _ ____ _ _ 29 | / \ _____ _ _ __ ___ / \ ___ ___ ___ ___ ___| _ \ ___ _ __ _ __ ___ (_)___ ___(_) ___ _ __ ___ 30 | / _ \ |_ / | | | '__/ _ \_____ / _ \ / __/ __/ _ \/ __/ __| |_) / _ \ '__| '_ ` _ \| / __/ __| |/ _ \| '_ \/ __| 31 | / ___ \ / /| |_| | | | __/_____/ ___ \ (_| (_| __/\__ \__ \ __/ __/ | | | | | | | \__ \__ \ | (_) | | | \__ \ 32 | /_/ \_\/___|\__,_|_| \___| /_/ \_\___\___\___||___/___/_| \___|_| |_| |_| |_|_|___/___/_|\___/|_| |_|___/ 33 | 34 | v$sScriptVersion by @0xcsandker 35 | 36 | Here are the functions you might wanna use: 37 | Invoke-AccessCheckForServicePrincipal ## Specific Service Principals 38 | Invoke-AccessCheckForAllServicePrincipals ## All Service Principals 39 | Invoke-AccessCheckForGroup ## Specific Group 40 | Invoke-AccessCheckForAllGroups ## All Groups 41 | Invoke-AccessCheckForUser ## Specifc User 42 | Invoke-AccessCheckForAllUsers ## All Users 43 | Invoke-AccessCheckForCurrentUser ## Your current User 44 | Invoke-AllAccessChecks ## All of the above 45 | 46 | Enumerate-AllHighPrivilegePrincipals ## Find all high privileged principals 47 | Enumerate-MFAStatusOfHighPrivilegePrincipals ## Check the MFA Status of all high privileged principals 48 | "@ 49 | 50 | $wellKnownApplicationIDs = @{ 51 | ## NOTE: THIS is not a complete list, but rather an add-on-the-go-list 52 | ### I don't want this script to be too bloated 53 | "00000002-0000-0ff1-ce00-000000000000" = "Exchange Online"; 54 | "00000003-0000-0ff1-ce00-000000000000" = "SharePoint Online"; 55 | "00000004-0000-0ff1-ce00-000000000000" = "Skype for Business online"; 56 | "0000000a-0000-0000-c000-000000000000" = "DeviceManagementApp (Microsoft Intune)"; 57 | "1b730954-1685-4b74-9bfd-dac224a7b894" = "MS Graph API"; 58 | "a0c73c16-a7e3-4564-9a95-2bdf47383716" = "MS Exchange Remote PowerShell"; 59 | "1fec8e78-bce4-4aaf-ab1b-5451cc387264" = "MS Teams"; 60 | "d3590ed6-52b3-4102-aeff-aad2292ab01c" = "Microsoft Support and Recovery Assistant (SARA)"; 61 | "ab9b8c07-8f02-4f72-87fa-80105867a763" = "OneDrive Sync Engine"; 62 | "de0853a1-ab20-47bd-990b-71ad5077ac7b" = "Windows Configuration Designer (WCD)"; 63 | "d4ebce55-015a-49b5-a083-c84d1797ae8c" = "Microsoft Intune Enrollment" 64 | } 65 | 66 | #-----------------------------------------------------------[Functions]------------------------------------------------------------ 67 | 68 | $MESSAGE_SUCCESS = '0' 69 | $MESSAGE_FAIL = '1' 70 | $MESSAGE_WARNING = '2' 71 | $MESSAGE_INFO = '3' 72 | Function __AAP-Log { 73 | PARAM( 74 | [String] 75 | $Msg = '', 76 | 77 | [String] 78 | $MsgType = '', 79 | 80 | [Int] 81 | $IndentationLevel = 0, 82 | 83 | [Switch] 84 | $NoNewLine = $false 85 | 86 | ) 87 | Process { 88 | $initalFC = $host.UI.RawUI.ForegroundColor 89 | switch ( $MsgType ) 90 | { 91 | $MESSAGE_SUCCESS { 92 | $host.UI.RawUI.ForegroundColor = "Green" 93 | Write-Host "$(' '*$IndentationLevel)$($Msg)" -NoNewline:$NoNewLine 94 | $host.UI.RawUI.ForegroundColor = $initalFC 95 | break 96 | } 97 | $MESSAGE_FAIL { 98 | $host.UI.RawUI.ForegroundColor = "Red" 99 | Write-Host "$(' '*$IndentationLevel)$($Msg)" -NoNewline:$NoNewLine 100 | $host.UI.RawUI.ForegroundColor = $initalFC 101 | break 102 | } 103 | $MESSAGE_WARNING { 104 | $host.UI.RawUI.ForegroundColor = "Yellow" 105 | Write-Host "$(' '*$IndentationLevel)$($Msg)" -NoNewline:$NoNewLine 106 | $host.UI.RawUI.ForegroundColor = $initalFC 107 | break 108 | } 109 | $MESSAGE_INFO { 110 | $host.UI.RawUI.ForegroundColor = "Cyan" 111 | Write-Host "$(' '*$IndentationLevel)$($Msg)" -NoNewline:$NoNewLine 112 | $host.UI.RawUI.ForegroundColor = $initalFC 113 | break 114 | } 115 | default { 116 | $host.UI.RawUI.ForegroundColor = "DarkGray" 117 | Write-Host "$(' '*$IndentationLevel)$($Msg)" -NoNewline:$NoNewLine 118 | $host.UI.RawUI.ForegroundColor = $initalFC 119 | break 120 | } 121 | } 122 | if( $Outfile ){ 123 | $script:gOutFileMessageBuffer += $Msg 124 | If(-Not $NoNewLine) { 125 | "$(' '*$IndentationLevel)$($script:gOutFileMessageBuffer)" | Out-File -Append -FilePath $Outfile 126 | $script:gOutFileMessageBuffer = "" 127 | } 128 | } 129 | } 130 | } 131 | 132 | Function __APP-DateTimeToString { 133 | PARAM( 134 | [Parameter()] 135 | [DateTime] 136 | $DateTime 137 | ) 138 | Process { 139 | If( $DateTime.getType() -eq [DateTime] ){ 140 | return $DateTime.GetDateTimeFormats('D')[-1] 141 | }Else { 142 | return $DateTime 143 | } 144 | } 145 | } 146 | 147 | Function __AAP-AppRoleIsHighPrivilegeConfidenceGuess { 148 | PARAM( 149 | [Object] 150 | $AppRoleObject 151 | ) 152 | Process { 153 | ## 154 | ## confidence level 155 | ## 0 => Assumed Not high privilege 156 | ## >0 => Assumed high privilege 157 | ## 100 => Certainly high privilege 158 | $confidenceLevel = 0 159 | If( $AppRoleObject.Value ){ 160 | If( $AppRoleObject.Value -eq 'Directory.ReadWrite.All' ){ 161 | $confidenceLevel = 100 162 | } 163 | ElseIf( $AppRoleObject.Value -Like '*FullControl.All' ){ 164 | $confidenceLevel = 10 165 | } 166 | ElseIf( $AppRoleObject.Value -Like '*ReadWrite.All' ){ 167 | $confidenceLevel = 10 168 | } 169 | ElseIf( $AppRoleObject.Value -Like 'full_access*' ){ 170 | $confidenceLevel = 10 171 | } 172 | } 173 | ## Return condifence level 174 | return $confidenceLevel 175 | } 176 | } 177 | 178 | Function __AAP-DisplayAppRoleAssignments { 179 | PARAM( 180 | [Object[]] 181 | $AppRoleAssignments, 182 | 183 | [Int] 184 | $IndentationLevel = 0 185 | ) 186 | Process { 187 | ForEach($appRoleAssignment in $AppRoleAssignments){ 188 | $appRoleValue = 'default' 189 | $appRole = $null 190 | $highPrivConfidenceLevel = $null 191 | If( $appRoleAssignment.AppRoleId -ne '00000000-0000-0000-0000-000000000000' ){ 192 | $appRole = ((Get-MgServicePrincipal -ServicePrincipalId $appRoleAssignment.ResourceId).AppRoles | ? {$_.Id -eq $appRoleAssignment.AppRoleId} | Select-Object -First 1) 193 | $appRoleValue = $appRole.Value 194 | If( $appRole ){ 195 | $highPrivConfidenceLevel =__AAP-AppRoleIsHighPrivilegeConfidenceGuess -AppRoleObject $appRole 196 | } 197 | } 198 | __AAP-Log " Resource: $($appRoleAssignment.ResourceDisplayName) ($($appRoleAssignment.ResourceId))" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 199 | __AAP-Log " AppRole ID: $($appRoleAssignment.AppRoleId)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 200 | If( $highPrivConfidenceLevel ){ 201 | __AAP-Log " AppRole ([!] Might be high privileged. Confidence $($highPrivConfidenceLevel)/100): " -MsgType $MESSAGE_WARNING -IndentationLevel $RecursionCounterDoNotUse 202 | } Else { 203 | __AAP-Log " AppRole:" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 204 | } 205 | __AAP-Log " Value: $($appRoleValue)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 206 | __AAP-Log " Display Name: $($appRole.DisplayName)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 207 | __AAP-Log " AllowedMemberTypes: $($appRole.AllowedMemberTypes)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 208 | __AAP-Log " Enabled: $($appRole.IsEnabled)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 209 | __AAP-Log "" 210 | #__AAP-Log " $($appRoleAssignmend.PrincipalType): $($appRoleAssignmend.PrincipalDisplayName) ($($appRoleAssignmend.PrincipalId))" 211 | } 212 | } 213 | } 214 | 215 | Function __AAP-DisplayOauth2PermissionGrants { 216 | PARAM( 217 | [Object[]]$Oauth2PermissionGrants 218 | ) 219 | Process { 220 | ForEach($Oauth2PermissionGrant in $Oauth2PermissionGrants){ 221 | $principal = If($Oauth2PermissionGrant.PrincipalId) {(Get-MgDirectoryObjectById -Ids $Oauth2PermissionGrant.PrincipalId).AdditionalProperties.userPrincipalName} Else { "" } 222 | $resource = If($Oauth2PermissionGrant.ResourceId) {(Get-MgDirectoryObjectById -Ids $Oauth2PermissionGrant.ResourceId).AdditionalProperties.displayName} Else {""} 223 | $client = If($Oauth2PermissionGrant.ClientId) {(Get-MgDirectoryObjectById -Ids $Oauth2PermissionGrant.ClientId).AdditionalProperties.displayName} Else {""} 224 | 225 | __AAP-Log " Resource: $($resource) ($($Oauth2PermissionGrant.ResourceId))" -MsgType $MESSAGE_INFO 226 | __AAP-Log " Consent To: $($Oauth2PermissionGrant.ConsentType)" -MsgType $MESSAGE_INFO 227 | __AAP-Log " Principal: $($principal) ($($Oauth2PermissionGrant.PrincipalId))" -MsgType $MESSAGE_INFO 228 | __AAP-Log " Scope: $($Oauth2PermissionGrant.Scope)" -MsgType $MESSAGE_INFO 229 | __AAP-Log " Client: $($client) ($($Oauth2PermissionGrant.ClientId))" -MsgType $MESSAGE_INFO 230 | __AAP-Log " Additional Attributes:" -MsgType $MESSAGE_INFO 231 | $Oauth2PermissionGrant.AdditionalProperties.Keys.ForEach{" $($_): $($Oauth2PermissionGrant.AdditionalProperties[$_])"} 232 | __AAP-Log "" 233 | } 234 | } 235 | } 236 | 237 | Function __AAP-GetHighPrivilegedDirectoryRoleTemplateMap { 238 | PARAM() 239 | Process { 240 | return @{ 241 | '62E90394-69F5-4237-9190-012177145E10' = 'Global administrator'; 242 | '9B895D92-2CD3-44C7-9D02-A6AC2D5EA5C3' = 'Application administrator'; 243 | 'C4E39BD9-1100-46D3-8C65-FB160DA0071F' = 'Authentication Administrator'; 244 | 'B0F54661-2D74-4C50-AFA3-1EC803F12EFE' = 'Billing administrator'; 245 | '158C047A-C907-4556-B7EF-446551A6B5F7' = 'Cloud application administrator'; 246 | 'B1BE1C3E-B65D-4F19-8427-F6FA0D97FEB9' = 'Conditional Access administrator'; 247 | '29232CDF-9323-42FD-ADE2-1D097AF3E4DE' = 'Exchange administrator'; 248 | '729827E3-9C14-49F7-BB1B-9608F156BBB8' = 'Helpdesk administrator'; 249 | '966707D0-3269-4727-9BE2-8C3A10F19B9D' = 'Password administrator'; 250 | '7BE44C8A-ADAF-4E2A-84D6-AB2649E08A13' = 'Privileged authentication administrator'; 251 | 'E8611AB8-C189-46E8-94E1-60213AB1F814' = 'Privileged Role Administrator'; 252 | '194AE4CB-B126-40B2-BD5B-6091B380977D' = 'Security administrator'; 253 | 'F28A1F50-F6E7-4571-818B-6A12F2AF6B6C' = 'SharePoint administrator'; 254 | 'FE930BE7-5E62-47DB-91AF-98C3A49A38B1' = 'User administrator'; 255 | '3A2C62DB-5318-420D-8D74-23AFFEE5D9D5' = 'Intune Administrator'; 256 | '9F06204D-73C1-4D4C-880A-6EDB90606FD8' = 'Azure AD Joined Device Local Administrator'; 257 | 258 | ## 'F2EF992C-3AFB-46B9-B7CF-A126EE74C451' = 'Global Reader'; 259 | } 260 | } 261 | } 262 | 263 | Function __AAP-DisplayDirectoryRoleAssignment { 264 | PARAM( 265 | [Object]$MgDirectoryRole 266 | ) 267 | Begin { 268 | #$mgAllDirectoryRoles = Get-MgDirectoryRole -All 269 | $highPrivilegedDirectoryRoles = __AAP-GetHighPrivilegedDirectoryRoleTemplateMap 270 | } 271 | Process { 272 | ## Azure AD Builtin Roles are described here: https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference 273 | If( $MgDirectoryRole.RoleTemplateId.toUpper() -In $highPrivilegedDirectoryRoles.Keys ){ 274 | __AAP-Log " DirectoryRole: $($MgDirectoryRole.DisplayName) ([!] High privileged)" -MsgType $MESSAGE_WARNING 275 | __AAP-Log " $($MgDirectoryRole.Description)" 276 | } 277 | Else { 278 | __AAP-Log " DirectoryRole: $($MgDirectoryRole.DisplayName) (RoleTemplate ID: $($MgDirectoryRole.RoleTemplateId))" -MsgType $MESSAGE_INFO 279 | __AAP-Log " $($MgDirectoryRole.Description)" 280 | } 281 | } 282 | } 283 | 284 | Function __AAP-DisplayHighPrivilegePrincipalMap { 285 | PARAM() 286 | Process { 287 | __AAP-Log "## High Privileged Principals " 288 | __AAP-Log "[*] Number of high privileged Accounts: $($script:gHighPrivilegdPrincipalMap.Keys.Count)" -MsgType $MESSAGE_WARNING 289 | ForEach($principalID in $script:gHighPrivilegdPrincipalMap.Keys){ 290 | $principalEntries = $script:gHighPrivilegdPrincipalMap[$principalID] 291 | $firstEntry = $principalEntries[0] 292 | $absoluteConfidenceEntries = $principalEntries | ? { $_['ConfidenceLevel'] -eq 100 } 293 | 294 | __AAP-Log "[+] $($firstEntry['principalName']) ($($firstEntry['principalID'])) [Type: $($firstEntry['principalType'])]" -MsgType $MESSAGE_SUCCESS 295 | ## If there is an entry with 100 confidence display only these entries 296 | If( $absoluteConfidenceEntries ){ 297 | ForEach($absoluteConfidenceEntry in $absoluteConfidenceEntries){ 298 | __AAP-Log " Reason: $($absoluteConfidenceEntry['Reason']) (Confidence: $($absoluteConfidenceEntry['ConfidenceLevel'])/100)" -MsgType $MESSAGE_INFO 299 | } 300 | } 301 | ## Otherwise display all entries 302 | Else { 303 | ForEach($principalEntry in $principalEntries){ 304 | __AAP-Log " Reason: $($principalEntry['Reason']) (Confidence: $($principalEntry['ConfidenceLevel'])/100)" -MsgType $MESSAGE_INFO 305 | } 306 | } 307 | } 308 | } 309 | } 310 | 311 | Function __AAP-ResolveDirectoryObjectByID { 312 | PARAM( 313 | [String] 314 | $ObjectID 315 | ) 316 | Process { 317 | $returnValue = "$($ObjectID)" 318 | $directoryObject = Get-MgDirectoryObjectById -Ids $ObjectID -ErrorAction SilentlyContinue 319 | If($directoryObject){ 320 | $aadDirectoryObjType = $directoryObject.AdditionalProperties['@odata.type'] 321 | Switch($aadDirectoryObjType){ 322 | '#microsoft.graph.user' { 323 | $returnValue = "$($directoryObject.AdditionalProperties['userPrincipalName']) (User) [ID: $($ObjectID)]" 324 | Break 325 | } 326 | '#microsoft.graph.group' { 327 | $returnValue = "$($directoryObject.AdditionalProperties['displayName']) (Group) [ID: $($ObjectID)]" 328 | Break 329 | } 330 | '#microsoft.graph.servicePrincipal' { 331 | $returnValue = "$($directoryObject.AdditionalProperties['appDisplayName']) (ServicePrincipal) [ID: $($ObjectID)]" 332 | Break 333 | } 334 | } 335 | } 336 | Else { 337 | ## Check if well known Application 338 | If( $wellKnownApplicationIDs.Keys -Contains $ObjectID ){ 339 | $returnValue = "$($wellKnownApplicationIDs[$ObjectID]) (Application) [ID: $($ObjectID)]" 340 | } 341 | } 342 | return $returnValue 343 | } 344 | } 345 | 346 | Function __AAP-DisplayApplicableMFAConditionalAccessPolicyForUserID { 347 | PARAM( 348 | [Parameter()] 349 | [String] 350 | $UserID, 351 | 352 | [Int] 353 | $IndentationLevel = 0 354 | ) 355 | Begin { 356 | If( -Not $script:gActiveMFAConditionalAccessPolicies ){ 357 | $script:gActiveMFAConditionalAccessPolicies = Get-MgIdentityConditionalAccessPolicy -All | ?{ $_.State -ne "disabled" -And $_.GrantControls.BuiltInControls -Contains "mfa" } 358 | } 359 | } 360 | Process { 361 | $usersGroups = Get-MgUserMemberOf -UserId $UserID -All 362 | $applicablePoliciesCount = 0 363 | ForEach($conditionalAccessPolicy in $script:gActiveMFAConditionalAccessPolicies){ 364 | $policyApplies = $false 365 | ## Check Excludes 366 | ### Excluded by Group 367 | If($conditionalAccessPolicy.Conditions.users.ExcludeGroups | ?{ $usersGroups.Id -Contains "$_" } ){ 368 | ## Write-Verbose "[*] Group Policy exlcuded by group membership: $($conditionalAccessPolicy.DisplayName)" 369 | Continue 370 | } 371 | ### Excluded by User 372 | If($conditionalAccessPolicy.Conditions.users.ExcludeUsers -Contains $UserID ) { 373 | ## Write-Verbose "[*] Group Policy exlcuded by group user: $($conditionalAccessPolicy.DisplayName)" 374 | Continue 375 | } 376 | ### Excluded by Role 377 | If($conditionalAccessPolicy.Conditions.users.ExcludeRoles) { 378 | $excludedRoles = $conditionalAccessPolicy.Conditions.Users.ExcludeRoles 379 | ForEach($excludedRole in $excludedRoles){ 380 | If( ( Get-MgRoleManagementDirectoryRoleAssignment -Filter "(RoleDefinitionId eq '$($excludedRole)') and (PrincipalId eq '$($UserID)')") ){ 381 | #Write-Verbose "[*] Group Policy exlcuded by group role: $($conditionalAccessPolicy.DisplayName)" 382 | Continue 383 | } 384 | } 385 | } 386 | 387 | ## Check Includes 388 | ### Inclue by Group 389 | If($conditionalAccessPolicy.Conditions.users.IncludeGroups | ?{ $usersGroups.Id -Contains "$_" } ){ 390 | #Write-Verbose "[+] Group Policy applies by Group: $($conditionalAccessPolicy.DisplayName)" -ForegroundColor DarkGreen 391 | $policyApplies = $true 392 | } 393 | ### Excluse by User 394 | If( 395 | ( $conditionalAccessPolicy.Conditions.users.IncludeUsers -Contains "All") -Or 396 | ( $conditionalAccessPolicy.Conditions.users.IncludeUsers -Contains $UserID ) 397 | ){ 398 | #Write-Verbose "[+] Group Policy applies by User: $($conditionalAccessPolicy.DisplayName)" -ForegroundColor DarkGreen 399 | $policyApplies = $true 400 | } 401 | ### Excluse by Role 402 | If($conditionalAccessPolicy.Conditions.users.IncludeRoles) { 403 | $includeRoles = $conditionalAccessPolicy.Conditions.Users.IncludeRoles 404 | ForEach($includeRole in $includeRoles){ 405 | If( ( Get-MgRoleManagementDirectoryRoleAssignment -Filter "(RoleDefinitionId eq '$($includeRole)') and (PrincipalId eq '$($UserID)')") ){ 406 | #Write-Verbose "[+] Group Policy applies by group role: $($conditionalAccessPolicy.DisplayName)" -ForegroundColor DarkGreen 407 | $policyApplies = $true 408 | } 409 | } 410 | } 411 | 412 | If($policyApplies){ 413 | $applicablePoliciesCount += 1 414 | __AAP-Log "[+] $($conditionalAccessPolicy.DisplayName)" -MsgType $MESSAGE_SUCCESS -IndentationLevel $IndentationLevel 415 | ## Grant Controls 416 | If( $conditionalAccessPolicy.GrantControls.BuiltInControls -eq "block" ){ 417 | ## It should not be possible to set controls to "block" AND "mfa" 418 | ### Therefore this is just a saftey net 419 | __AAP-Log "==> Block access" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 420 | } ElseIf ($conditionalAccessPolicy.GrantControls.BuiltInControls.Count) { 421 | __AAP-Log "==> Grant access " -NoNewline -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 422 | If( $conditionalAccessPolicy.GrantControls.BuiltInControls.Count ){ 423 | __AAP-Log "IF [ " -NoNewline -MsgType $MESSAGE_INFO 424 | ForEach($bultInControl in $conditionalAccessPolicy.GrantControls.BuiltInControls){ 425 | If( ($conditionalAccessPolicy.GrantControls.BuiltInControls.IndexOf($bultInControl) % 2) -ne 0 ){ 426 | __AAP-Log "$($conditionalAccessPolicy.GrantControls.Operator) " -NoNewline -MsgType $MESSAGE_INFO 427 | } 428 | __AAP-Log "$($bultInControl) " -NoNewline -MsgType $MESSAGE_INFO 429 | } 430 | __AAP-Log "]" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 431 | } Else { 432 | __AAP-Log "" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel ## newline 433 | } 434 | } 435 | ## Session Controls 436 | If( $conditionalAccessPolicy.SessionControls.ApplicationEnforcedRestrictions.IsEnabled ){ 437 | __AAP-Log "--> Session Control: Use app enforced restrictions" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 438 | } 439 | If( $conditionalAccessPolicy.SessionControls.CloudAppSecurity.IsEnabled ){ 440 | __AAP-Log "--> Session Control: CloudAppSecurity (Type: $($conditionalAccessPolicy.SessionControls.CloudAppSecurity.CloudAppSecurityType))" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 441 | } 442 | If( $conditionalAccessPolicy.SessionControls.ContinuousAccessEvaluation.Mode ){ 443 | __AAP-Log "--> Session Control: Customize continuous access evaluation (Mode: $($conditionalAccessPolicy.SessionControls.ContinuousAccessEvaluation.Mode))" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 444 | } 445 | If( $conditionalAccessPolicy.SessionControls.DisableResilienceDefaults ){ 446 | __AAP-Log "--> Session Control: Disable resilience defaults" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 447 | } 448 | If( $conditionalAccessPolicy.SessionControls.PersistentBrowser.IsEnabled ){ 449 | __AAP-Log "--> Session Control: Persistent browser session" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 450 | } 451 | If( $conditionalAccessPolicy.SessionControls.SignInFrequency.IsEnabled ){ 452 | __AAP-Log "--> Session Control: Sign-in frequency" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 453 | __AAP-Log " $($conditionalAccessPolicy.SessionControls.SignInFrequency.Value) $($conditionalAccessPolicy.SessionControls.SignInFrequency.Type) ($($conditionalAccessPolicy.SessionControls.SignInFrequency.FrequencyInterval))" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 454 | } 455 | 456 | 457 | ## Applications 458 | ForEach($excludedApplication in $conditionalAccessPolicy.Conditions.Applications.ExcludeApplications){ 459 | ## Values could be "All", "", "" 460 | $excludedApp = __AAP-ResolveDirectoryObjectByID $excludedApplication 461 | __AAP-Log " Excluded Application: $($excludedApp)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 462 | } 463 | ForEach($includedApplication in $conditionalAccessPolicy.Conditions.Applications.IncludeApplications){ 464 | ## Values could be "All", "", "" 465 | $includeApp = __AAP-ResolveDirectoryObjectByID $includedApplication 466 | __AAP-Log " Included Application: $($includeApp)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 467 | } 468 | If( $conditionalAccessPolicy.Conditions.Applications.IncludeAuthenticationContextClassReferences ){ 469 | __AAP-Log "TODO IncludeAuthenticationContextClassReferences: $($conditionalAccessPolicy.Conditions.Applications.IncludeAuthenticationContextClassReferences )" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 470 | } 471 | If( $conditionalAccessPolicy.Conditions.Applications.IncludeUserActions ){ 472 | __AAP-Log "TODO IncludeUserActions: $($conditionalAccessPolicy.Conditions.Applications.IncludeUserActions )" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 473 | } 474 | If( $conditionalAccessPolicy.Conditions.Applications.AdditionalProperties.Count ){ 475 | __AAP-Log "TODO AdditionalProperties: $($conditionalAccessPolicy.Conditions.Applications.AdditionalProperties | fl )" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 476 | } 477 | ## ClientApps 478 | __AAP-Log " Client Apps: " -NoNewline -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 479 | ForEach($clientApp in $conditionalAccessPolicy.Conditions.ClientAppTypes){ 480 | If( ($conditionalAccessPolicy.Conditions.ClientAppTypes.IndexOf($clientApp) % 2) -ne 0 ){ 481 | __AAP-Log ", " -NoNewline -MsgType $MESSAGE_INFO 482 | } 483 | __AAP-Log "$($clientApp)" -NoNewline -MsgType $MESSAGE_INFO 484 | } 485 | __AAP-Log "" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel ## new line 486 | ## Devices 487 | If( $conditionalAccessPolicy.Conditions.Devices.ExcludeDeviceStates ){ 488 | __AAP-Log " Excluded Device States: $($conditionalAccessPolicy.Conditions.Devices.ExcludeDeviceState)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 489 | } 490 | If( $conditionalAccessPolicy.Conditions.Devices.ExcludeDevices ){ 491 | __AAP-Log " Excluded Devices: $($conditionalAccessPolicy.Conditions.Devices.ExcludeDevices)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 492 | } 493 | If( $conditionalAccessPolicy.Conditions.Devices.IncludeDeviceStates ){ 494 | __AAP-Log " Included Device States: $($conditionalAccessPolicy.Conditions.Devices.IncludeDeviceStates)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 495 | } 496 | If( $conditionalAccessPolicy.Conditions.Devices.IncludeDevices ){ 497 | __AAP-Log " Included Devices: $($conditionalAccessPolicy.Conditions.Devices.IncludeDevices)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 498 | } 499 | ## Locations 500 | If( $conditionalAccessPolicy.Conditions.Locations.ExcludeLocations ){ 501 | __AAP-Log " Excluded Locations: $($conditionalAccessPolicy.Conditions.Locations.ExcludeLocations)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 502 | } 503 | If( $conditionalAccessPolicy.Conditions.Locations.IncludeLocations ){ 504 | __AAP-Log " Included Locations: $($conditionalAccessPolicy.Conditions.Locations.IncludeLocations)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 505 | } 506 | ## Plattforms 507 | If( $conditionalAccessPolicy.Conditions.Platforms.ExcludePlatforms ){ 508 | __AAP-Log " Excluded Plattforms: $($conditionalAccessPolicy.Conditions.Platforms.ExcludeLocations)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 509 | } 510 | If( $conditionalAccessPolicy.Conditions.Platforms.IncludePlatforms ){ 511 | __AAP-Log " Included Plattforms: $($conditionalAccessPolicy.Conditions.Platforms.IncludePlatforms)" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 512 | } 513 | ## ServicePrincipalRiskLevels 514 | If( $conditionalAccessPolicy.Conditions.ServicePrincipalRiskLevels.Count ){ 515 | __AAP-Log " TODO: ServicePrincipalRiskLevels" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 516 | } 517 | ## SignInRiskLevels 518 | If( $conditionalAccessPolicy.Conditions.SignInRiskLevels.Count ){ 519 | __AAP-Log " TODO: SignInRiskLevels" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 520 | } 521 | ## UserRiskLevels 522 | If( $conditionalAccessPolicy.Conditions.UserRiskLevels.Count ){ 523 | __AAP-Log " TODO: UserRiskLevels" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 524 | } 525 | } 526 | 527 | #Write-Host "[+] Group Policy applies ??? last call : $($conditionalAccessPolicy.DisplayName)" -ForegroundColor DarkGreen 528 | } 529 | If( $applicablePoliciesCount -eq 0 ){ 530 | ## No policy applies 531 | __AAP-Log " -- No MFA Conditional Access Policy applies for this user --" -MsgType $MESSAGE_INFO -IndentationLevel $IndentationLevel 532 | } 533 | } 534 | } 535 | 536 | Function __AAP-CheckRequiredModules { 537 | PARAM() 538 | Process { 539 | $modulesInstalled = $true 540 | ## Microsoft.Graph 541 | if ( -Not (Get-Module -ListAvailable -Name Microsoft.Graph) ){ 542 | __AAP-Log -Msg "[-] Module 'Microsoft.Graph' does not exist." -MsgType $MESSAGE_FAIL 543 | __AAP-Log -Msg " Install it via: Install-Module Microsoft.Graph" -MsgType $MESSAGE_WARNING 544 | $modulesInstalled = $false 545 | } 546 | ## AADInternals 547 | if ( -Not (Get-Module -ListAvailable -Name AADInternals) ) { 548 | __AAP-Log -Msg "[-] Module 'AADInternals' does not exist." -MsgType $MESSAGE_FAIL 549 | __AAP-Log -Msg " Install it via: Install-Module AADInternals" -MsgType $MESSAGE_WARNING 550 | $modulesInstalled = $false 551 | } 552 | ## AzureADPreview 553 | if ( -Not (Get-Module -ListAvailable -Name AzureADPreview) ) { 554 | __AAP-Log -Msg "[-] Module 'AzureADPreview' does not exist." -MsgType $MESSAGE_FAIL 555 | __AAP-Log -Msg " Install it via: Install-Module AzureADPreview" -MsgType $MESSAGE_WARNING 556 | $modulesInstalled = $false 557 | } 558 | return $modulesInstalled 559 | } 560 | } 561 | 562 | Function __AAP-ImportRequiredModules { 563 | PARAM() 564 | Process { 565 | Try { 566 | Import-Module AADInternals 6>$null | Out-Null 567 | Import-Module Microsoft.Graph.Applications | Out-Null 568 | Import-Module AzureADPreview | Out-Null 569 | return $true 570 | } 571 | Catch { 572 | __AAP-Log -Msg "[-] An error occured while trying to import required modules." -MsgType $MESSAGE_FAIL 573 | __AAP-Log -Msg " The error was: $_" 574 | return $false 575 | } 576 | } 577 | } 578 | 579 | Function __AAP-GetAcessTokenForAADGraphWithRefreshToken { 580 | PARAM( 581 | [Parameter()] 582 | [String] 583 | $RefreshToken = $global:__AAPgRefreshToken, 584 | 585 | [Parameter()] 586 | [String] 587 | $Tenant = $global:__AAPgTenantID 588 | ) 589 | Process { 590 | return (Get-AADIntAccessTokenWithRefreshToken -Resource "https://graph.windows.net" -ClientId "1b730954-1685-4b74-9bfd-dac224a7b894" -RefreshToken $RefreshToken -Tenant $global:__AAPgTenantID) 591 | } 592 | } 593 | 594 | Function __AAP-GetAcessTokenForMSGraphWithRefreshToken { 595 | PARAM( 596 | [Parameter()] 597 | [String] 598 | $RefreshToken = $global:__AAPgRefreshToken, 599 | 600 | [Parameter()] 601 | [String] 602 | $Tenant = $global:__AAPgTenantID 603 | ) 604 | Process { 605 | return (Get-AADIntAccessTokenWithRefreshToken -Resource "https://graph.microsoft.com" -ClientId "1b730954-1685-4b74-9bfd-dac224a7b894" -RefreshToken $RefreshToken -Tenant $global:__AAPgTenantID) 606 | } 607 | } 608 | 609 | Function __AAP-GetAcessTokenForMSGraphWithCredentials { 610 | PARAM( 611 | [Parameter()] 612 | [String] 613 | $Tenant = $global:__AAPgTenantID 614 | ) 615 | Process { 616 | $accessTokenMSGraph, $refreshToken = Get-AADIntAccessToken -Resource "https://graph.microsoft.com" -ClientId "1b730954-1685-4b74-9bfd-dac224a7b894" -IncludeRefreshToken:$true -Tenant $Tenant 617 | return @($accessTokenMSGraph, $refreshToken) 618 | } 619 | } 620 | 621 | Function __AAP-ConnectMicrosoftGraph { 622 | PARAM( 623 | [Parameter()] 624 | [String] 625 | $Tenant 626 | ) 627 | Begin { 628 | ## Tenant dependent Script global variables 629 | $script:gAllMgDirectoryRoles = @() 630 | $script:gHighPrivilegdPrincipalMap = @{} 631 | $script:gLastCollectionOfHighPrivilegdPrincipalMap = $null 632 | } 633 | Process{ 634 | $global:__AAPgAccessTokenMSGraph, $global:__AAPgRefreshToken = __AAP-GetAcessTokenForMSGraphWithCredentials -Tenant $Tenant 635 | 636 | If( $global:__AAPgAccessTokenMSGraph ){ 637 | Connect-MgGraph -AccessToken $global:__AAPgAccessTokenMSGraph 638 | Select-MgProfile -Name "beta" 639 | $global:__AAPgTenantID = (Get-MgContext).TenantId 640 | } 641 | Else { 642 | __AAP-Log -Msg "[-] An error occured while trying to connect to Micrsoft Graph." -MsgType $MESSAGE_FAIL 643 | __AAP-Log -Msg " We can't continue. Please retry..." 644 | Break 645 | } 646 | return $accessTokenMSGraph 647 | } 648 | } 649 | 650 | Function __AAP-ConnectAllResources { 651 | PARAM( 652 | [Parameter()] 653 | [String] 654 | $Tenant 655 | ) 656 | Process { 657 | __AAP-Log "[*] Connecting to Microsoft Graph..." 658 | $accessTokenMSGraph = __AAP-ConnectMicrosoftGraph -Tenant $Tenant 659 | __AAP-Log "[*] Connecting to AzureAD Graph..." 660 | $graphContext = Get-MgContext 661 | Connect-AzureAD -AccountId $graphContext.Account | Out-Null 662 | } 663 | } 664 | 665 | Function __AAP-ConnectIfNecessary { 666 | PARAM( 667 | [Parameter()] 668 | [String] 669 | $Tenant 670 | ) 671 | Process { 672 | ## Check if given Tenant matches 673 | If( $Tenant -And ( $global:__AAPgTenantID -ne (Get-AADIntTenantID -Domain $Tenant) ) ){ 674 | __AAP-Log "[*] Connecting Microsoft Graph to different Tenant..." 675 | __AAP-ConnectAllResources -Tenant $Tenant 676 | } 677 | ## Check if connected 678 | If( -Not (Get-MgContext) ){ 679 | __AAP-ConnectAllResources -Tenant $Tenant 680 | } 681 | ## Test connection 682 | Try { 683 | $mgUser = Get-MgUser -Top 1 -ErrorAction Stop 684 | } Catch { 685 | $caughtError = $_ 686 | If( $caughtError.ToString() -Like "*Authentication needed*" ){ 687 | __AAP-Log -Msg "[*] We need to re-authenticate..." -MsgType $MESSAGE_WARNING 688 | __AAP-ConnectAllResources -Tenant $Tenant 689 | } 690 | If( $caughtError.ToString() -Like "*token has expired*" ){ 691 | __AAP-Log -Msg "[*] Access Token expired we need to re-authenticate..." -MsgType $MESSAGE_WARNING 692 | __AAP-ConnectAllResources -Tenant $Tenant 693 | } 694 | } 695 | } 696 | } 697 | 698 | Function __AAP-AddToHighPrivilegePrincipalMap { 699 | PARAM( 700 | [Parameter(Mandatory)] 701 | [String] 702 | $PrincipalID, 703 | 704 | [String] 705 | $PrincipalName, 706 | 707 | [String] 708 | $Reason, 709 | 710 | [Parameter(Mandatory)] 711 | [ValidateSet("User","Group","ServicePrincipal","Unknown", IgnoreCase = $true)] 712 | [String] 713 | $PrincipalType, 714 | 715 | [Int] 716 | $ConfidenceLevel = -1 717 | ) 718 | Process { 719 | $entryArray = If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $PrincipalID ){ ,$script:gHighPrivilegdPrincipalMap.Item($PrincipalID) } Else { ,@() } 720 | ## Update Entries with a reason already added 721 | $updateEntries = $entryArray | ? { $_['Reason'] -eq $Reason } 722 | If( $updateEntries ){ 723 | ForEach($updateEntry in $updateEntries){ 724 | ## Update only if the confidence level increased 725 | If( $ConfidenceLevel -gt $updateEntry['ConfidenceLevel'] ){ 726 | $updateEntry['ConfidenceLevel'] = $ConfidenceLevel 727 | } 728 | } 729 | } 730 | ## Add new entry if new reason 731 | Else { 732 | $entryArray += @{ 733 | 'principalID' = $PrincipalID; 734 | 'principalName' = $PrincipalName; 735 | 'principalType' = $PrincipalType; 736 | 'ConfidenceLevel' = $ConfidenceLevel; 737 | 'Reason' = $Reason 738 | } 739 | $script:gHighPrivilegdPrincipalMap[$PrincipalID] = $entryArray 740 | } 741 | } 742 | } 743 | 744 | Function __AAP-DisplayNonHighPrivilegedRoleAssignments { 745 | PARAM( 746 | [Parameter()] 747 | [hashtable] 748 | $NonHighPrivilegedRoleAssignments 749 | ) 750 | Process { 751 | ForEach($roleTemplateName in $NonHighPrivilegedRoleAssignments.Keys){ 752 | __AAP-Log "[*] The Directory Role '$($roleTemplateName)' is currently not considered high privileged, but has the following members:" -MsgType $MESSAGE_WARNING 753 | ForEach($principalDisplayStr in $NonHighPrivilegedRoleAssignments[$roleTemplateName]){ 754 | __AAP-Log " $($principalDisplayStr)" 755 | } 756 | } 757 | } 758 | } 759 | 760 | Function __AAP-CheckIfMemberOfPrivilegedDirectoryRole { 761 | PARAM( 762 | [Parameter()] 763 | [String] 764 | $PrincipalID, 765 | 766 | [Parameter()] 767 | [hashtable] 768 | $NonHighPrivilegedRoleAssignments, 769 | 770 | [Parameter()] 771 | [String] 772 | $TemplateID, 773 | 774 | [Parameter()] 775 | [String] 776 | $TemplateName = "", 777 | 778 | [Parameter()] 779 | [Switch] 780 | $AssignedViaPIM = $false, 781 | 782 | [Parameter()] 783 | [Object] 784 | $PIMAssignmentEndDateTime, 785 | 786 | [Parameter()] 787 | [String] 788 | $PIMAssignmentState 789 | ) 790 | Begin { 791 | If( $TemplateName -eq "" ){ 792 | $mgDirectoryRoleTemplate = Get-MgDirectoryRoleTemplate -DirectoryRoleTemplateId $TemplateID 793 | If( $mgDirectoryRoleTemplate ){ 794 | $TemplateName = $mgDirectoryRoleTemplate.DisplayName 795 | } 796 | } 797 | } 798 | Process { 799 | ### Check if role is high privileged 800 | If( $templateID -In $highPrivilegedDirectoryRoleTemplatesMap.Keys ){ 801 | ## 100 for Global Administrator, 99 for all others 802 | $confidenceLevel = If( $templateID -eq '62E90394-69F5-4237-9190-012177145E10' ){ 100 } Else { 99 } 803 | 804 | ## Get corresponding principal 805 | $principalObjectData = (Get-MgDirectoryObjectById -Ids $PrincipalID) 806 | $principalID = $principalObjectData.Id 807 | $principalName = $null 808 | $principalType = 'Unknown' 809 | $highPrivReason = "High privileged directory Role assigned: $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 810 | If( $AssignedViaPIM ){ 811 | If( $PIMAssignmentEndDateTime ){ 812 | $endDateStr = __APP-DateTimeToString -DateTime $PIMAssignmentEndDateTime 813 | $highPrivReason = "High privileged directory Role assigned (via time-based PIM assignment, ending $($endDateStr)): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 814 | If( $PIMAssignmentState ){ 815 | $highPrivReason = "High privileged directory Role assigned (via $($PIMAssignmentState) time-based PIM assignment, ending $($endDateStr)): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 816 | } 817 | } 818 | Else { 819 | $highPrivReason = "High privileged directory Role assigned (via permanent PIM assignment): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 820 | If( $PIMAssignmentState ){ 821 | $highPrivReason = "High privileged directory Role assigned (via $($PIMAssignmentState) permanent PIM assignment): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 822 | } 823 | } 824 | } 825 | If( $principalObjectData.AdditionalProperties ){ 826 | ## Resolve principal 827 | $aadDirectoryObjType = $principalObjectData.AdditionalProperties['@odata.type'] 828 | Switch($aadDirectoryObjType){ 829 | '#microsoft.graph.user' { 830 | $principalType = 'User' 831 | $principalName = $principalObjectData.AdditionalProperties['userPrincipalName'] 832 | ## Add entry 833 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $confidenceLevel 834 | Break 835 | } 836 | '#microsoft.graph.group' { 837 | $principalType = 'Group' 838 | ## Resovle members 839 | $securityIdentifier = $principalObjectData.AdditionalProperties['securityIdentifier'] 840 | $mgGroup = Get-MgGroup -Filter "securityIdentifier eq '$($securityIdentifier)'" -Top 1 841 | If( $mgGroup ){ 842 | $mgGroupMembers = Get-MgGroupTransitiveMember -GroupId $mgGroup.Id 843 | ForEach($mgGroupMember in $mgGroupMembers){ 844 | $principalID = $mgGroupMember.Id 845 | $principalName = $null 846 | $groupMemberObjType = $mgGroupMember.AdditionalProperties['@odata.type'] 847 | $highPrivReason = "Member of group ($($mgGroup.DisplayName)) with high privileged directory Role: $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 848 | If( $AssignedViaPIM ){ 849 | If( $PIMAssignmentEndDateTime ){ 850 | $endDateStr = __APP-DateTimeToString -DateTime $PIMAssignmentEndDateTime 851 | $highPrivReason = "Member of group ($($mgGroup.DisplayName)) with high privileged directory Role (via time-based PIM assignment, ending $($endDateStr)): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 852 | If( $PIMAssignmentState ){ 853 | $highPrivReason = "Member of group ($($mgGroup.DisplayName)) with high privileged directory Role (via $($PIMAssignmentState) time-based PIM assignment, ending $($endDateStr)): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 854 | } 855 | }Else { 856 | $highPrivReason = "Member of group ($($mgGroup.DisplayName)) with high privileged directory Role (via permanent PIM assignment): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 857 | If( $PIMAssignmentState ){ 858 | $highPrivReason = "Member of group ($($mgGroup.DisplayName)) with high privileged directory Role (via $($PIMAssignmentState) permanent PIM assignment): $($highPrivilegedDirectoryRoleTemplatesMap[$templateID])" 859 | } 860 | } 861 | } 862 | Switch($groupMemberObjType){ 863 | '#microsoft.graph.user' { 864 | $principalType = 'User' 865 | $principalName = $mgGroupMember.AdditionalProperties['userPrincipalName'] 866 | Break 867 | } 868 | '#microsoft.graph.servicePrincipal' { 869 | $principalType = 'ServicePrincipal' 870 | $principalName = $mgGroupMember.AdditionalProperties['appDisplayName'] 871 | Break 872 | } 873 | } 874 | ## Add entry 875 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $confidenceLevel 876 | } 877 | } 878 | Break 879 | } 880 | '#microsoft.graph.servicePrincipal' { 881 | $principalType = 'ServicePrincipal' 882 | $principalName = $principalObjectData.AdditionalProperties['appDisplayName'] 883 | ## Add entry 884 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $confidenceLevel 885 | Break 886 | } 887 | } 888 | } 889 | }Else { 890 | $principalEntries = If( $NonHighPrivilegedRoleAssignments.Keys -Contains $TemplateName ){ ,$NonHighPrivilegedRoleAssignments.Item($TemplateName) } Else { ,@() } 891 | ## Add Principal if not already contained 892 | If( $principalEntries.Keys -NotContains $PrincipalID ){ 893 | $principalDisplayStr = __AAP-ResolveDirectoryObjectByID -ObjectID $PrincipalID 894 | If( $AssignedViaPIM ){ 895 | If( $PIMAssignmentEndDateTime ){ 896 | $endDateStr = __APP-DateTimeToString -DateTime $PIMAssignmentEndDateTime 897 | If( $PIMAssignmentState ){ 898 | $principalDisplayStr += " [[ Assigned via $($PIMAssignmentState) PIM assignment (ends '$($endDateStr)') ]]" 899 | }Else { 900 | $principalDisplayStr += " [[ Assigned via PIM assignment (ends '$($endDateStr)') ]]" 901 | } 902 | } 903 | Else { 904 | If( $PIMAssignmentState ){ 905 | $principalDisplayStr += " [[ Assigned via $($PIMAssignmentState) PIM assignment (permanent) ]]" 906 | }Else { 907 | $principalDisplayStr += " [[ Assigned via PIM assignment (permanent) ]]" 908 | } 909 | } 910 | } 911 | $principalEntries += @($principalDisplayStr) 912 | } 913 | $NonHighPrivilegedRoleAssignments[$TemplateName] = $principalEntries 914 | } 915 | } 916 | } 917 | 918 | #-----------------------------------------------------------[Execution]------------------------------------------------------------ 919 | 920 | Function Invoke-AccessCheckForServicePrincipal { 921 | PARAM( 922 | [Parameter(Mandatory, ParameterSetName="ServicePrincipalIdentifier")] 923 | [Object] 924 | $ServicePrincipalIdentifier, 925 | 926 | [Parameter(Mandatory, ParameterSetName="MgServicePrincipalObject")] 927 | [Object] 928 | $MgServicePrincipalObject, 929 | 930 | [Parameter()] 931 | [String] 932 | $Outfile = $false, 933 | 934 | [Parameter()] 935 | [String] 936 | $Tenant 937 | ) 938 | Begin { 939 | __AAP-ConnectIfNecessary -Tenant $Tenant 940 | 941 | If($MgServicePrincipalObject){ 942 | $mgServicePrincipal = $MgServicePrincipalObject 943 | } 944 | ElseIf($ServicePrincipalIdentifier) { 945 | ## Try to find service principals via ID 946 | $mgServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId $ServicePrincipalIdentifier -ErrorAction SilentlyContinue 947 | If(-Not $mgServicePrincipal){ 948 | ## Try to find service principal via appID 949 | $mgServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$($ServicePrincipalIdentifier)'" -Top 1 -ErrorAction SilentlyContinue 950 | } 951 | If(-Not $mgServicePrincipal){ 952 | __AAP-Log "[-] Could not find service principal: $($ServicePrincipalIdentifier)" -MsgType $MESSAGE_FAIL 953 | } 954 | } 955 | Else { 956 | $mgServicePrincipal = $null 957 | } 958 | } 959 | Process { 960 | If( $mgServicePrincipal ){ 961 | $appRoleAssignmendToResult = Get-MgServicePrincipalAppRoleAssignedTo -All -ServicePrincipalId $mgServicePrincipal.Id 962 | $appRoleAssignmentsResult = Get-MgServicePrincipalAppRoleAssignment -All -ServicePrincipalId $mgServicePrincipal.Id 963 | $oauthPermissionsResult = Get-MgServicePrincipalOauth2PermissionGrant -All -ServicePrincipalId $mgServicePrincipal.Id 964 | $owner = Get-MgServicePrincipalOwner -All -ServicePrincipalId $mgServicePrincipal.Id 965 | $delegPermissionsClassifciation = Get-MgServicePrincipalDelegatedPermissionClassification -All -ServicePrincipalId $mgServicePrincipal.Id 966 | $mgOwnedObjectsByServicePrincipal = Get-MgServicePrincipalOwnedObject -All -ServicePrincipalId $mgServicePrincipal.Id 967 | $createdObjs = Get-MgServicePrincipalCreatedObject -All -ServicePrincipalId $mgServicePrincipal.Id 968 | $resourceSpecificAppPermissions = $mgServicePrincipal.ResourceSpecificApplicationPermissions 969 | #$oauthPermissionGrants = $mgServicePrincipal.Oauth2PermissionGrants 970 | #$oauthPermissionScopes = $mgServicePrincipal.Oauth2PermissionScopes 971 | 972 | 973 | __AAP-Log "### Service Principal: $($mgServicePrincipal.DisplayName) ($($mgServicePrincipal.Id))" 974 | 975 | ## Owned Objects 976 | if( $mgOwnedObjectsByServicePrincipal.Length -gt 0 ){ 977 | ForEach($mgOwnedObjectRef in $mgOwnedObjectsByServicePrincipal){ 978 | __AAP-Log "[+] User owns the following object: $($mgOwnedObjectRef.Id)" -MsgType $MESSAGE_SUCCESS 979 | $mgOwnedObjProperties = (Get-MgDirectoryObjectById -Ids $mgOwnedObjectRef.Id).AdditionalProperties 980 | if( $mgOwnedObjProperties ){ 981 | $mgOwnedObjProperties.Keys | %{ __AAP-Log " $($_): $($mgOwnedObjProperties[$_])" -MsgType $MESSAGE_INFO } 982 | } 983 | } 984 | } 985 | 986 | ## Application-Type API Permissions of the service principal 987 | if( $appRoleAssignmentsResult ){ 988 | __AAP-Log "[+] Application-Type API Permission access rights of this service principal:" -MsgType $MESSAGE_SUCCESS 989 | __AAP-DisplayAppRoleAssignments -AppRoleAssignments $appRoleAssignmentsResult 990 | } 991 | 992 | ## Delegated-Type API Permissions of the service principal 993 | if( $oauthPermissionsResult ){ 994 | __AAP-Log "[+] Delegated-Type API Permission access rights of this service principal:" -MsgType $MESSAGE_SUCCESS 995 | __AAP-DisplayOauth2PermissionGrants -Oauth2PermissionGrants $oauthPermissionsResult 996 | } 997 | 998 | ## Principals with assigned AppRoles to this service account 999 | if( $appRoleAssignmendToResult ){ 1000 | __AAP-Log "[+] The following principals have an AppRole assigned for this this service account:" -MsgType $MESSAGE_SUCCESS 1001 | ForEach($appRoleAssignmendTo in $appRoleAssignmendToResult){ 1002 | $appRoleValue = 'default' 1003 | ## 00000000-0000-0000-0000-000000000000 is the default appRoleID 1004 | If( $appRoleAssignmendTo.AppRoleId -ne '00000000-0000-0000-0000-000000000000' ){ 1005 | $appRole = $mgServicePrincipal.appRoles | ? {$_.Id -eq $appRoleAssignmendTo.AppRoleId} | Select-Object -First 1 1006 | $appRoleValue = $appRole.Value 1007 | } 1008 | __AAP-Log " $($appRoleAssignmendTo.PrincipalType): $($appRoleAssignmendTo.PrincipalDisplayName) ($($appRoleAssignmendTo.PrincipalId))" -MsgType $MESSAGE_INFO 1009 | __AAP-Log " Value: $($appRoleValue) ($($appRoleAssignmendTo.AppRoleId))" -MsgType $MESSAGE_INFO 1010 | } 1011 | } 1012 | 1013 | ## Owner of the service principal 1014 | If( $owner ){ 1015 | __AAP-Log "[+] The following user is the owner of this service principal: $($owner.AdditionalProperties.userPrincipalName)" -MsgType $MESSAGE_SUCCESS 1016 | __AAP-Log " Additional Properties:" 1017 | $owner.AdditionalProperties.Keys.ForEach{" $($_): $($owner.AdditionalProperties[$_])"} 1018 | } 1019 | 1020 | ## Other Attributes of interest 1021 | If( $delegPermissionsClassifciation ){ 1022 | __AAP-Log "[+] Delegated permission classifications" -MsgType $MESSAGE_SUCCESS 1023 | $delegPermissionsClassifciation | fl 1024 | } 1025 | 1026 | If( $createdObjs ){ 1027 | __AAP-Log "[+] Created Objects" -MsgType $MESSAGE_SUCCESS 1028 | __AAP-Log $createdObjs | fl 1029 | } 1030 | 1031 | If( $resourceSpecificAppPermissions ){ 1032 | __AAP-Log "[+] Resource specific Application Permissions of this service principal:" -MsgType $MESSAGE_SUCCESS 1033 | __AAP-Log " (Currently only supported for Teams, see: https://learn.microsoft.com/en-us/graph/api/resources/resourcespecificpermission?view=graph-rest-1.0)" 1034 | ForEach($resourceSpecificAppPermission in $resourceSpecificAppPermissions){ 1035 | __AAP-Log " ID: $($resourceSpecificAppPermission.Id)" -MsgType $MESSAGE_INFO 1036 | __AAP-Log " Enabled: $($resourceSpecificAppPermission.IsEnabled)" -MsgType $MESSAGE_INFO 1037 | __AAP-Log " Value: $($resourceSpecificAppPermission.VAlue)" -MsgType $MESSAGE_INFO 1038 | __AAP-Log " DisplayName: $($resourceSpecificAppPermission.displayName)`n" -MsgType $MESSAGE_INFO 1039 | } 1040 | } 1041 | } 1042 | } 1043 | } 1044 | 1045 | Function Invoke-AccessCheckForAllServicePrincipals { 1046 | PARAM( 1047 | [Parameter()] 1048 | [String] 1049 | $Outfile = $false, 1050 | 1051 | [Parameter()] 1052 | [String] 1053 | $Tenant 1054 | ) 1055 | Begin { 1056 | __AAP-ConnectIfNecessary -Tenant $Tenant 1057 | $mgServicePrincipals = Get-MgServicePrincipal -All 1058 | } 1059 | Process { 1060 | __AAP-Log "## Access Checks for all AAD Service Principals" 1061 | ## Init Progess 1062 | $progressCount = 0 1063 | $progressLimit = $mgServicePrincipals.Count 1064 | ForEach($mgServicePrincipal in $mgServicePrincipals){ 1065 | $progressCount += 1 1066 | Try { 1067 | Invoke-AccessCheckForServicePrincipal -MgServicePrincipalObject $mgServicePrincipal -Outfile:$Outfile 1068 | } 1069 | Catch { 1070 | $caughtError = $_ 1071 | If( $caughtError.ToString() -Like "*Authentication needed*" ){ 1072 | __AAP-Log -Msg "[*] We need to re-authenticate..." -MsgType $MESSAGE_WARNING 1073 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1074 | } 1075 | If( $caughtError.ToString() -Like "*token has expired*" ){ 1076 | __AAP-Log -Msg "[*] Access Token expired we need to re-authenticate..." -MsgType $MESSAGE_WARNING 1077 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1078 | } 1079 | } 1080 | 1081 | ## Update Progess 1082 | $progressOperation = "$progressCount/$progressLimit" 1083 | $progressPercentage = ($progressCount/$progressLimit)*100 1084 | Write-Progress -Activity "Enumerating Azure AD Service Principals" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1085 | } 1086 | ## Complete Progess 1087 | Write-Progress -Activity "Enumerating Azure AD Service Principals" -Status "Ready" -Completed 1088 | } 1089 | } 1090 | 1091 | Function Invoke-AccessCheckForGroup { 1092 | PARAM( 1093 | [Parameter(Mandatory, ParameterSetName="GroupIdentifier")] 1094 | [Object] 1095 | $GroupIdentifier, 1096 | 1097 | [Parameter(Mandatory, ParameterSetName="MgGroupObject")] 1098 | [Object] 1099 | $MgGroupObject, 1100 | 1101 | [Parameter()] 1102 | [String] 1103 | $Outfile = $false, 1104 | 1105 | [Parameter()] 1106 | [String] 1107 | $Tenant, 1108 | 1109 | [Parameter()] 1110 | [Switch] 1111 | $Recursive = $true, 1112 | 1113 | [Parameter()] 1114 | [Int] 1115 | $RecursiveDepthLevel = 5, 1116 | 1117 | [Parameter()] 1118 | [Int] 1119 | $RecursionCounterDoNotUse = 0 1120 | ) 1121 | Begin { 1122 | __AAP-ConnectIfNecessary -Tenant $Tenant 1123 | 1124 | If($MgGroupObject){ 1125 | $mgGroup = $MgGroupObject 1126 | } 1127 | ElseIf($GroupIdentifier) { 1128 | ## Try to find group by SecurityIdentifier 1129 | $mgGroup = Get-MgGroup -Filter "SecurityIdentifier eq '$($GroupIdentifier)'" -Top 1 1130 | If(-Not $mgGroup){ 1131 | ## Try to find group by ID 1132 | $mgGroup = Get-MgGroup -GroupId $GroupIdentifier -ErrorAction SilentlyContinue 1133 | If(-Not $mgGroup){ 1134 | ## Try to find group by DisplayName 1135 | $mgGroup = Get-MgGroup -Filter "DisplayName eq '$($GroupIdentifier)'" -ErrorAction SilentlyContinue -Top 1 1136 | } 1137 | } 1138 | If(-Not $mgGroup){ 1139 | __AAP-Log "[-] Could not find group: $($GroupIdentifier)" -MsgType $MESSAGE_FAIL 1140 | } 1141 | } 1142 | Else { 1143 | $mgGroup = $null 1144 | } 1145 | } 1146 | Process { 1147 | if( $mgGroup ){ 1148 | __AAP-Log "### Group: $($mgGroup.displayName) (ID: $($mgGroup.Id))" -IndentationLevel $RecursionCounterDoNotUse 1149 | $groupAppRoleAssignments = Get-MgGroupAppRoleAssignment -All -GroupId $mgGroup.Id 1150 | $groupPermissionGrants = Get-MgGroupPermissionGrant -All -GroupId $mgGroup.Id -ErrorAction SilentlyContinue 1151 | $transitiveMemberships = Get-MgGroupTransitiveMemberOf -GroupId $mgGroup.Id 1152 | $mgGroupIsMemberOfObjects = Get-MgGroupMemberObject -GroupId $mgGroup.Id -SecurityEnabledOnly:$false ## Return all IDs for the groups, administrative units, and directory roles that a group is a member of. 1153 | 1154 | ## App Role Assignments 1155 | If($groupAppRoleAssignments){ 1156 | __AAP-Log "[+] Application Permission Access of this group (AppRoles of this group):" -MsgType $MESSAGE_SUCCESS -IndentationLevel $RecursionCounterDoNotUse 1157 | __AAP-DisplayAppRoleAssignments -AppRoleAssignments $groupAppRoleAssignments -IndentationLevel $RecursionCounterDoNotUse 1158 | } 1159 | ## Permission Grants 1160 | If( $groupPermissionGrants ){ 1161 | __AAP-Log Write-Output "[+] Permissions grants" -MsgType $MESSAGE_SUCCESS 1162 | $groupPermissionGrants | fl 1163 | } 1164 | ## Transitive Memberships 1165 | if( $transitiveMemberships ){ 1166 | __AAP-Log "[+] Memberships" -MsgType $MESSAGE_SUCCESS -IndentationLevel $RecursionCounterDoNotUse 1167 | ForEach($transitiveMembership in $transitiveMemberships){ 1168 | $transitiveMembershipProperties = $transitiveMembership.AdditionalProperties 1169 | If( $transitiveMembershipProperties ){ 1170 | If( $transitiveMembershipProperties['@odata.type'] -eq "#microsoft.graph.group" ){ 1171 | __AAP-Log " Member of Group: $($transitiveMembershipProperties.displayName)" -MsgType $MESSAGE_INFO -IndentationLevel $RecursionCounterDoNotUse 1172 | $recorsionCounterStep = 3 1173 | $recursionLevel = $RecursionCounterDoNotUse/$recorsionCounterStep 1174 | If( $Recursive -And ($recursionLevel -lt $RecursiveDepthLevel) ){ 1175 | ## Recursively traverse group memmberships 1176 | $memberOfGroup = Get-MgGroup -Filter "SecurityIdentifier eq '$( $transitiveMembershipProperties.securityIdentifier )'" -Top 1 -ErrorAction SilentlyContinue 1177 | If( $memberOfGroup ){ 1178 | $recursionCounter = $RecursionCounterDoNotUse + $recorsionCounterStep 1179 | Invoke-AccessCheckForGroup -MgGroupObject $memberOfGroup -RecursionCounterDoNotUse $recursionCounter -Recursive:$Recursive -RecursiveDepthLevel:$RecursiveDepthLevel 1180 | }Else { 1181 | __AAP-Log "Could not find group with DisplayName: $($transitiveMembershipProperties.displayName)" -IndentationLevel $RecursionCounterDoNotUse 1182 | } 1183 | } 1184 | } 1185 | Else { 1186 | __AAP-Log "[*] This group is a member of the following object: $($transitiveMembershipProperties['@odata.type'])" 1187 | $transitiveMembershipProperties.Keys | %{ __AAP-Log " $($_): $($transitiveMembershipProperties[$_])" } 1188 | } 1189 | } 1190 | } 1191 | } 1192 | ## Objects where the group is a member of 1193 | if( $mgGroupIsMemberOfObjects.Length -gt 0 ){ 1194 | ForEach($mgGroupIsMemberOfObjectId in $mgGroupIsMemberOfObjects){ 1195 | $mgGroupIsMemberOfObjectProperties = (Get-MgDirectoryObjectById -Ids $mgGroupIsMemberOfObjectId).AdditionalProperties 1196 | if( $mgGroupIsMemberOfObjectProperties ){ 1197 | ## Only add this information if this is data type is not already covered 1198 | if( $mgGroupIsMemberOfObjectProperties['@odata.type'] -Notin @("#microsoft.graph.group") ){ 1199 | __AAP-Log "[*] This group is member of the following object: $($mgGroupIsMemberOfObjectProperties['@odata.type'])" -IndentationLevel $RecursionCounterDoNotUse 1200 | $mgGroupIsMemberOfObjectProperties.Keys | %{ Write-Output " $($_): $($mgGroupIsMemberOfObjectProperties[$_])" } 1201 | } 1202 | } 1203 | } 1204 | } 1205 | } 1206 | } 1207 | } 1208 | 1209 | Function Invoke-AccessCheckForAllGroups { 1210 | PARAM( 1211 | [Parameter()] 1212 | [String] 1213 | $Tenant, 1214 | 1215 | [Parameter()] 1216 | [String] 1217 | $Outfile = $false 1218 | ) 1219 | Begin { 1220 | __AAP-ConnectIfNecessary -Tenant $Tenant 1221 | $mgGroups = Get-MgGroup -All 1222 | } 1223 | Process { 1224 | __AAP-Log "## Access Checks for all AAD Groups" 1225 | ## Init Progess 1226 | $progressCount = 0 1227 | $progressLimit = $mgGroups.Count 1228 | ForEach($mgGroup in $mgGroups){ 1229 | $progressCount += 1 1230 | Try { 1231 | Invoke-AccessCheckForGroup -MgGroupObject $mgGroup -Outfile:$Outfile 1232 | } 1233 | Catch { 1234 | $caughtError = $_ 1235 | If( $caughtError.ToString() -Like "*Authentication needed*" ){ 1236 | __AAP-Log -Msg "[*] We need to re-authenticate..." -MsgType $MESSAGE_WARNING 1237 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1238 | } 1239 | If( $caughtError.ToString() -Like "*token has expired*" ){ 1240 | __AAP-Log -Msg "[*] Access Token expired we need to re-authenticate..." -MsgType $MESSAGE_WARNING 1241 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1242 | } 1243 | } 1244 | 1245 | ## Update Progess 1246 | $progressOperation = "$progressCount/$progressLimit" 1247 | $progressPercentage = ($progressCount/$progressLimit)*100 1248 | Write-Progress -Activity "Enumerating Azure AD Groups" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1249 | } 1250 | ## Complete Progess 1251 | Write-Progress -Activity "Enumerating Azure AD Groups" -Status "Ready" -Completed 1252 | } 1253 | } 1254 | 1255 | Function Invoke-AccessCheckForUser { 1256 | PARAM( 1257 | [Parameter(Mandatory, ParameterSetName="UserIdentifier")] 1258 | [Object] 1259 | $UserIdentifier, 1260 | 1261 | [Parameter(Mandatory, ParameterSetName="MgUserObject")] 1262 | [Object] 1263 | $MgUserObject, 1264 | 1265 | [Parameter()] 1266 | [String] 1267 | $Tenant, 1268 | 1269 | [Parameter()] 1270 | [String] 1271 | $Outfile = $false, 1272 | 1273 | [Parameter()] 1274 | [Switch] 1275 | $Recursive = $true 1276 | ) 1277 | Begin { 1278 | __AAP-ConnectIfNecessary -Tenant $Tenant 1279 | 1280 | If($MgUserObject){ 1281 | $mgUser = $MgUserObject 1282 | } 1283 | ElseIf($UserIdentifier) { 1284 | $mgUser = Get-MgUser -UserId $UserIdentifier -ErrorAction SilentlyContinue 1285 | If(-Not $mgUser){ 1286 | __AAP-Log "[-] Could not find user: $($UserIdentifier)" -MsgType $MESSAGE_FAIL 1287 | } 1288 | } 1289 | Else { 1290 | $mgUser = $null 1291 | } 1292 | } 1293 | Process { 1294 | if( $mgUser ){ 1295 | __AAP-Log "### User: $($mgUser.UserPrincipalName) (ID: $($mgUser.Id))" -MsgType $MESSAGE_INFO 1296 | $userAppRoleAssignments = Get-MgUserAppRoleAssignment -All -UserId $mgUser.Id 1297 | $userOauthGrants = Get-MgUserOauth2PermissionGrant -All -UserId $mgUser.Id 1298 | $mgUserMemberships = Get-MgUserMemberOf -UserId $mgUser.Id -All 1299 | $mgOwnedObjectsByUser = Get-MgUserOwnedObject -UserId $mgUser.Id -All 1300 | $mgUserIsMemberOfObjects = Get-MgUserMemberObject -UserId $mgUser.Id -SecurityEnabledOnly:$false ## Return all IDs for the groups, administrative units, and directory roles that a user is a member of 1301 | 1302 | ## Owned Objects 1303 | if( $mgOwnedObjectsByUser.Length -gt 0 ){ 1304 | ForEach($mgOwnedObjectRef in $mgOwnedObjectsByUser){ 1305 | __AAP-Log "[+] User owns the following object: $($mgOwnedObjectRef.Id)" -MsgType $MESSAGE_SUCCESS 1306 | $mgOwnedObjProperties = (Get-MgDirectoryObjectById -Ids $mgOwnedObjectRef.Id).AdditionalProperties 1307 | if( $mgOwnedObjProperties ){ 1308 | $mgOwnedObjProperties.Keys | %{ __AAP-Log " $($_): $($mgOwnedObjProperties[$_])" -MsgType $MESSAGE_INFO } 1309 | } 1310 | } 1311 | } 1312 | ## App Role Assignments 1313 | If( $userAppRoleAssignments){ 1314 | __AAP-Log "[+] Application Permission Access of this user (AppRoles of this user):" -MsgType $MESSAGE_SUCCESS 1315 | __AAP-DisplayAppRoleAssignments -AppRoleAssignments $userAppRoleAssignments 1316 | } 1317 | ## OAuth Grants 1318 | if( $userOauthGrants ){ 1319 | __AAP-Log "[+] Delegated Permission Access of this user (Oauth2PermissionGrants):" -MsgType $MESSAGE_SUCCESS 1320 | __AAP-DisplayOauth2PermissionGrants -Oauth2PermissionGrants $userOauthGrants 1321 | } 1322 | ## Memberships 1323 | if( $mgUserMemberships.Length -gt 0 ){ 1324 | __AAP-Log "[+] Memberships" -MsgType $MESSAGE_SUCCESS 1325 | If( $script:gAllMgDirectoryRoles.Length -eq 0 ){ $script:gAllMgDirectoryRoles = Get-MgDirectoryRole -All } 1326 | ForEach($mgUserMembership in $mgUserMemberships){ 1327 | $mgUserMembershipProperties = $mgUserMembership.AdditionalProperties 1328 | $mgDirectoryRole = $script:gAllMgDirectoryRoles | ? {$_.Id -eq $mgUserMembership.Id } 1329 | 1330 | If( $mgDirectoryRole ){ 1331 | __AAP-DisplayDirectoryRoleAssignment -MgDirectoryRole $mgDirectoryRole 1332 | } 1333 | Else { 1334 | If( $mgUserMembershipProperties ){ 1335 | __AAP-Log " $($mgUserMembershipProperties.displayName) ($($mgUserMembership.Id))" -MsgType $MESSAGE_INFO 1336 | If( $Recursive ){ 1337 | If( $mgUserMembershipProperties['@odata.type'] -eq '#microsoft.graph.group' ){ 1338 | $groupSecurityIdentifier = $mgUserMembershipProperties.securityIdentifier 1339 | Invoke-AccessCheckForGroup -GroupIdentifier $groupSecurityIdentifier -RecursionCounterDoNotUse 3 -Recursive 1340 | } 1341 | } 1342 | } 1343 | Else { 1344 | __AAP-Log " $($mgUserMembership.Id)" -MsgType $MESSAGE_INFO 1345 | } 1346 | } 1347 | } 1348 | } 1349 | ## Objects where the user is a member of 1350 | If( $mgUserIsMemberOfObjects.Length -gt 0 ){ 1351 | ForEach($mgUserIsMemberOfObjectId in $mgUserIsMemberOfObjects){ 1352 | $mgUserIsMemberOfObjectProperties = (Get-MgDirectoryObjectById -Ids $mgUserIsMemberOfObjectId).AdditionalProperties 1353 | if( $mgUserIsMemberOfObjectProperties ){ 1354 | ## Only add this information if this is data type is not already covered 1355 | if( $mgUserIsMemberOfObjectProperties['@odata.type'] -Notin @("#microsoft.graph.group", "#microsoft.graph.directoryRole") ){ 1356 | __AAP-Log "[*] User is member of the following object: $($mgUserIsMemberOfObjectProperties['@odata.type'])" 1357 | $mgUserIsMemberOfObjectProperties.Keys | %{ Write-Output " $($_): $($mgUserIsMemberOfObjectProperties[$_])" } 1358 | } 1359 | } 1360 | } 1361 | } 1362 | } 1363 | } 1364 | } 1365 | 1366 | Function Invoke-AccessCheckForCurrentUser { 1367 | PARAM( 1368 | [Parameter()] 1369 | [String] 1370 | $Tenant, 1371 | 1372 | [Parameter()] 1373 | [String] 1374 | $Outfile = $false 1375 | ) 1376 | Begin { 1377 | __AAP-ConnectIfNecessary -Tenant $Tenant 1378 | } 1379 | Process { 1380 | $currentUserString = (Get-MgContext).Account 1381 | __AAP-Log "## Permission Check for current AAD User: $currentUserString" 1382 | Invoke-AccessCheckForUser -UserIdentifier $currentUserString -Outfile:$Outfile 1383 | } 1384 | } 1385 | 1386 | Function Invoke-AccessCheckForAllUsers { 1387 | PARAM( 1388 | [Parameter()] 1389 | [String] 1390 | $Tenant, 1391 | 1392 | [Parameter()] 1393 | [String] 1394 | $Outfile = $false 1395 | ) 1396 | Begin { 1397 | __AAP-ConnectIfNecessary -Tenant $Tenant 1398 | $mgUsers = Get-MgUser -All 1399 | } 1400 | Process { 1401 | __AAP-Log "## Access Checks for all AAD Users" 1402 | ## Init Progess 1403 | $progressCount = 0 1404 | $progressLimit = $mgUsers.Count 1405 | ForEach($mgUser in $mgUsers){ 1406 | $progressCount += 1 1407 | ## Test connection 1408 | Try { 1409 | Invoke-AccessCheckForUser -MgUserObject $mgUser -Outfile:$Outfile 1410 | } Catch { 1411 | $caughtError = $_ 1412 | If( $caughtError.ToString() -Like "*Authentication needed*" ){ 1413 | __AAP-Log -Msg "[*] We need to re-authenticate..." -MsgType $MESSAGE_WARNING 1414 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1415 | } 1416 | If( $caughtError.ToString() -Like "*token has expired*" ){ 1417 | __AAP-Log -Msg "[*] Access Token expired we need to re-authenticate..." -MsgType $MESSAGE_WARNING 1418 | $global:__AAPgAccessTokenMSGraph = __AAP-GetAcessTokenForMSGraphWithRefreshToken 1419 | } 1420 | } 1421 | 1422 | ## Update Progess 1423 | $progressOperation = "$progressCount/$progressLimit" 1424 | $progressPercentage = ($progressCount/$progressLimit)*100 1425 | Write-Progress -Activity "Enumerating Azure AD Users" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1426 | } 1427 | ## Complete Progess 1428 | Write-Progress -Activity "Enumerating Azure AD Users" -Status "Ready" -Completed 1429 | } 1430 | } 1431 | 1432 | Function Invoke-AllAccessChecks { 1433 | PARAM( 1434 | [Parameter()] 1435 | [String] 1436 | $Tenant, 1437 | 1438 | [Parameter()] 1439 | [String] 1440 | $Outfile = $false 1441 | ) 1442 | Begin { 1443 | __AAP-ConnectIfNecessary -Tenant $Tenant 1444 | } 1445 | Process { 1446 | __AAP-Log "# All Access Checks" 1447 | Invoke-AccessCheckForCurrentUser -Outfile:$Outfile -Tenant $Tenant 1448 | Invoke-AccessCheckForAllUsers -Outfile:$Outfile -Tenant $Tenant 1449 | Invoke-AccessCheckForAllGroups -Outfile:$Outfile -Tenant $Tenant 1450 | Invoke-AccessCheckForAllServicePrincipals -Outfile:$Outfile -Tenant $Tenant 1451 | } 1452 | } 1453 | 1454 | 1455 | Function Enumerate-AllHighPrivilegePrincipals { 1456 | PARAM( 1457 | [Parameter()] 1458 | [String] 1459 | $Tenant, 1460 | 1461 | [Parameter()] 1462 | [String] 1463 | $Outfile = $false 1464 | ) 1465 | Begin { 1466 | __AAP-ConnectIfNecessary -Tenant $Tenant 1467 | $highPrivilegedDirectoryRoleTemplatesMap = __AAP-GetHighPrivilegedDirectoryRoleTemplateMap 1468 | If($script:gAllMgDirectoryRoles.Length -eq 0){ $script:gAllMgDirectoryRoles = Get-MgDirectoryRole -All } 1469 | } 1470 | Process { 1471 | __AAP-Log "[*] Hang on, this might take a while..." 1472 | ## 1473 | ## Principals with high privileged default directory role 1474 | ## Enumerating all Directory Role Assignments 1475 | ## 1476 | $nonHighPrivAssignments = @{} 1477 | $directoryRoleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -All -ExpandProperty "RoleDefinition" 1478 | $progressCount = 0 1479 | $progressLimit = $directoryRoleAssignments.Count 1480 | ForEach($directoryRoleAssignment in $directoryRoleAssignments ){ 1481 | $progressCount += 1 1482 | $principalID = $directoryRoleAssignment.PrincipalId 1483 | $templateID = $directoryRoleAssignment.RoleDefinition.TemplateId 1484 | $templateName = $directoryRoleAssignment.RoleDefinition.DisplayName 1485 | 1486 | __AAP-CheckIfMemberOfPrivilegedDirectoryRole -PrincipalID $principalID -NonHighPrivilegedRoleAssignments $nonHighPrivAssignments -TemplateID $templateID -TemplateName $templateName 1487 | 1488 | $progressOperation = "$progressCount/$progressLimit" 1489 | $progressPercentage = ($progressCount/$progressLimit)*100 1490 | Write-Progress -Activity "Enumerating High Privileged Directory Role Assignments" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1491 | } 1492 | Write-Progress -Activity "Enumerating High Privileged Directory Role Assignments" -Status "Ready" -Completed 1493 | 1494 | ## 1495 | ## Principals with high privileged PIM assigned directory role 1496 | ## Enumerating all PIM assigned roles 1497 | ## 1498 | $azureADPrivRoleAssignments = Get-AzureADMSPrivilegedRoleAssignment -ProviderId "aadRoles" -ResourceId $global:__AAPgTenantID 1499 | $azureADPrivRoleDefinitions = Get-AzureADMSPrivilegedRoleDefinition -ProviderId "aadRoles" -ResourceId $global:__AAPgTenantID 1500 | 1501 | $progressCount = 0 1502 | $progressLimit = $azureADPrivRoleAssignments.Count 1503 | ForEach($azureADPrivRoleAssignment in $azureADPrivRoleAssignments){ 1504 | $progressCount += 1 1505 | If( 1506 | ( -Not $azureADPrivRoleAssignment.EndDateTime ) -Or ## no end date ==> Permanent 1507 | ( (Get-Date) -lt ($azureADPrivRoleAssignment.EndDateTime) ) ## End date in the future 1508 | ){ 1509 | ## Active assginment 1510 | $privRoleDefinition = $azureADPrivRoleDefinitions | ? { $_.Id -eq $azureADPrivRoleAssignment.RoleDefinitionId } | Select-Object -First 1 1511 | If( $privRoleDefinition ){ 1512 | $azureADPrivRoleAssignmentState = $azureADPrivRoleAssignment.AssignmentState 1513 | __AAP-CheckIfMemberOfPrivilegedDirectoryRole ` 1514 | -PrincipalID $azureADPrivRoleAssignment.SubjectId ` 1515 | -NonHighPrivilegedRoleAssignments $nonHighPrivAssignments ` 1516 | -TemplateID $privRoleDefinition.ExternalId ` 1517 | -TemplateName $privRoleDefinition.DisplayName ` 1518 | -AssignedViaPIM ` 1519 | -PIMAssignmentEndDateTime $azureADPrivRoleAssignment.EndDateTime ` 1520 | -PIMAssignmentState $azureADPrivRoleAssignmentState 1521 | } 1522 | } 1523 | $progressOperation = "$progressCount/$progressLimit" 1524 | $progressPercentage = ($progressCount/$progressLimit)*100 1525 | Write-Progress -Activity "Enumerating High Privileged Directory Role Assignments (via PIM)" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1526 | } 1527 | Write-Progress -Activity "Enumerating High Privileged Directory Role Assignments (via PIM)" -Status "Ready" -Completed 1528 | 1529 | ## Display Non High Privileged Role Assignments 1530 | __AAP-DisplayNonHighPrivilegedRoleAssignments -NonHighPrivilegedRoleAssignments $nonHighPrivAssignments 1531 | 1532 | ## 1533 | ## Groups 1534 | ## Enumerating AppRoles assigned to a grouo 1535 | ## 1536 | $mgGroups = Get-MgGroup -All 1537 | $progressCount = 0 1538 | $progressLimit = $mgGroups.Count 1539 | ForEach($mgGroup in $mgGroups ){ 1540 | $progressCount += 1 1541 | $groupMarkedAsHighvalue = $false 1542 | $groupHighValueConfidenceLevel = 0 1543 | $groupAppRoleAssignments = Get-MgGroupAppRoleAssignment -GroupId $mgGroup.Id 1544 | 1545 | ## Enumerating AppRoles assigned to a grouo 1546 | If( $groupAppRoleAssignments ){ 1547 | ForEach($appRoleAssignment in $groupAppRoleAssignments ){ 1548 | $appRole = $null 1549 | $appRoleValue = 'default' 1550 | If( $appRoleAssignment.AppRoleId -ne '00000000-0000-0000-0000-000000000000' ){ 1551 | $appRole = ((Get-MgServicePrincipal -ServicePrincipalId $appRoleAssignment.ResourceId).AppRoles | ? {$_.Id -eq $appRoleAssignment.AppRoleId} | Select-Object -First 1) 1552 | $appRoleValue = $appRole.Value 1553 | } 1554 | 1555 | If( $appRole -And $appRole.IsEnabled ){ 1556 | $highPrivConfidenceLevel = __AAP-AppRoleIsHighPrivilegeConfidenceGuess -AppRoleObject $appRole 1557 | If( $highPrivConfidenceLevel -gt 0 ){ 1558 | $highPrivReason = "Assigned AppRole '$($appRole.Value)' of Resource '$($appRoleAssignment.ResourceDisplayName)'" 1559 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $mgGroup.Id -PrincipalName $mgGroup.DisplayName -Reason $highPrivReason -PrincipalType 'Group' -ConfidenceLevel $highPrivConfidenceLevel 1560 | $groupMarkedAsHighvalue = $true 1561 | $groupHighValueConfidenceLevel = $highPrivConfidenceLevel 1562 | } 1563 | } 1564 | } 1565 | } 1566 | If( $groupMarkedAsHighvalue ){ 1567 | ## If the group is high privilege, all its members are high privilege 1568 | $groupMembers = Get-MgGroupTransitiveMember -GroupId $mgGroup.Id -All 1569 | ForEach($groupMember in $groupMembers){ 1570 | $principalID = $groupMember.Id 1571 | $principalType = 'Unknown' 1572 | $principalName = '' 1573 | $additionalProperties = $groupMember.AdditionalProperties 1574 | If( $additionalProperties ){ 1575 | $additionalPropertiesObjType = $additionalProperties['@odata.type'] 1576 | Switch($additionalPropertiesObjType){ 1577 | '#microsoft.graph.user' { 1578 | $principalType = 'User' 1579 | $principalName = $additionalProperties['userPrincipalName'] 1580 | Break 1581 | } 1582 | '#microsoft.graph.group' { 1583 | $principalType = 'Group' 1584 | $principalName = $additionalProperties['displayName'] 1585 | Break 1586 | } 1587 | '#microsoft.graph.servicePrincipal' { 1588 | $principalType = 'ServicePrincipal' 1589 | $principalName = $additionalProperties['appDisplayName'] 1590 | Break 1591 | } 1592 | '#microsoft.graph.device' { 1593 | ## Currently not including devices 1594 | Break 1595 | } 1596 | } 1597 | } 1598 | $highPrivReason = "Member of high privilege group '$($mgGroup.DisplayName)'" 1599 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $groupHighValueConfidenceLevel 1600 | } 1601 | } 1602 | 1603 | $progressOperation = "$progressCount/$progressLimit" 1604 | $progressPercentage = ($progressCount/$progressLimit)*100 1605 | Write-Progress -Activity "Enumerating High Privilege Groups" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1606 | } 1607 | Write-Progress -Activity "Enumerating High Privilege Groups" -Status "Ready" -Completed 1608 | 1609 | ## 1610 | ## Service Principals 1611 | ## Enumerating AppRoles assigned to Service Princiapals 1612 | ## Enumerating AppRoles assigned from Service Principals to Users, Groups and Service Principals 1613 | ## Enumerating owners of service principals 1614 | ## Enumerating owners of corresponding applications 1615 | ## Enumerating objects owned by service principals 1616 | ## 1617 | $mgServicePrincipals = Get-MgServicePrincipal -All 1618 | $progressCount = 0 1619 | $progressLimit = $mgServicePrincipals.Count 1620 | ForEach($mgServicePrincipal in $mgServicePrincipals ){ 1621 | $progressCount += 1 1622 | $appRolesAssignedToServicePrincipals = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $mgServicePrincipal.Id -All 1623 | $principalsWithAssignedAppRoleToServicePrincipal = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $mgServicePrincipal.Id -All 1624 | $applicationObjectsOfServicePrincipal = Get-MgApplication -Filter "AppId eq '$($mgServicePrincipal.AppId)'" -All 1625 | $objectsOwnedByServicePrincipal = Get-MgServicePrincipalOwnedObject -ServicePrincipalId $mgServicePrincipal.Id -All 1626 | 1627 | $servicePrincipalMarkedAsHighvalue = $false 1628 | $servicePrincipalHighValueConfidenceLevel = 0 1629 | 1630 | ## Enumerating AppRoles assigned to Service Princiapals 1631 | If( $appRolesAssignedToServicePrincipals ){ 1632 | ForEach($appRoleAssignment in $appRolesAssignedToServicePrincipals){ 1633 | $appRole = $null 1634 | $appRoleValue = 'default' 1635 | If( $appRoleAssignment.AppRoleId -ne '00000000-0000-0000-0000-000000000000' ){ 1636 | $appRole = ((Get-MgServicePrincipal -ServicePrincipalId $appRoleAssignment.ResourceId).AppRoles | ? {$_.Id -eq $appRoleAssignment.AppRoleId} | Select-Object -First 1) 1637 | $appRoleValue = $appRole.Value 1638 | } 1639 | 1640 | If( $appRole -And $appRole.IsEnabled ){ 1641 | $highPrivConfidenceLevel = __AAP-AppRoleIsHighPrivilegeConfidenceGuess -AppRoleObject $appRole 1642 | If( $highPrivConfidenceLevel -gt 0 ){ 1643 | $highPrivReason = "Assigned AppRole '$($appRole.Value)' of Resource '$($appRoleAssignment.ResourceDisplayName)'" 1644 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $mgServicePrincipal.Id -PrincipalName $mgServicePrincipal.AppDisplayName -Reason $highPrivReason -PrincipalType 'ServicePrincipal' -ConfidenceLevel $highPrivConfidenceLevel 1645 | $servicePrincipalMarkedAsHighvalue = $true 1646 | $servicePrincipalHighValueConfidenceLevel = $highPrivConfidenceLevel 1647 | } 1648 | } 1649 | } 1650 | } 1651 | 1652 | ## Enumerating AppRoles assigned from Service Principals to Users, Groups and Service Principals 1653 | If( $principalsWithAssignedAppRoleToServicePrincipal ){ 1654 | ForEach($appRoleAssignment in $principalsWithAssignedAppRoleToServicePrincipal){ 1655 | ## 00000000-0000-0000-0000-000000000000 is the default appRoleID 1656 | If( $appRoleAssignment.AppRoleId -ne '00000000-0000-0000-0000-000000000000' ){ 1657 | $appRole = $mgServicePrincipal.appRoles | ? { $_.Id -eq $appRoleAssignment.AppRoleId } | Select-Object -First 1 1658 | If( $appRole -And $appRole.IsEnabled ){ 1659 | $highPrivConfidenceLevel = __AAP-AppRoleIsHighPrivilegeConfidenceGuess -AppRoleObject $appRole 1660 | If( $highPrivConfidenceLevel -gt 0 ){ 1661 | $highPrivReason = "Assigned AppRole '$($appRole.Value)' of Resource '$($appRoleAssignment.ResourceDisplayName)'" 1662 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $appRoleAssignment.PrincipalId -PrincipalName $appRoleAssignment.PrincipalDisplayName -Reason $highPrivReason -PrincipalType $appRoleAssignment.PrincipalType -ConfidenceLevel $highPrivConfidenceLevel 1663 | } 1664 | } 1665 | } 1666 | } 1667 | } 1668 | 1669 | ## Check if service principal has been marked as high value in previous steps 1670 | If(-Not $servicePrincipalMarkedAsHighvalue){ 1671 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $mgServicePrincipal.Id ){ 1672 | $servicePrincipalMarkedAsHighvalue = $true 1673 | $entryArray = $script:gHighPrivilegdPrincipalMap.Item($mgServicePrincipal.Id) 1674 | $highestConfidenceLevel = $entryArray | Sort-Object { $_.ConfidenceLevel } -Descending | Select-Object -First 1 | %{ $_.ConfidenceLevel } 1675 | $servicePrincipalHighValueConfidenceLevel = $highestConfidenceLevel 1676 | } 1677 | } 1678 | 1679 | ## Enumerating owners of service principals 1680 | If( $servicePrincipalMarkedAsHighvalue ) { 1681 | $ownersOfServicePrincipal = Get-MgServicePrincipalOwner -All -ServicePrincipalId $mgServicePrincipal.Id 1682 | ForEach($ownerOfServicePrincipal in $ownersOfServicePrincipal){ 1683 | $principalID = $ownerOfServicePrincipal.Id 1684 | $principalType = 'Unknown' 1685 | $principalName = '' 1686 | $additionalProperties = $ownerOfServicePrincipal.AdditionalProperties 1687 | If( $additionalProperties ){ 1688 | $additionalPropertiesObjType = $additionalProperties['@odata.type'] 1689 | Switch($additionalPropertiesObjType){ 1690 | '#microsoft.graph.user' { 1691 | $principalType = 'User' 1692 | $principalName = $additionalProperties['userPrincipalName'] 1693 | Break 1694 | } 1695 | '#microsoft.graph.group' { 1696 | $principalType = 'Group' 1697 | $principalName = $additionalProperties['displayName'] 1698 | Break 1699 | } 1700 | '#microsoft.graph.servicePrincipal' { 1701 | $principalType = 'ServicePrincipal' 1702 | $principalName = $additionalProperties['appDisplayName'] 1703 | Break 1704 | } 1705 | } 1706 | } 1707 | $highPrivReason = "Owns high privilege service principal '$($mgServicePrincipal.AppDisplayName)'" 1708 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $servicePrincipalHighValueConfidenceLevel 1709 | } 1710 | } 1711 | 1712 | ## Enumerating owners of corresponding applications 1713 | If( $servicePrincipalMarkedAsHighvalue -And $applicationObjectsOfServicePrincipal ){ 1714 | ForEach($applicationObjectOfServicePrincipal in $applicationObjectsOfServicePrincipal){ 1715 | $applicationOwnersProperties = Get-MgApplicationOwner -ApplicationId $applicationObjectOfServicePrincipal.Id -All 1716 | ForEach($applicationOwnerProperties in $applicationOwnersProperties){ 1717 | $principalID = $applicationOwnerProperties.Id 1718 | $principalType = 'Unknown' 1719 | $principalName = '' 1720 | $additionalProperties = $applicationOwnerProperties.AdditionalProperties 1721 | If( $additionalProperties ){ 1722 | $additionalPropertiesObjType = $additionalProperties['@odata.type'] 1723 | Switch($additionalPropertiesObjType){ 1724 | '#microsoft.graph.user' { 1725 | $principalType = 'User' 1726 | $principalName = $additionalProperties['userPrincipalName'] 1727 | Break 1728 | } 1729 | '#microsoft.graph.group' { 1730 | $principalType = 'Group' 1731 | $principalName = $additionalProperties['displayName'] 1732 | Break 1733 | } 1734 | '#microsoft.graph.servicePrincipal' { 1735 | $principalType = 'ServicePrincipal' 1736 | $principalName = $additionalProperties['appDisplayName'] 1737 | Break 1738 | } 1739 | } 1740 | } 1741 | $highPrivReason = "Owns the application object of the high privilege service principal '$($mgServicePrincipal.AppDisplayName)'" 1742 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason $highPrivReason -PrincipalType $principalType -ConfidenceLevel $servicePrincipalHighValueConfidenceLevel 1743 | } 1744 | } 1745 | } 1746 | 1747 | ## Enumerating objects owned by service principals 1748 | If( $objectsOwnedByServicePrincipal ){ 1749 | ForEach($objectOwnedByServicePrincipal in $objectsOwnedByServicePrincipal){ 1750 | ## Check if the owned object is of high value 1751 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $objectOwnedByServicePrincipal.Id ){ 1752 | ## Get confidence Level of owned object 1753 | $entryArray = $script:gHighPrivilegdPrincipalMap.Item($objectOwnedByServicePrincipal.Id) 1754 | $highestConfidenceLevel = $entryArray | Sort-Object { $_.ConfidenceLevel } -Descending | Select-Object -First 1 | %{ $_.ConfidenceLevel } 1755 | ## gather additional information 1756 | $additionalProperties = $objectOwnedByServicePrincipal.AdditionalProperties 1757 | If(-Not $additionalProperties){ 1758 | $mgDirectoryObject = Get-MgDirectoryObjectById -Ids $objectOwnedByServicePrincipal.Id 1759 | $additionalProperties = $mgDirectoryObject.AdditionalProperties 1760 | } 1761 | $principalID = $objectOwnedByServicePrincipal.Id 1762 | $principalType = 'Unknown' 1763 | $principalName = '' 1764 | If( $additionalProperties ){ 1765 | $additionalPropertiesObjType = $additionalProperties['@odata.type'] 1766 | Switch($additionalPropertiesObjType){ 1767 | '#microsoft.graph.user' { 1768 | $principalType = 'User' 1769 | $principalName = $additionalProperties['userPrincipalName'] 1770 | Break 1771 | } 1772 | '#microsoft.graph.group' { 1773 | $principalType = 'Group' 1774 | $principalName = $additionalProperties['displayName'] 1775 | Break 1776 | } 1777 | '#microsoft.graph.servicePrincipal' { 1778 | $principalType = 'ServicePrincipal' 1779 | $principalName = $additionalProperties['appDisplayName'] 1780 | Break 1781 | } 1782 | } 1783 | } 1784 | $highPrivReason = "Service principal owns high value $($principalType): '$($principalName)' ($($principalID))" 1785 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $mgServicePrincipal.Id -PrincipalName $mgServicePrincipal.AppDisplayName -Reason $highPrivReason -PrincipalType 'ServicePrincipal' -ConfidenceLevel $highestConfidenceLevel 1786 | } 1787 | } 1788 | } 1789 | 1790 | $progressOperation = "$progressCount/$progressLimit" 1791 | $progressPercentage = ($progressCount/$progressLimit)*100 1792 | Write-Progress -Activity "Enumerating High Privilege Service Principals" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1793 | } 1794 | Write-Progress -Activity "Enumerating High Privilege Service Principals" -Status "Ready" -Completed 1795 | 1796 | ## 1797 | ## User owning objects 1798 | ## 1799 | $mgUsers = Get-MgUser -All -ExpandProperty 'ownedObjects' 1800 | $usersOwningObjects = $mgUsers | ?{ $_.OwnedObjects } 1801 | $progressCount = 0 1802 | $progressLimit = $usersOwningObjects.Count 1803 | ForEach( $mgUser in $usersOwningObjects ){ 1804 | $progressCount += 1 1805 | $principalID = $mgUser.Id 1806 | $principalName = $mgUser.UserPrincipalName 1807 | 1808 | ForEach($mgOwnedObjectRef in $mgUser.OwnedObjects){ 1809 | $mgOwnedObj = Get-MgDirectoryObjectById -Ids $mgOwnedObjectRef.Id -ErrorAction SilentlyContinue 1810 | $mgOwnedObjProperties = $mgOwnedObj.AdditionalProperties 1811 | if( $mgOwnedObjProperties ){ 1812 | Switch($mgOwnedObjProperties['@odata.type']){ 1813 | '#microsoft.graph.user' { 1814 | $userName = $mgOwnedObjProperties['userPrincipalName'] 1815 | ## Check if owned user is high value 1816 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $mgOwnedObjectRef.Id ){ 1817 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason "Owns high privilege user '$userName'" -PrincipalType 'User' -ConfidenceLevel 1 1818 | } 1819 | Break 1820 | } 1821 | '#microsoft.graph.group' { 1822 | $groupName = $mgOwnedObjProperties['displayName'] 1823 | ## Check if owned group is high value 1824 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $mgOwnedObjectRef.Id ){ 1825 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason "Owns high privilege group '$groupName'" -PrincipalType 'User' -ConfidenceLevel 1 1826 | } 1827 | Break 1828 | } 1829 | '#microsoft.graph.servicePrincipal' { 1830 | $principalName = $mgOwnedObjProperties['appDisplayName'] 1831 | ## Check if owned service principal is high value 1832 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $mgOwnedObjectRef.Id ){ 1833 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason "Owns high privilege service principal '$principalName'" -PrincipalType 'User' -ConfidenceLevel 1 1834 | } 1835 | Break 1836 | } 1837 | '#microsoft.graph.application' { 1838 | $applicationName = $mgOwnedObjProperties['displayName'] 1839 | $applicationId = $mgOwnedObjProperties['appId'] 1840 | $applicationServicePrincipals = Get-MgServicePrincipal -All -Filter "AppId eq '$($applicationId)'" 1841 | ForEach($applicationServicePrincipal in $applicationServicePrincipals){ 1842 | ## Check if associated service principal is high value 1843 | If( $script:gHighPrivilegdPrincipalMap.Keys -Contains $applicationServicePrincipal.Id ){ 1844 | __AAP-AddToHighPrivilegePrincipalMap -PrincipalID $principalID -PrincipalName $principalName -Reason "Owns application '$($applicationName)' with high privilege service principal '$($applicationServicePrincipal.AppDisplayName)'" -PrincipalType 'User' -ConfidenceLevel 1 1845 | } 1846 | } 1847 | Break 1848 | } 1849 | Default { 1850 | ## Print for debug purposes 1851 | $mgOwnedObjProperties.Keys | %{ __AAP-Log " $($_): $($mgOwnedObjProperties[$_])" -MsgType $MESSAGE_INFO } 1852 | } 1853 | } 1854 | } 1855 | } 1856 | 1857 | 1858 | $progressOperation = "$progressCount/$progressLimit" 1859 | $progressPercentage = ($progressCount/$progressLimit)*100 1860 | Write-Progress -Activity "Enumerating High Privilege Ownerships" -PercentComplete $progressPercentage -CurrentOperation $progressOperation 1861 | } 1862 | Write-Progress -Activity "Enumerating High Privilege Ownerships" -Status "Ready" -Completed 1863 | 1864 | } 1865 | End { 1866 | $script:gLastCollectionOfHighPrivilegdPrincipalMap = (Get-Date) 1867 | __AAP-DisplayHighPrivilegePrincipalMap 1868 | } 1869 | } 1870 | 1871 | Function Enumerate-MFAStatusOfHighPrivilegePrincipals { 1872 | PARAM( 1873 | [Parameter()] 1874 | [String] 1875 | $Tenant, 1876 | 1877 | [Parameter()] 1878 | [String] 1879 | $Outfile = $false 1880 | ) 1881 | Begin { 1882 | __AAP-ConnectIfNecessary -Tenant $Tenant 1883 | } 1884 | Process { 1885 | 1886 | ## Check if the high privileged principals have already been enumerated in the past 1887 | If( $script:gLastCollectionOfHighPrivilegdPrincipalMap ){ 1888 | ## Check how long ago this was 1889 | $currentTimeStamp = (Get-Date) 1890 | $diffToLastEnumRun = New-TimeSpan -End $currentTimeStamp -Start $script:gLastCollectionOfHighPrivilegdPrincipalMap 1891 | If( $diffToLastEnumRun.Hours -gt 8 ){ 1892 | __AAP-Log "Last enumeration of high privileged principals is older than 8 hours. Let's run this again..." -MsgType $MESSAGE_WARNING 1893 | Enumerate-AllHighPrivilegePrincipals 1894 | } 1895 | } Else { 1896 | ## Enumerate all high privilege principals if this has not been done before 1897 | Enumerate-AllHighPrivilegePrincipals 1898 | } 1899 | ## Go through the identified high privileged principals 1900 | __AAP-Log "## MFA Status of high privileged principals" 1901 | $accessTokenAADGraph = __AAP-GetAcessTokenForAADGraphWithRefreshToken 1902 | ForEach($principalID in $script:gHighPrivilegdPrincipalMap.Keys){ 1903 | $principalEntries = $script:gHighPrivilegdPrincipalMap[$principalID] 1904 | $firstEntry = $principalEntries[0] 1905 | 1906 | If( $firstEntry['principalType'] -eq 'User' ){ 1907 | ## Per User MFA 1908 | $userMFAStatus = Get-AADIntUserMFA -AccessToken $accessTokenAADGraph -UserPrincipalName $principalID -ErrorAction SilentlyContinue 1909 | If( $userMFAStatus ){ 1910 | $mfaDefaultMethod = $userMFAStatus.DefaultMethod 1911 | If( $userMFAStatus.State -eq "Enforced" ){ 1912 | __AAP-Log "[+] User: $($firstEntry['principalName']) ($($firstEntry['principalID'])): Per User MFA Enforced (Defaullt Method: $mfaDefaultMethod)" -MsgType $MESSAGE_SUCCESS 1913 | } 1914 | ElseIf( $userMFAStatus.State -eq "Enabled" ){ 1915 | __AAP-Log "[!] User: $($firstEntry['principalName']) ($($firstEntry['principalID'])): Per User MFA Enabled, but not enforced (Defaullt Method: $mfaDefaultMethod)" -MsgType $MESSAGE_WARNING 1916 | } 1917 | ElseIf( $userMFAStatus.State -eq "Disabled" ){ 1918 | __AAP-Log "[!] User: $($firstEntry['principalName']) ($($firstEntry['principalID'])): Per User MFA Disabled." -MsgType $MESSAGE_WARNING 1919 | } 1920 | Else { 1921 | __AAP-Log "[?] User: $($firstEntry['principalName']) ($($firstEntry['principalID'])): Per User MFA status unknown/unset." -MsgType $MESSAGE_WARNING 1922 | } 1923 | } 1924 | ## Conditional Access Policies 1925 | __AAP-Log "[*] Applicable MFA Conditional Access Policies for this user:" -MsgType $MESSAGE_INFO 1926 | __AAP-DisplayApplicableMFAConditionalAccessPolicyForUserID -UserID $principalID -IndentationLevel 1 1927 | } 1928 | Else { 1929 | __AAP-Log "[X] $($firstEntry['principalType']): $($firstEntry['principalName']) ($($firstEntry['principalID'])): MFA is not supported for $($firstEntry['principalType'])s." -MsgType $MESSAGE_FAIL 1930 | } 1931 | __AAP-Log "" ## empty newline 1932 | } 1933 | } 1934 | } 1935 | 1936 | If( __AAP-CheckRequiredModules ){ 1937 | __AAP-Log "[*] Loading required modules..." 1938 | $windowTitle = $host.ui.RawUI.WindowTitle 1939 | If( __AAP-ImportRequiredModules ){ 1940 | $host.ui.RawUI.WindowTitle = $windowTitle 1941 | __AAP-Log "[*] Modules imported." 1942 | __AAP-Log $banner -MsgType $MESSAGE_WARNING 1943 | } 1944 | } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Azure-AccessPermissions 2 | 3 | Easy to use PowerShell script to enumerate access permissions in an Azure Active Directory environment. 4 | 5 | Background details can be found in the accompanied blog posts: 6 | 7 | - [Untangling Azure Active Directory Principals & Access Permissions](https://csandker.io/2022/10/19/Untangling-Azure-Permissions.html) 8 | - [Untangling Azure Active Directory Permissions II: Privileged Access](https://csandker.io/2022/11/10/Untangling-Azure-II-Privileged-Access.html) 9 | 10 | ## Requirements 11 | 12 | To run this script you'll need these two PowerShell modules: 13 | - [Microsoft.Graph](https://github.com/microsoftgraph/msgraph-sdk-powershell) 14 | - [AADInternals](https://github.com/Gerenios/AADInternals) 15 | - [AzureADPreview](https://learn.microsoft.com/en-us/powershell/azure/active-directory/install-adv2?view=azureadps-2.0) 16 | 17 | All of these can be installed directly within PowerShell: 18 | 19 | ```PowerShell 20 | PS:> Install-Module Microsoft.Graph 21 | PS:> Install-Module AADInternals 22 | PS:> Install-Module AzureADPreview 23 | ``` 24 | 25 | ## Usage 26 | 27 | ### First time use 28 | 29 | The script uses a browser-based Login UI to connect to Azure. If you run the tool for the first time you might experience the following error 30 | 31 | ```s 32 | [*] Connecting to Microsoft Graph... 33 | WARNING: WebBrowser control emulation not set for PowerShell or PowerShell ISE! 34 | Would you like set the emulation to IE 11? Otherwise the login form may not work! (Y/N): Y 35 | Emulation set. Restart PowerShell/ISE! 36 | ``` 37 | 38 | ![Error on first run](img/first-run-error.png) 39 | 40 | To solve this simply allow PowerShell to emulate the browser and rerun your command. 41 | 42 | ### Example use 43 | 44 | Import and run, no argumentes needed. 45 | 46 | *Note: On your first run you will likely have to authenticate twice (once Microsoft Graph and once against Azure AD Graph). I might wrap this into a single login in the future...* 47 | 48 | ```PowerShell 49 | PS:> Import-Module .\Azure-AccessPermissions.ps1 50 | ``` 51 | 52 | ![SampleOutput](img/sample_output1.png) 53 | 54 | ![SampleOutput](img/sample_output2.png) 55 | -------------------------------------------------------------------------------- /img/first-run-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csandker/Azure-AccessPermissions/13e1523e8ac44a6aafb2623ccb708746e0d50291/img/first-run-error.png -------------------------------------------------------------------------------- /img/sample_output1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csandker/Azure-AccessPermissions/13e1523e8ac44a6aafb2623ccb708746e0d50291/img/sample_output1.png -------------------------------------------------------------------------------- /img/sample_output2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csandker/Azure-AccessPermissions/13e1523e8ac44a6aafb2623ccb708746e0d50291/img/sample_output2.png --------------------------------------------------------------------------------