Ataques con Azure Run Command: investigar ejecución en VM
Cómo usan los atacantes Run Command y la Custom Script Extension en Azure, qué registra (y omite) el Activity Log y qué artefactos de la VM conservan el script.
En resumen. Run Command permite a cualquiera con Microsoft.Compute/virtualMachines/runCommand/action ejecutar un script en una VM a través del agente de VM (como SYSTEM en Windows, como usuario con privilegios elevados en Linux) sin ningún camino de red hacia la VM. El Activity Log registra quién, cuándo, desde dónde y en qué VM, pero por lo general no el script. El script está en la VM: C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Downloads\ en Windows, /var/lib/waagent/run-command/download/ en Linux. La Custom Script Extension (extensions/write) es la misma idea con un paso de descarga. Correlaciona con los flujos salientes de la VM en los minutos siguientes.
Run Command es la función de Azure que más echaría de menos como administrador y la que más temo como defensor. Convierte un permiso del plano de control en ejecución de código en el sistema invitado, y precisamente por eso los intrusos que consiguen Contributor en una suscripción la usan en cuestión de minutos.
Por qué a los atacantes les gusta Run Command
Microsoft describe Run Command como una forma de ejecutar scripts dentro de una VM mediante el agente de VM, útil cuando RDP o SSH están cerrados (Windows, Linux). Desde el punto de vista de un atacante:
| Propiedad (según Microsoft Learn) | Qué significa para un atacante |
|---|---|
| Los scripts se ejecutan como System en Windows y como usuario con privilegios elevados en Linux | Máximo privilegio inmediato en el invitado |
Requiere virtualMachines/runCommand/action; Virtual Machine Contributor y superiores lo tienen | Basta con cualquier identidad robada de nivel Contributor |
| Funciona sin RDP/SSH; el agente recoge el trabajo | Los NSG y cortafuegos no lo detienen |
| Salida limitada a los últimos 4096 bytes, máximo 90 minutos | Los atacantes lanzan un descargador y se van |
La secuencia típica: ejecutar RunShellScript o RunPowerShellScript con una línea que descarga una segunda fase, vuelca credenciales o consulta el Instance Metadata Service para obtener el token de la identidad administrada de la VM. Esto último convierte la ejecución en la VM en una nueva identidad cloud (abuso de identidades administradas).
MITRE ATT&CK lo registra como T1651 Cloud Administration Command. La Azure Threat Research Matrix de Microsoft lo incluye como AZT301.1.
Qué muestra el Activity Log
| Operación | Significado |
|---|---|
Microsoft.Compute/virtualMachines/runCommand/action | Run Command de tipo «acción» en una VM (el clásico) |
Microsoft.Compute/virtualMachines/runCommands/write | Recurso de Run Command administrado creado o actualizado |
Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action | Run Command en una instancia de un conjunto de escalado |
Microsoft.HybridCompute/machines/runCommands/write | Run Command en un servidor habilitado para Azure Arc |
Microsoft.Compute/virtualMachines/extensions/write | Extensión instalada o actualizada (Custom Script Extension, entre otras) |
Microsoft.SerialConsole/serialPorts/connect/action | Conexión a la consola serie |
Cada Run Command suele producir un evento Started, uno Accepted y uno Succeeded que comparten correlationId. Obtienes el autor, las claims, la IP del autor y la VM objetivo. Lo que normalmente no obtienes es el script. El artículo de Mandiant Azure Run Command for Dummies deja claro que el Activity Log muestra la acción pero no el contenido del script; recuperarlo exige la VM.
Dónde está el script en la VM
Según la investigación de Mandiant y las notas de solución de problemas de Microsoft:
| SO | Artefacto | Ruta |
|---|---|---|
| Windows | Contenido del script | C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Downloads\script<n>.ps1 |
| Windows | Estado y salida | C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\<version>\Status\<n>.status |
| Windows | Log de la extensión | 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 del handler (marcas de tiempo, sin script) | /var/log/azure/run-command/handler.log |
En Windows, RunPowerShellScript deja también telemetría de PowerShell (script block logging, evento 4104) si estaba activada. Recoge estos archivos antes de reconstruir la VM: snapshot del disco del SO y después imagen o montaje de solo lectura.
La variante Custom Script Extension
La Custom Script Extension descarga archivos desde fileUris y ejecuta commandToExecute. Se instala con Microsoft.Compute/virtualMachines/extensions/write; el publicador y el tipo son Microsoft.Compute / CustomScriptExtension en Windows y Microsoft.Azure.Extensions / CustomScript en Linux (Windows, Linux).
Notas forenses:
- El comando puede ir en
settings(en claro en el cuerpo de la solicitud) o enprotectedSettings(cifrado para la VM). Los atacantes que lo saben usan la configuración protegida. - En Windows se ejecuta como
LocalSystem; las descargas van aC:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.*\Downloads\<n>y los logs aC:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension. - En Linux, las descargas,
stdoutystderrvan a/var/lib/waagent/custom-script/download/<n>/, y el log del handler es/var/log/azure/custom-script/handler.log. az vm extension listmuestra la configuración pública actual, incluidocommandToExecutesi no estaba protegido. Solo el estado actual, no el historial.
Qué marca el analizador
| Regla | Detecta | Nivel |
|---|---|---|
AZ-VM-001 | Run Command en una VM, una instancia de conjunto de escalado o una máquina Arc | Alto |
AZ-VM-002 | Custom Script Extension desplegada (extensions/write de tipo custom script) | Alto |
AZ-VM-003 | Conexión a la consola serie | Medio |
AZ-VM-004 | VM creada en una región no usada durante el periodo de aprendizaje | Medio |
AZ-VM-005 | Disco o snapshot exportado con una URL SAS (beginGetAccess) | Alto |
AZ-VM-006 | Snapshot de disco creado | Bajo |
AZ-NET-004 | Los flow logs muestran tráfico con una IP implicada en otros hallazgos | Alto |
AZ-VM-001 es ruidoso en entornos que usan Run Command para administrar. No pasa nada: la idea es poner cada ejecución delante de una persona, con su autor y su IP, y la cronología del analizador Azure Forensics muestra lo que ocurrió justo después.
Lista de triaje
- ¿Quién lo ejecutó? ¿Un administrador humano desde el rango de IP de la empresa en horario laboral, o una entidad de servicio que normalmente despliega aplicaciones web?
- ¿Es nueva la IP del autor?
AZ-ID-001marca las entidades de servicio usadas desde IP nunca vistas. - ¿Qué hizo la VM a continuación? En los flow logs de VNet o NSG, busca conexiones salientes desde la IP privada de la VM hacia una IP pública en los minutos siguientes. En el ejemplo ficticio, la VM conecta con la IP del atacante por el puerto 80 unos 25 segundos después de que empiece el Run Command.
- ¿Apareció la identidad administrada de la VM en otro sitio? Un token de identidad administrada usado desde una IP distinta de la de la VM es la firma del robo de tokens vía IMDS (
AZ-ID-002). - Consigue el script a partir del snapshot del disco, en las rutas anteriores.
- Comprueba la exportación de discos.
beginGetAccesssobre un disco o snapshot devuelve una URL de descarga del disco completo (MITRE T1537).
Remediación
- Aísla la VM (NSG que lo deniegue todo), haz snapshots de sus discos para el análisis y reconstrúyela desde una imagen fiable.
- Elimina extensiones desconocidas y run commands administrados; quita la extensión RunCommand si no la usas.
- Rota las credenciales que estuvieran en la VM o al alcance de su identidad administrada.
- Limita
runCommand/actioncon roles personalizados; Virtual Machine Contributor lo concede.