Setting WMI ACLs via SetSecurityDescriptor
Asked Answered
S

2

8

I can't seem to be able to set WMI ACLs via Powershell. An invocation of

Invoke-WmiMethod -Name "SetSecurityDescriptor" -Path "__systemsecurity=@" -ArgumentList $acl.psobject.immediateBaseObject

returns this exception:

Invoke-WmiMethod : Invalid method Parameter(s) 
At line:1 char:17.
+ Invoke-WmiMethod <<<<  -Name "SetSecurityDescriptor" -Path "__systemsecurity=@" -ArgumentList $acl.psobject.immediateBaseObject
    + CategoryInfo          : InvalidOperation: (:) [Invoke-WmiMethod], ManagementException
    + FullyQualifiedErrorId : InvokeWMIManagementException,Microsoft.PowerShell.Commands.InvokeWmiMethod

SetSecurityDescriptor takes exactly one parameter of the __SecurityDescriptor type and the $acl object itself I am using in -Arguments seems alright:

PS C:\Windows\system32> $acl | gm


   TypeName: System.Management.ManagementBaseObject#\__SecurityDescriptor

Name             MemberType Definition
----             ---------- ----------
ControlFlags     Property   System.UInt32 ControlFlags {get;set;}
DACL             Property   System.Management.ManagementObject#__ACE[] DACL ...
Group            Property   System.Management.ManagementObject#__ACE Group {...
Owner            Property   System.Management.ManagementObject#__ACE Owner {...
SACL             Property   System.Management.ManagementObject#__ACE[] SACL ...
TIME_CREATED     Property   System.UInt64 TIME_CREATED {get;set;}
__CLASS          Property   System.String __CLASS {get;set;}
__DERIVATION     Property   System.String[] __DERIVATION {get;set;}
__DYNASTY        Property   System.String __DYNASTY {get;set;}
__GENUS          Property   System.Int32 __GENUS {get;set;}
__NAMESPACE      Property   System.String __NAMESPACE {get;set;}
__PATH           Property   System.String __PATH {get;set;}
__PROPERTY_COUNT Property   System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH        Property   System.String __RELPATH {get;set;}
__SERVER         Property   System.String __SERVER {get;set;}
__SUPERCLASS     Property   System.String __SUPERCLASS {get;set;}

From what I can get off the docs, I am invoking the Parameter Set: path overload, so the parameter set seems not to be missing required arguments.

I am basically ripping the code off this MSDN blog post on the very same topic and while GetSecurityDescriptor using a similar invocation gives the desired results:

$output = Invoke-WmiMethod -Path "__systemsecurity=@" -Name GetSecurityDescriptor

the SetSecurityDescriptor keeps throwing exceptions on me. How do I get it working?

The code in context, for reference:

# connect to SystemSecurity
$invokeparams = @{Path="__systemsecurity=@"}

# get SecurityDescriptor with ACL
$output = Invoke-WmiMethod @invokeparams -Name GetSecurityDescriptor
if ($output.ReturnValue -ne 0) {
     throw "GetSecurityDescriptor failed: $($output.ReturnValue)"
    }

# ACL object reference is in the .Descriptor property 
$acl = $output.Descriptor

$ace = (New-Object System.Management.ManagementClass("win32_Ace")).CreateInstance()

# AccessMask is WBEM_ENABLE, $WBEM_METHOD_EXECUTE, $WBEM_WRITE_PROVIDER, $WBEM_REMOTE_ACCESS
$ace.AccessMask = 1 + 2 + 0x10 + 0x20
# AceFlags are  $OBJECT_INHERIT_ACE_FLAG, $CONTAINER_INHERIT_ACE_FLAG
$ace.AceFlags =  0x01 + 0x2
# AceType is ACCESS_ALLOWED_ACE_TYPE
$ace.AceType = 0x1 

# get user SID
$getparams = @{Class="Win32_Account";Filter="Domain='MYDOMAIN' and Name='SERVER$'"}
$win32account = Get-WmiObject @getparams
# and build a new Trustee object
$trustee = (New-Object System.Management.ManagementClass("win32_Trustee")).CreateInstance()
$trustee.SidString = $win32account.Sid
$ace.Trustee = $trustee    

# Add ACE to ACL
$acl.DACL += $ace.psobject.immediateBaseObject

# apply new ACL
$setparams = @{Name="SetSecurityDescriptor";ArgumentList=$acl.psobject.immediateBaseObject} + $invokeParams
$output = Invoke-WmiMethod @setparams
if ($output.ReturnValue -ne 0) {
        throw "SetSecurityDescriptor failed: $($output.ReturnValue)"
    }

I also already have tried playing with the .AceFlags property as suggested in comments to the aforementioned blog post by Steve Lee - to no avail.

Sourwood answered 10/1, 2014 at 10:23 Comment(1)
Logged in to SO for the first time in a long time to give you my upvote. Thanks for this.Visual
G
2

In the article you refer to the call is different and those differences could well be important - the params are a single hashtable built up to include all the params as name/value pairs:

$invokeparams = @{Namespace=$namespace;Path="__systemsecurity=@"}

$setparams = @{Name="SetSecurityDescriptor";ArgumentList=$acl.psobject.immediateBaseObject} + $invokeParams

$output = Invoke-WmiMethod @setparams
Gather answered 15/1, 2014 at 15:32 Comment(4)
Thanks for your answer. For the Invoke-WmiMethod cmdlet it seems to make no difference if the function arguments are supplied as a hashtable. Wrapping the arguments in a hashtable results in the same exception being thrown.Sourwood
Can you confirm OS level you are running on, and if running this against a remote computer, what the OS is there too? Maybe share the full code? So far I can't replicate the error you are seeing.Gather
Graham, thanks for taking the time. I have posted the code to my question. And further debugging indicates that the Get-WmiObject Win32_Account call, which is supposed to return the account object with the account's SID, is returning a null object. I do not work with PowerShell all that much and tend to forget setting the strict-mode to on, but it appears I am going to learn my lesson over time.Sourwood
I got the same with a modified version of that call that should work in my environment, but the following format works for me: $win32account = Get-WmiObject Win32_Account -filter "Domain='MYDOMAIN' and Name='$SERVER'". I assume you also meant $SERVER and not SERVER$?Gather
H
1

I got this to work by changing the this line

# AceFlags are  $OBJECT_INHERIT_ACE_FLAG, $CONTAINER_INHERIT_ACE_FLAG
$ace.AceFlags =  0x01 + 0x2

to

# AceFlags are  $CONTAINER_INHERIT_ACE_FLAG
$ace.AceFlags = 0x2

I wish I could tell you why this change is necessary, but I don't really know. I also changed the invokeparams to equal @{Path="__systemsecurity=@"; Namespace="root"} so that the script sets the permissions at the root level instead of the cimv2 level. The permission seems to be inherited as expected.

Hymenopteran answered 23/8, 2016 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.