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
}