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} [TRUNCATED] 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 Caption Description InstallDate Name Status [Truncated] ThreadCount VirtualSize WindowsVersion WriteOperationCount WriteTransferCount
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 } $Result 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)'" $Info ProcessId Name HandleCount WorkingSetSize VirtualSize PSComputerName --------- ---- ----------- -------------- ----------- -------------- 42440 powershell.exe 452 54824960 2199659937792 $Info.CommandLine 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)'" $CommandLine.CommandLine 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 = "10.1.1.1" 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:10.1.1.1 /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 10.1.1.1 -Credential $Credential __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 2 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ProcessId : 14612 ReturnValue : 0 PSComputerName : 10.1.1.1
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 10.1.1.1 -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 10.1.1.1
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'" $local 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:
FirewallProduct
Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class FirewallProduct -ComputerName 10.1.1.1 -Credential $Credential Get-WmiObject -Query "SELECT * FROM AntiVirusProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName 10.1.1.1 -Credential $Credential Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName FirewallProduct -CimSession $CimSession
AntiSpywareProduct
Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class AntiSpywareProduct -ComputerName 10.1.1.1 -Credential $Credential Get-WmiObject -Query "SELECT * FROM AntiSpywareProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName 10.1.1.1 -Credential $Credential Get-CimInstance -Namespace ROOT/SecurityCenter2 -ClassName AntiSpywareProduct -CimSession $CimSession
AntiVirusProduct
Get-WmiObject -Namespace ROOT/SecurityCenter2 -Class AntiVirusProduct -ComputerName 10.1.1.1 -Credential $Credential Get-WmiObject -Query "SELECT * FROM AntiVirusProduct" -NameSpace ROOT/SecurityCenter2 -ComputerName 10.1.1.1 -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 $av 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 : 10.1.1.1
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 10.1.1.1 -Credential $Credential Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Query "SELECT * FROM MicrosoftDNS_AType" -ComputerName 10.1.1.1 -Credential $Credential Get-CimInstance -NameSpace ROOT/MicrosoftDNS -ClassName MicrosoftDNS_AType -CimSession $CimSession
PTR Records
Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Class MicrosoftDNS_PTRType -ComputerName 10.1.1.1 -Credential $Credential Get-WmiObject -NameSpace ROOT/MicrosoftDNS -Query "SELECT * FROM MicrosoftDNS_PTRType" -ComputerName 10.1.1.1 -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 10.1.1.1 -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.
$av.GetProductState.ToString("X6") 041000
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 $av.productState.ToString("X6") 060100
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:
$Class.Put() 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 __SUPERCLASS : __DYNASTY : MyClass __RELPATH : MyClass.MyKey="MyValue" __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : . __NAMESPACE : ROOT\cimv2 __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.
Explore more blog posts
Practical Methods for Decapping Chips
Discover the intricate process of chip decapping, exposing secrets stored within snuggly layers of industrial epoxy, sleeping in beds of silicon.
Hijacking Azure Machine Learning Notebooks (via Storage Accounts)
Abusing Storage Account Permissions to attack Azure Machine Learning notebooks
Celebrating NetSPI’s Partners of the Year 2024
Congratulations to NetSPI’s 2024 Partner of the Year Recipients Defy Security, VLCM, Softcat, Enduir, Evotek, and AWS