Connecting to WMI … and security

What confused me right from the start was, that there seem to be several ways to connecto to WMI. Every sample script i studied used a slightly different technique.
That’s because the API has so much possibilities and shortcuts to get to the data you want. Typically (i will talk about WSH here only, because i did most of the experiments with scripting) there are two ways to connect – using a SwbemLocator object or through a moniker.

I will discuss the “traditional” way first and then show you several shortcuts using the moniker method. oh yeah – and i will talk about the several ways to adjust security settings.

The Object Way
Normally there a three objects involved when you want to access a wmi object.

SwbemLocator (returns a)
SwbemService (returns a)
SwbemObject

The SwbemLocator object let’s you connect to a WMI Service – local or remote. You can use integrated security, which means that your current logon (domain or local) is used to connect, or you can specify different credentials. The WMI service you connect to, will impersonate the account – in either way. WMI support both Kerberos and NTLM.

BTW – you can only specify security credentials using the locator way of connecting to WMI – if you are using monikers you are tied to integrated security. which is not that bad, because it keeps the hardcoded admin passwords out of those scripts…

First, you create a SwbemLocator object with

Set objLocator = CreateObject(“WbemScripting.SwbemLocator”)

The WMI Locator object has only one Method which is called ConnectServer (and a property called Security_ which i will talk about later).

ConnectServer will ultimately return a SwbemService object which represents the WMI service you connected to.

Calling ConnectServer without parameters will connect you to the local WMI service using integrated security.

Set objSvc = objLocator.ConnectServer()

All other parameters are optional – see the WMI SDK for more info.

Other common ways of using ConnectServer:

Connect to a remote Machine (using the ‘rootcimv2’ namespace and different credentials)
Set objSvc = objLocator.ConnectServer(“Server1”, “rootcimv2”, “administrator”, “standard”)

or connect to a remote machine (using the standard namespace – which is ‘rootcimv2’ – at least in xp/w2k3 and integrated security)
Set objSvc = objLocator.ConnectServer(“Server1”)

The SwbemService object that gets returned has several methods to retrieve the WMI objects that are managed by the WMI Service on the specific machine. The most common used methods are InstancesOf, which returns all instances of a WMI class, Get which let’s you query key properties (see my previous post about keys) and ExecQuery – with which you can specifiy a WQL statement (very similar to SQL) to narrow down what objects you want to retrieve (and also to query key and non-key properties).

