Attaques Azure Run Command : enquêter sur l'exécution VM
Run Command et Custom Script Extension aux mains des attaquants : ce que l'Activity Log enregistre ou omet, et quels artefacts de la VM gardent le script.
En bref. Run Command permet à quiconque détient Microsoft.Compute/virtualMachines/runCommand/action d'exécuter un script sur une VM via l'agent VM (en SYSTEM sous Windows, avec un utilisateur élevé sous Linux) sans aucun chemin réseau vers la VM. L'Activity Log enregistre qui, quand, d'où et sur quelle VM, mais en général pas le script. Le script se trouve sur la VM : C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Downloads\ sous Windows, /var/lib/waagent/run-command/download/ sous Linux. La Custom Script Extension (extensions/write) repose sur la même idée avec une étape de téléchargement. Corrélez avec les flux sortants de la VM dans les minutes qui suivent.
Run Command est la fonctionnalité Azure qui me manquerait le plus en tant qu'administrateur et que je crains le plus en tant que défenseur. Elle transforme une permission du plan de contrôle en exécution de code sur l'invité, et c'est exactement pour cela que les intrus qui obtiennent Contributor sur un abonnement l'utilisent en quelques minutes.
Pourquoi les attaquants aiment Run Command
Microsoft présente Run Command comme un moyen d'exécuter des scripts dans une VM via l'agent VM, utile quand RDP ou SSH est fermé (Windows, Linux). Du point de vue d'un attaquant :
| Propriété (selon Microsoft Learn) | Ce que cela signifie pour un attaquant |
|---|---|
| Les scripts s'exécutent en System sous Windows, avec un utilisateur élevé sous Linux | Privilège maximal immédiat sur l'invité |
Nécessite virtualMachines/runCommand/action ; Virtual Machine Contributor et au-delà l'ont | N'importe quelle identité volée de niveau Contributor suffit |
| Fonctionne sans RDP/SSH ; l'agent récupère la tâche | Les NSG et pare-feu ne l'arrêtent pas |
| Sortie limitée aux 4 096 derniers octets, 90 minutes maximum | Les attaquants lancent un téléchargeur et repartent |
La séquence typique : exécuter RunShellScript ou RunPowerShellScript avec une commande d'une ligne qui télécharge une deuxième charge, extrait des identifiants ou interroge l'Instance Metadata Service pour obtenir le jeton de l'identité managée de la VM. Ce dernier cas transforme l'exécution sur la VM en nouvelle identité cloud (abus d'identités managées).
MITRE ATT&CK suit cette technique sous T1651 Cloud Administration Command. La Azure Threat Research Matrix de Microsoft la référence sous AZT301.1.
Ce que montre l'Activity Log
| Opération | Signification |
|---|---|
Microsoft.Compute/virtualMachines/runCommand/action | Run Command « action » sur une VM (le classique) |
Microsoft.Compute/virtualMachines/runCommands/write | Ressource Run Command managée créée ou modifiée |
Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action | Run Command sur une instance de groupe identique |
Microsoft.HybridCompute/machines/runCommands/write | Run Command sur un serveur Azure Arc |
Microsoft.Compute/virtualMachines/extensions/write | Extension installée ou mise à jour (Custom Script Extension, entre autres) |
Microsoft.SerialConsole/serialPorts/connect/action | Connexion à la console série |
Chaque Run Command produit généralement un événement Started, un Accepted et un Succeeded partageant le même correlationId. Vous obtenez l'appelant, les claims, l'IP de l'appelant et la VM ciblée. Ce que vous n'obtenez généralement pas, c'est le script. L'article de Mandiant Azure Run Command for Dummies est explicite : l'Activity Log montre l'action mais pas le contenu du script ; il faut la VM pour le récupérer.
Où se trouve le script sur la VM
D'après les recherches de Mandiant et les notes de dépannage de Microsoft :
| OS | Artefact | Chemin |
|---|---|---|
| Windows | Contenu du script | C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Downloads\script<n>.ps1 |
| Windows | Statut et sortie | C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Status\<n>.status |
| Windows | Log de l'extension | C:\WindowsAzure\Logs\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\RunCommandExtension.log |
| Linux | Script, stdout, stderr | /var/lib/waagent/run-command/download/<n>/ (script.sh, stdout, stderr) |
| Linux | Log du handler (horodatages, sans script) | /var/log/azure/run-command/handler.log |
Sous Windows, RunPowerShellScript laisse aussi une télémétrie PowerShell (script block logging, événement 4104) si elle était activée. Collectez ces fichiers avant de reconstruire la VM : snapshot du disque OS, puis image ou montage en lecture seule.
La variante Custom Script Extension
La Custom Script Extension télécharge les fichiers listés dans fileUris et exécute commandToExecute. Elle s'installe avec Microsoft.Compute/virtualMachines/extensions/write ; l'éditeur et le type sont Microsoft.Compute / CustomScriptExtension sous Windows et Microsoft.Azure.Extensions / CustomScript sous Linux (Windows, Linux).
Notes forensiques :
- La commande peut se trouver dans
settings(en clair dans le corps de la requête) ouprotectedSettings(chiffrée pour la VM). Les attaquants qui le savent utilisent les paramètres protégés. - Sous Windows elle s'exécute en
LocalSystem; les téléchargements vont dansC:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.*\Downloads\<n>et les logs dansC:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension. - Sous Linux, téléchargements,
stdoutetstderrvont dans/var/lib/waagent/custom-script/download/<n>/, et le log du handler est/var/log/azure/custom-script/handler.log. az vm extension listmontre les paramètres publics actuels, y compriscommandToExecutes'il n'était pas protégé. Seulement l'état actuel, pas l'historique.
Ce que signale l'analyseur
| Règle | Détecte | Niveau |
|---|---|---|
AZ-VM-001 | Run Command sur une VM, une instance de groupe identique ou une machine Arc | Haut |
AZ-VM-002 | Custom Script Extension déployée (extensions/write de type custom script) | Haut |
AZ-VM-003 | Connexion à la console série | Moyen |
AZ-VM-004 | VM créée dans une région jamais utilisée pendant la période d'apprentissage | Moyen |
AZ-VM-005 | Disque ou snapshot exporté via une URL SAS (beginGetAccess) | Haut |
AZ-VM-006 | Snapshot de disque créé | Faible |
AZ-NET-004 | Les flow logs montrent du trafic avec une IP impliquée dans d'autres constats | Haut |
AZ-VM-001 est bruyant là où Run Command sert à l'administration. C'est voulu : l'objectif est de mettre chaque exécution sous les yeux d'un humain, avec son appelant et son IP, et la chronologie de l'analyseur Azure Forensics montre ce qui s'est passé juste après.
Checklist de tri
- Qui l'a lancé ? Un administrateur humain depuis la plage IP de l'entreprise en heures ouvrées, ou un principal de service qui déploie normalement des applications web ?
- L'IP de l'appelant est-elle nouvelle ?
AZ-ID-001signale les principaux de service utilisés depuis des IP jamais vues. - Qu'a fait la VM ensuite ? Dans les flow logs VNet ou NSG, cherchez des connexions sortantes de l'IP privée de la VM vers une IP publique dans les minutes qui suivent. Dans l'exemple fictif, la VM contacte l'IP de l'attaquant sur le port 80 environ 25 secondes après le début du Run Command.
- L'identité managée de la VM est-elle apparue ailleurs ? Un jeton d'identité managée utilisé depuis une autre IP que celle de la VM est la signature d'un vol de jeton via IMDS (
AZ-ID-002). - Récupérez le script depuis le snapshot du disque, aux chemins ci-dessus.
- Vérifiez l'export de disques.
beginGetAccesssur un disque ou un snapshot renvoie une URL de téléchargement du disque entier (MITRE T1537).
Remédiation
- Isolez la VM (NSG qui bloque tout), faites des snapshots de ses disques pour l'analyse, reconstruisez-la à partir d'une image saine.
- Supprimez les extensions inconnues et les run commands managées ; retirez l'extension RunCommand si vous ne l'utilisez pas.
- Faites tourner les identifiants présents sur la VM ou accessibles par son identité managée.
- Limitez
runCommand/actionavec des rôles personnalisés ; Virtual Machine Contributor l'accorde.