List-OrphanRedirectUri.ps1
Function List-OrphanRedirectUri { #Requires -Module 'DnsClient-PS' [CmdletBinding()] ################################################################ ## Signature $t = @' Package designed and managed by ___ _ _ ___ _ / __| |___ _ _ __| | / __| ___ _ ___ _(_)__ ___ ___ | (__| / _ \ || / _ | \__ \/ -_) '_\ V / / _/ -_|_-< \___|_\___/\_,_\__,_| |___/\___|_| \_/|_\__\___/__/ Powershell package designed to perform vulnerability assesment for App Registration Redirect URIs. Contact- Azure Cloud Servives Product Owner. '@ Write-Verbose "$($t)" ################################################################ $t = @' +----------------+-----------------------------------------------------------+ | Output generated as array that can be stored in the variable while running | | the function. | | Eg: $Result = List-OrphanRedirectUri | +----------------+-----------------------------------------------------------+ '@ Write-Warning "How to retrive the results for parsing ?`n$($t)" # Authentication part Write-Verbose 'Checking ManagedIdentity Authentication to platform.' try { Connect-AzAccount -Identity } catch { Write-Warning "Not enough autherization to 'Managed Identity' for performing operations." } # Constructing MS Graph Authentication header $azContext = Get-AzContext $token = Get-AzAccessToken -ResourceTypeName MSGraph $headers = @{ 'Content-Type' = 'application/json' 'Authorization' = 'Bearer ' + $token.Token } $APIResponse = Invoke-RestMethod 'https://graph.microsoft.com/v1.0/applications' -Method 'GET' -Headers $headers $CloudAppRegistration = $APIResponse.Value $UserNextLink = $APIResponse.'@odata.nextLink' while ($UserNextLink -ne $null) { $APIResponse = (Invoke-RestMethod -Headers $headers -Uri $UserNextLink -Method Get) $UserNextLink = $APIResponse.'@odata.nextLink' $CloudAppRegistration += $APIResponse.value } Write-Verbose "$($CloudAppRegistration.Count)" # $CloudAppRegistration | Format-Table DisplayName -AutoSize # $response | ConvertTo-Json $Url = "https://graph.microsoft.com/v1.0/applications?$select=displayName" #$headers = @{Authorization = "Bearer $($Tokenresponse.access_token)"} $CloudUser = @() do { $UserResponse = Invoke-RestMethod -Headers $headers -Uri $Url -Method Get $CloudUser += $UserResponse.Value $Url = $UserResponse.'@odata.nextLink' } while ($Url) $Array = @(); foreach ($currentItemName in $CloudUser) { $redirects = $currentItemName | Where-Object { $_.web.redirectUris -match 'azurewebsites.net' } foreach ($parsed in $redirects) { Write-Verbose "$($redirects.displayName)" try { foreach ($testUri in $redirects.web.redirectUris) { $toPing = ($testUri -split 'https://' -split '/')[1] if ($toPing) { Write-Verbose " Pinging $($toPing)" $s = Resolve-Dns $toPing; # if ($s) { # Write-Verbose " No vulnerability identified for ['$($testUri)] | [$($redirects.displayName)]" # } # ('-' * 75) } } } catch { $owner = "https://graph.microsoft.com/v1.0/myorganization/applications/$($currentItemName.Id)/owners" $response2 = Invoke-RestMethod $owner -Method 'GET' -Headers $headers $owners = ($response2.value.userPrincipalName -join '; ') $Array += [PSCustomObject]@{ 'SPN_Name' = $($redirects.displayName) 'App' = $testUri 'Owners' = $owners 'passwordCredentials' = $($redirects.displayName.passwordCredentials.endDateTime) } Write-Warning "Subdomain takeover possible for [$($testUri)] | [$($redirects.displayName)]" } } } # $Array | Format-Table Write-Output $Array } ########################################################################### $data = List-OrphanRedirectUri Write-Verbose $each_SP_Owners = $data $spnOwnersListSize = ($each_SP_Owners.Owners | Measure-Object).Count Write-Verbose "Count of SPN Owners - $spnOwnersListSize" $graphversion = 'v1.0' $url = 'https://graph.microsoft.com' $endpoint = "users/?`$select=" Connect-MgGraph -AccessToken $token.Token $invalidUPN = @() if ($spnOwnersListSize -gt 0) { foreach ($spnOwnersEmail in $each_SP_Owners) { $spnOwnerEmail = ($spnOwnersEmail.Owners).Split(';').replace(' ', '') foreach ($UPN in $spnOwnerEmail) { Write-Host "--- $($UPN)" if ($UPN) { $filterOwner = "DisplayName,mail,accountEnabled&filter=mail eq '$($UPN)'" $filterInactiveOwner = ' and accountEnabled eq false' $body = @{} $uri = "$url/$graphversion/$endpoint$filterOwner$filterInactiveOwner" $results = Invoke-MgGraphRequest ` -Uri $uri ` -Method GET ` -Body $body if ($results.value) { $invalidUPN += [PSCustomObject]@{ 'AppName' = $spnOwnersEmail.SPN_Name 'RedirectURI' = $spnOwnersEmail.App 'OwnerUPN' = $UPN 'Status' = 'InAvtive_User' # "Usage Count" = ($SignIns.value ).count } } else { Write-Host "Sending mail to $($UPN)" -ForegroundColor Green Write-Host "$($spnOwnersEmail.SPN_Name) | $($UPN)" -ForegroundColor Green } } } } $invalidUPN } |