Getting Started with WMI Weaponization – Part 2

Exploring WMI Classes, Properties, and Methods

What is a WMI class?

A WMI class, such as Win32_Process is a grouping of like properties and methods. Using SQL as an analogy, a property is like a SQL column and a method is similar to a stored procedure.

Exploring WMI Classes

Using CIM, it is possible to easily explore classes with the Get-CimClass command. For those systems that do not yet have the CIM commandlets, Get-WmiObject -list will also work. These commands make it quite simple to explore what is available in WMI on a given system. Let’s start by looking how many WMI classes there are on the system.

Get-CimClass | Measure-Object

Count : 1513
Average :
Sum :
Maximum :
Minimum :
Property :

There are 1,513 WMI classes on this system. Lets try to filter out some to find one’s that are useful for our current task at hand by searching for the keyword process.

Get-CimClass -ClassName *process* | Measure-Object

Count : 43
Average :
Sum :
Maximum :
Minimum :
Property :

Get-CimClass -ClassName *process*

 NameSpace: ROOT/CIMV2
CimClassName                        CimClassMethods      CimClassProperties
------------                        ---------------      ------------------
Win32_ProcessTrace                  {}                   {SECURITY_DESCRIPTOR, TIME_CREATED, ParentProcessID, Proces...
Win32_ProcessStartTrace             {}                   {SECURITY_DESCRIPTOR, TIME_CREATED, ParentProcessID, Proces...
Win32_ProcessStopTrace              {}                   {SECURITY_DESCRIPTOR, TIME_CREATED, ParentProcessID, Proces...
CIM_ProcessExecutable               {}                   {Antecedent, Dependent, BaseAddress, GlobalProcessCount...}
Win32_SessionProcess                {}                   {Antecedent, Dependent}
CIM_AssociatedProcessorMemory       {}                   {Antecedent, Dependent, BusSpeed}
Win32_AssociatedProcessorMemory     {}                   {Antecedent, Dependent, BusSpeed}
CIM_Processor                       {SetPowerState, R... {Caption, Description, InstallDate, Name...}
Win32_Processor                     {SetPowerState, R... {Caption, Description, InstallDate, Name...}
CIM_Process                         {}                   {Caption, Description, InstallDate, Name...}
Win32_Process                       {Create, Terminat... {Caption, Description, InstallDate, Name...}
Win32_ComputerSystemProcessor       {}                   {GroupComponent, PartComponent}
Win32_SystemProcesses               {}                   {GroupComponent, PartComponent}
CIM_ProcessThread                   {}                   {GroupComponent, PartComponent}
CIM_OSProcess                       {}                   {GroupComponent, PartComponent}
Win32_NamedJobObjectProcess         {}                   {Collection, Member}
Win32_PerfRawData_WorkerVpProvid... {}                   {Caption, Description, Name, Frequency_Object...}

That returned 43 WMI classes. In this case lets look for only WMI classes that have a associated methods.

Get-CimClass | Where-Object CimClassMethods -NotLike {} | Measure-Object

Count : 210
Average :
Sum :
Maximum :
Minimum :
Property :

In total there are 210 WMI classes on this system with an associated method. Lets see how many there are if we combine the two filters.

Get-CimClass -ClassName *process* | Where-Object CimClassMethods -NotLike {} | Measure-Object

Count : 3
Average :
Sum :
Maximum :
Minimum :
Property :

Get-CimClass -ClassName *process* | Where-Object CimClassMethods -NotLike {}

 NameSpace: ROOT/cimv2

CimClassName    CimClassMethods      CimClassProperties
------------    ---------------      ------------------
CIM_Processor   {SetPowerState, R... {Caption, Description, InstallDate, Name...}
Win32_Processor {SetPowerState, R... {Caption, Description, InstallDate, Name...}
Win32_Process   {Create, Terminat... {Caption, Description, InstallDate, Name...}

Of these, the Win32_Process class looks to be the most promising, lets look more closely at this one.

Get-CimClass -ClassName Win32_Process

 NameSpace: ROOT/cimv2
CimClassName     CimClassMethods      CimClassProperties
------------     ---------------      ------------------
Win32_Process    {Create, Terminat... {Caption, Description, InstallDate, Name...}

Looking at the output of this command, we can see that there are several classes and properties.

A closer look at the methods shows that there are six methods in addition to the previously used one. The command also shows the parameters that are used in each method.

(Get-CimClass -ClassName Win32_Process).CimClassMethods
Name                    ReturnType Parameters                                                            Qualifiers
----                    ---------- ----------                                                            ----------
Create                      UInt32 {CommandLine, CurrentDirectory, ProcessStartupInformation, ProcessId} {Constructo...
Terminate                   UInt32 {Reason}                                                              {Destructor...
GetOwner                    UInt32 {Domain, User}                                                        {Implemente...
GetOwnerSid                 UInt32 {Sid}                                                                 {Implemente...
SetPriority                 UInt32 {Priority}                                                            {Implemente...
AttachDebugger              UInt32 {}                                                                    {Implemente...
GetAvailableVirtualSize     UInt32 {AvailableVirtualSize}                                                {Implemente...

There are 45 properties available in this class to query to enumerate additional information about the system.

(Get-CimClass -ClassName Win32_Process).CimClassProperties.Name


Let’s look at a practical example of using these methods and properties together.

$Command = "PowerShell.exe -Command Start-Sleep -Seconds 180"
$Result = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
       CommandLine = $Command


ProcessId ReturnValue PSComputerName
--------- ----------- --------------
    17468           0

This sequence of commands is similar to the ones previously shown highlighting how WMI can be accessed. However, what we can also do is query WMI for the status of the started process and get additional information about it. Instead of using the Invoke-CimMethod command, we will use the Get-CimInstance command to access the WMI properties.

$Info = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = '$($Result.ProcessId)'"

ProcessId Name           HandleCount WorkingSetSize VirtualSize   PSComputerName
--------- ----           ----------- -------------- -----------   --------------
42440     powershell.exe 452         54824960       2199659937792

PowerShell.exe -Command Start-Sleep -Seconds 180

Alternatively, this property query could be restructured to be done in a more SQL like syntax.

$CommandLine = Get-CimInstance -Query "SELECT CommandLine FROM Win32_Process WHERE ProcessId = '$($Result.ProcessId)'" 

PowerShell.exe -Command Start-Sleep -Seconds 180

If WMI is a Web Service how can it be used remotely?

PowerShell, WMIC, and VBScript all have native ability to remotely execute queries and methods. Each approaches it in a different way. One important note is that in general remotely invoking WMI requires admin privileges on the remote system.

VBScript (1996)

With VBscript a connection string has to be built, where the remote system is specified. In this instance we specify an alternative set of credentials with the runas.exe command.

C:\> runas.exe /netonly /user:corp\user cmd.exe
Enter the password for corp\user:
C:\> type wmi.vbs
strComputer = ""
strProcess  = "cmd.exe /c echo 'netspi' > C:\text.txt"

Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!"_
       & "\\" & strComputer & "\root\cimv2:Win32_Process")
Error = objWMI.Create(strProcess, null, null, intProcessID)

Wscript.Echo "Process Id = " & intProcessID
Wscript.Echo "ReturnValue = " & Error

C:> cscript.exe wmi.vbs
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

Process Id = 14040
ReturnValue = 0

wmic.exe (2001)

With wmic.exe a remote system can be targeted by specifying the /node: parameter. Optionally a separate set of credentials can be specified on the command line, if the user doesn’t want to use the current set of credentials. Optionally, runas can replace the command line usage of /user and /password.

wmic.exe /node: /user:corp\user /password:Password123 process call create "cmd.exe /c echo 'netspi' > C:\text.txt"
Executing (Win32_Process)->Create()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
        ProcessId = 33900;
        ReturnValue = 0;

PowerShell Version 1+ (2006)

Beginning with PowerShell Version 1, the Get-WmiObject, Invoke-WmiMethod, and similar commands allowed for a credential object to be built and passed to commandlets as an alternative method of authentication. There are multiple ways to build a credential object, and the method below is one of the possible methods. To specify a remote system, the -ComputerName can be passed to the commandlet.

$Pass = ConvertTo-SecureString "Password123" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential("corp\user", $Pass)
$Command = "powershell.exe -Command Set-Content -Path C:\text.txt -Value netspi";

Invoke-WmiMethod -Class Win32_Process -Name Create -ArguementList $Command -ComputerName -Credential $Credential

__GENUS          : 2
__CLASS          : __PARAMETERS
__RELPATH        :
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ProcessId        : 14612
ReturnValue      : 0
PSComputerName   :

PowerShell Version 3+ (2012)

In PowerShell Version 3, with the introduction of CIM commandlets, the execution of remote WMI we simplified further. CIM introduced the concept of CIM Sessions, which act similarly to PsSessions. These are persistent, and can be used across several WMI queries. Below, we build a credential object as we did before, but it is then used to establish a CIM Session, across which our queries are run.

$Pass = ConvertTo-SecureString "Password123" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential("corp\user", $Pass)
$CimSession = New-CimSession -ComputerName -Credential $Credential
$Command = "powershell.exe -Command Set-Content -Path C:\text.txt -Value netspi";

Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
       CommandLine = $Command
} -CimSession $CimSession

ProcessId ReturnValue PSComputerName
--------- ----------- --------------
    17468           0

If WMI is also like SQL what else can be done with it?

Those familiar with SQL might be wondering if WQL has similar core functionality. SELECT queries are largely treated the same in SQL and WQL, and  JOIN’s are as well. In WQL they are not referred to as a JOIN, but instead as an ASSOCIATOR.

Let’s look at an example of this. WQL has the native functionality to query users and groups, but to get group membership we have to associate the users to the group. We will start off by querying WMI for the members of the local group Administrators.

$local = Get-WmiObject -Class Win32_Group -Filter "Name='Administrators'"
# or Get-WmiObject -Query "SELECT * FROM Win32_Group WHERE Name='Administrators'"

Caption                       Domain         Name           SID
-------                       ------         ----           ---
TestSystem\Administrators     TestSystem     Administrators S-1-5-32-544

We can then take the result of that query and apply an associaters/join query to find who is a member of this group.

Get-WmiObject -Query "ASSOCIATORS OF {$($local.__RELPATH)} WHERE AssocClass=Win32_GroupUser"

AccountType : 512
Caption     : TestSystem\Administrator
Domain      : TestSystem
SID         : S-1-5-21-[REDACTED]-500
FullName    :
Name        : Administrator

AccountType : 512
Caption     : TestSystem\backup
Domain      : TestSystem
SID         : S-1-5-21-[REDACTED]-1047
FullName    :
Name        : backup

That’s cool, but not particularly user friendly. Fortunately, as with most of WMI, commands were simplified with the CIM commands. The same query could be restructured as:

$Group = Get-CimInstance -ClassName Win32_Group -Filter "Name='Administrators'" 
Get-CimAssociatedInstance -Association Win32_GroupUser -InputObject $Group

Name             Caption                      AccountType  SID          Domain
----             -------                      -----------  ---          ------
Administrator    TestSystem\Administrator     512          S-1-5-21-... TestSystem
backup           TestSystem\backup            512          S-1-5-21-... TestSystem

Or even more simply via the pipeline:

Get-CimInstance -ClassName Win32_Group -Filter "Name='Administrators'" | Get-CimAssociatedInstance -Association Win32_GroupUser

Name             Caption                      AccountType  SID          Domain
----             -------                      -----------  ---          ------
Administrator    TestSystem\Administrator     512          S-1-5-21-... TestSystem
backup           TestSystem\backup            512          S-1-5-21-... TestSystem

The same process can be repeated against the domains:

$Domain = Get-WmiObject -Class Win32_Group -Filter "Domain = 'NETSPI' AND Name = 'Domain Admins'"
# or Get-WmiObject -Query "SELECT * FROM Win32_Group WHERE Domain = 'NETSPI' AND Name = 'Domain Admins'"

Get-WmiObject -Query "ASSOCIATORS OF {$($local.__RELPATH)} WHERE AssocClass=Win32_GroupUser"

Get-CimInstance -ClassName Win32_Group -Filter "Domain = 'NETSPI' AND Name='Domain Admins'" | Get-CimAssociatedInstance -Association Win32_GroupUser

Why might you want to use this approach? The net.exe command works well enough. Isn’t it more complicated to get the same information?

Using methods like these, it is possible to completely bypass command line auditing. If run from a remote system, the commands wont register on the targets command line. This can potentially bypass detective solutions that trigger on command line events. We are able to bypass those triggers because we are directly querying directory services. It is also possible to perform more advanced queries. For instance, finding users to target who have overlapping group membership.

WMI Namespaces

In SQL Server different databases can exist on the same system or instance. The analogous item in WMI to a database is a Namespace. Thus far all of the queries have been in the default ROOT/CIMV2 namespace, however, others exist. One such location is the SecurityCenter (XP and Prior) and SecurityCenter2 (Vista+). Using the security center namespace, it is possible to remotely query what security products are registered on the system. The three main categories are as follows:


Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class FirewallProduct -ComputerName -Credential $Credential

Get-WmiObject -Query "SELECT * FROM AntiVirusProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName -Credential $Credential

Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName FirewallProduct -CimSession $CimSession


Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class AntiSpywareProduct -ComputerName -Credential $Credential

Get-WmiObject -Query "SELECT * FROM AntiSpywareProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName -Credential $Credential

Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName AntiSpywareProduct -CimSession $CimSession


Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class AntiVirusProduct -ComputerName -Credential $Credential

Get-WmiObject -Query "SELECT * FROM AntiVirusProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName -Credential $Credential

Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName AntiVirusProduct -CimSession $CimSession

Let’s take a closer look at the AntiVirusProduct Class.

$av = Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName AntiVirusProduct -CimSession $cimsession

displayName              : Windows Defender
instanceGuid             : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe   : %ProgramFiles%\Windows Defender\MSASCui.exe
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState             : 266240
timestamp                : Fri, 10 Feb 2017 00:03:38 GMT
PSComputerName           :

Here it we can see that Windows Defender is installed on the system.

There are other areas in which can be explored. Most roles that I have seen installed on servers install a WMI namespace. Active Directory is no different. On a recent Red Team engagement, we were looking for target systems without triggering an alert by initiating an AXFR. The solution? A WMI query for the A and PTR records stored on the system.

A Records

Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Class MicrosoftDNS_AType -ComputerName -Credential $Credential

Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Query "SELECT * FROM MicrosoftDNS_AType" -ComputerName -Credential $Credential

Get-CimInstance -NameSpace ROOT/MicrosoftDNS -ClassName MicrosoftDNS_AType -CimSession $CimSession

PTR Records

Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Class MicrosoftDNS_PTRType -ComputerName -Credential $Credential

Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Query "SELECT * FROM MicrosoftDNS_PTRType" -ComputerName -Credential $Credential

Get-CimInstance -NameSpace ROOT/MicrosoftDNS -ClassName MicrosoftDNS_PTRType -CimSession $CimSession

Using this command, we were able to enumerate all the systems registered in DNS and find the target we were tasked with finding.

$ARecords = Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Class MicrosoftDNS_PTRType -ComputerName -Credential $Credential

$ARecords | Export-CSV -Path ARecords.CSV

{Get; Set;}

Some WMI classes have properties that can be set without invoking a method. Looking into the AntiVirusProduct further, we can see that there is a product state property.



Converting the Product state to Hex we see that it is set to 060110. Some research will reveal that this indicates that the product is both running a up to day. However, we can remotely change the reported product state to disabled. Your mileage will vary with this one.

$av.productState = 393472



WMI Class Creation

Let’s first look at how to create a customized WMI Class. The process is surprisingly simple and straightforward.

First we create a class object:

$Class = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);

Then we assign it properties:

$Class["__CLASS"] = "MyClass";

$Class.Qualifiers.Add("Static", $true)

$Class.Properties.Add("MyKey", [System.Management.CimType]::String, $false)

$Class.Properties["MyKey"].Qualifiers.Add("Key", $true)

Finally, we push it to WMI:


Path : \\.\root\cimv2:MyClass
RelativePath : MyClass
Server : .
NamespacePath : root\cimv2
ClassName : MyClass
IsClass : True
IsInstance : False
IsSingleton : False

Get-CimClass -ClassName MyClass

 NameSpace: ROOT/cimv2
CimClassName CimClassMethods CimClassProperties
------------ --------------- ------------------
MyClass      {}              {MyKey}

So now we have a class, but there is no data in it. Data in WMI is stored as instances of the class, to place data in it we must start creating instances of it. To this we use the Set-WmiInstance method in PowerShell.

Set-WmiInstance -Class MyClass -Arguments @{MyKey = "MyValue"; }

__GENUS : 2
__CLASS : MyClass
__DYNASTY : MyClass
__RELPATH : MyClass.MyKey="MyValue"
__SERVER : .
__PATH : \\.\ROOT\cimv2:MyClass.MyKey="MyValue"
MyKey : MyValue
PSComputerName : .

In the next post, we are going to look at how several common administrative tasks normal performed over SMB can be accomplished via WMI.