Set objSet = objSvc.InstancesOf(“win32_NetworkAdapterConfiguration”)
or
Set objSet = objSvc.ExecQuery(“select * from Win32_NetworkAdapterConfiguration where IPEnabled=’true'”)

In both cases you get a collection of SwbemObjects which you can iterate over.

For Each objNic In objSet
 Wscript.Echo(objNic.Caption)
Next

The Get method only returns a single object – that’s because Get queries the key property of an object – which is unique

Set objNic = objSvc.Get(“win32_NetworkAdapterConfiguration.Index=1”)
 

The Moniker Way

You can also connect to WMI using Monikers. This involves the GetObject statement in WSH. Depending on how much information you provide in the moniker, you will get a service or an object in return.

Service (local – default Namespace)
Set ObjSvc = GetObject(“WinMgmgts:”)

Service (local – ‘rootcimv2’ Namespace)
Set ObjSvc = GetObject(“WinMgmgts:root/cimv2”)

Service (remote – default Namespace)
Set ObjSvc = GetObject(“WinMgmgts://Server1”)

Service (remote – ‘rootcimv2’ Namespace)
Set ObjSvc = GetObject(“WinMgmgts://Server1/root/cimv2”)

Object (local – return drive c:)
Set ObjSet = GetObject(“WinMgmts:win32_LogicalDisk.DeviceId=’C:'”)

or a combination of both (gets all processes)
Set objProcs = GetObject(“WinMgmts:”).InstancesOf(“win32_Process”)
respectively (gets all services which are configured to auto start but are not running)
Set objProcs = GetObject(“WinMgmts:”).ExecQuery(“select * from win32_service where StartMode = ‘Auto’ and Started = ‘false'”

So you can see that both methods are equivalent in functionality and can be mixed. the only big difference between both approaches is when it comes to security.

Security

As i mentioned before, you can specify different credentials while connecting with a SwbemLocator object –

you can’t do that with a moniker.

All the other settings apply to both ways.

Authenication Level
WMI is backed up by DCOM – so you can set the usual auth levels you are already accustomed to. These range from Connect (which authenticates the first connect – but not the subsequent calls) to PacketPrivacy (which encrypts and integrity protects every single network packet). Needless to say that i recommend PacketPrivacy

because you don’t want that someone else can read or tamper with you management data. especially when you send credentials (and in that case admin passwords) over the wire.

objLocator.Security_.AuthenticationLevel = wbemAuthenticationLevelPkt
or
Set objSvc = GetObject(“WinMgmts:{impersonationLevel=impersonate,”& _                “authenticationLevel=pktPrivacy}!root/cimv2”)

Rant #1

The authentication level is set to packet by default (win xp box). That means that every single packet is _just_ authenticated. no encryption, no integrity checks. and even more odd – this only applies to remote connections – local connection have a level of PacketPrivacy by default…so a wire is more trustworthy than my motherboard?? Watch out for Rant #2 – which just goes in the opposite direction.

Impersonation Level
The default impersonation level is set to Impersonate – which is fine unless your script doesn’t do two networks hops. That could happen if you connect to a remote WMI service which in turn call out to a remote resource, e.g. another file system. Think about the following scenario : You want to install software on a remote machine and this machine has to pull the msi file from a share….

You have two choices here –

If you choose a ImpersonationLevel of ‘Impersonate’ the remote WMI service will not have the appropriate token to use your account to call out to the network – it will use the machine account (e.g. SERVER1$).

If you choose a ImpersonationLevel of ‘Delegate’ your credentials will be delegated to the back end file server to access the file. This has some implications which are out of scope for this entry, e.g. enable delegation for the server in Active Directory.

In both cases make sure the right account has read rights for the remote resource, e.g. the msi file.

objLocator.Security_.ImpersonationLevel = wbemImpersonationLevelImpersonate
Set Service = GetObject(“winmgmts:{impersonationLevel=impersonate}”)
Set Service = GetObject(“WinMgmts:{impersonationLevel=delegate}!//Instructor/root/cimv2”)

Privileges
An interesting concept of WMI is, that you have to specifiy which privileges are needed to carry out the requested action. Let me give you an example.

When you want to shut down your machine via WMI – this wouldn’t work:

Set objSvc = GetObject(“WinMgmts:”)
Set objSet = objSvc.ExecQuery(“select * from Win32_OperatingSystem where Primary = ‘true'”)

For Each opSys In objSet
 WScript.Echo opSys.Caption
 opSys.Shutdown()
Next

The problem with the code is, that you have to specify that you want the Shutdown privilege to be enabled

(you can only enable privileges which are granted to you already – of course).
This would work instead.

Set objSvc = GetObject(“WinMgmts:{(Shutdown)}”)
or
Service.Security_.Privileges.AddAsString “SeShutdownPrivilege”, True

You can even refuse privileges – watch this:

Set objSvc = GetObject(“WinMgmts:{(!Shutdown)}”)

This reminds me a little bit of .net code access security – at least from a user perspective. of course – i know, if you are writing low level c code you can achieve the same thing by disabling the privileges in your token – but hey this is vbscript.

This can shield your code from misuse, e.g. if you are writing a management app you can selectively enable and disable privileges that you don’t need. Especially the string-based nature of the WMI interface may open up potential security holes like we see in SQL and HTML today. So refuse those privileges ! :)

So – a complete moniker could look like this (see also):

WinMgmts:{ImpersonationLevel=Impersonate, AuthenticationLevel=PktPrivacy,(Shutdown,
!Debug)}!//w2k3netdev/root/cimv2

Rant #2 – A Big One

This nice ‘feature’ of enabling and disabling privileges only works when you connect to your local machine.

Citing the docs :

“For remote connections, this is not an issue. In other words, DCOM ensures that if the account being used has the required user right on the target computer, the script does not need to also set this privilege.
Setting or revoking the privilege has no effect on the account’s user right”

So i guess the whole privileges thing is more a limitation than a feature. sad but true.

 

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a comment