WMI - Windows Management Instrumentation

WMI was designed to allow local and remote system administration using DCOM or WSMan, it's enabled on all systems by default. Generally it's used to get information about a system, set parameters, execute methods and subscribe to events.

From an offensive PoV, WMI is excellent for recon, remote code execution, persistence and convert storage as WMI-based detection is far behind the rest. WMI persistence can technically be considered RCE as well but it doesn't involve invoking a method.

WMI uses SQL-like syntax, for example

SELECT [Class property name / names | *] FROM [Class name] <WHERE [Condition]>
SELECT * FROM Win32_Service WHERE Name = "PSEXESVC"

PowerShell is considered to be the best tool to interact with WMI and here are some commands to interact with the service

Get-WmiObject -Class Win32_Service
Get-WmiObject -Class Win32_Service -Filter 'Name = "WinDefend"'
Get-WmiObject -Class Win32_Service -Filter 'Name = "WinDefend"' -Property State, PathName
Get-WmiObject -Namespace 'root/cimv2' -Query 'SELECT State, PathName FROM Win32_Service WHERE Name = "WinDefend"'
Get-CimInstance -ClassName Win32_Service
Get-CimInstance -ClassName Win32_Service -Filter 'Name = "WinDefend"'
Get-CimInstance -ClassName Win32_Service -Filter 'Name = "WinDefend"' -Property State, PathName
Get-CimInstance -Namespace 'root/cimv2' -Query 'SELECT State, PathName FROM Win32_Service WHERE Name = "WinDefend"'

Most WMI classes are not well documented but we can use WMI to query for them

Get-WmiObject -Namespace root/cimv2 -Class Meta_Class
Get-WmiObject -Namespace root/default -List
Get-WmiObject -Namespace root -Class __NAMESPACE
Get-CimClass -Namespace root/subscription
Get-CimInstance -Namespace root -ClassName __NAMESPACE

It's also possible to retrieve file content remotely

$FilePath = 'C:\Windows\System32\notepad.exe'
$PSModuleFileClass = Get-CimClass -Namespace ROOT/Microsoft/Windows/Powershellv3 -ClassName PS_ModuleFile
$InMemoryModuleFileInstance = New-CimInstance -CimClass
$PSModuleFileClass -Property @{ InstanceID= $FilePath } -ClientOnly
$FileContents = Get-CimInstance -InputObject $InMemoryModuleFileInstance

Association queries can be used to get further information remotely

ASSOCIATORS OF {[Object].[Key]=[KeyValue]} <WHERE [AssocClass|ResultClass = ClassName]>

for example

  1. List all running processes that have wldp.dll loaded

Get-WmiObject -Query 'ASSOCIATORS OF {CIM_DataFile.Name="c:\\windows\\system32\\wldp.dll"} WHERE AssocClass=CIM_ProcessExecutable'
Get-CimInstance -ClassName CIM_DataFile -Filter 'Drive = "C:" AND Path="\\Windows\\System32\\" AND (Name="C:\\Windows\\System32\\wldp.dll")' -Property Name | Get-CimAssociatedInstance -Association CIM_ProcessExecutable
  1. List members of the local administrator group

Get-CimInstance -ClassName Win32_Group -Filter 'SID = "S-1-5-32-544"' | Get-CimAssociatedInstance -ResultClassName Win32_Account

WMI event queries

Event can be of two types:

  • Intrinsic

    • can be used to detect the creation, modification or deletion of any WMI object instance

    • requires a polling interval to be specified and can affect performance

  • Extrinsic

    • they fire immediately and don't require a polling period

It's possible to enumerate WMI events with Enumerate WMI events.

This is what a query looks like

SELECT [Class property name[s]|*] FROM [INTRINSIC CLASS NAME] WITHIN [POLLING INTERVAL] <WHERE [CONSTRAINT]>
SELECT [Class property name[s]|*] FROM [EXTRINSIC CLASS NAME] <WHERE [CONSTRAINT]>
Register-WmiEvent -Query 'SELECT ProcessName FROM Win32_ProcessStartTrace' -Action { Write-Host "New process: $($EventArgs.NewEvent.ProcessName)" }
Register-CimIndicationEvent -Namespace root/subscription -Query 'SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA "__FilterToConsumerBinding"' -Action {Write-Host 'New WMI persistence!'}

Event queries can persist beyond reboots and execute something in response instead of just being executed in the context of the PowerShell process they're executed from; for this to be possible there are 3 requirements

  1. __EventConsumer: the action to execute

  2. __EventFilter: the event to trigger off of

  3. __FilterToConsumerBinding: binds the filter and consumer together

$EventFilterArgs = @{
	EventNamespace = 'root/cimv2'
	Name = 'DriveChanged'
	Query = 'SELECT * FROM Win32_VolumeChangeEvent'
	QueryLanguage = 'WQL'
}
$Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments $EventFilterArgs
$CommandLineConsumerArgs = @{
	Name = 'Infector'
	CommandLineTemplate = "powershell.exe -NoP -C `"[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String('WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSL VNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=')) | Out-File %DriveName%\eicar.txt`""
}
$Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments
$CommandLineConsumerArgs
$FilterToConsumerArgs = @{ Filter = $Filter; Consumer = $Consumer }
$FilterToConsumerBinding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments
$FilterToConsumerArgs

Last updated