Skip to content

Instantly share code, notes, and snippets.

@realslacker
Created April 16, 2019 20:01
Show Gist options
  • Save realslacker/d1b5463c2db6689bd7e458d3babdc048 to your computer and use it in GitHub Desktop.
Save realslacker/d1b5463c2db6689bd7e458d3babdc048 to your computer and use it in GitHub Desktop.
A module with expanded functionality base on original work by Steve Lee and Graeme Bray. See Set-WMINameSpaceSecurity.ps1 for original.
<#
Disclaimer:
This module is provided AS IS without warranty of any kind. This work is based off work by
other talented individuals, and should not be considered an original work.
Modified into module by Shannon Graybrook
Modified by Graeme Bray
Original Content by Steve Lee
#>
$WBEM_ENABLE = 0x00001
$WBEM_METHOD_EXECUTE = 0x00002
$WBEM_FULL_WRITE_REP = 0x00004
$WBEM_PARTIAL_WRITE_REP = 0x00008
$WBEM_WRITE_PROVIDER = 0x00010
$WBEM_REMOTE_ACCESS = 0x00020
$WBEM_RIGHT_SUBSCRIBE = 0x00040
$WBEM_RIGHT_PUBLISH = 0x00080
$READ_CONTROL = 0x20000
$WRITE_DAC = 0x40000
$OBJECT_INHERIT_ACE_FLAG = 0x00001
$CONTAINER_INHERIT_ACE_FLAG = 0x00002
$ACCESS_ALLOWED_ACE_TYPE = 0x00000
$ACCESS_DENIED_ACE_TYPE = 0x00001
<#
.SYNOPSIS
Converts a permission set to an access mask
.PARAMETER Permission
The permissions to include in the access mask
#>
function Get-AccessMaskFromPermission {
param(
[ValidateSet('Enable','MethodExecute','FullWrite','PartialWrite','ProviderWrite','RemoteAccess','ReadSecurity','WriteSecurity')]
[string[]]
$Permission
)
$PermissionsTable = @{
Enable = $WBEM_ENABLE
MethodExecute = $WBEM_METHOD_EXECUTE
FullWrite = $WBEM_FULL_WRITE_REP
PartialWrite = $WBEM_PARTIAL_WRITE_REP
ProviderWrite = $WBEM_WRITE_PROVIDER
RemoteAccess = $WBEM_REMOTE_ACCESS
ReadSecurity = $READ_CONTROL
WriteSecurity = $WRITE_DAC
}
$AccessMask = 0
foreach ($PermissionItem in $Permission) {
if ( $PermissionsTable.Keys -notcontains $PermissionItem ) {
throw ( "Unknown permission: {0}`nValid permissions: {1}" -f $PermissionItem, ( $PermissionsTable.Keys -join ', ' ) )
}
$AccessMask += $PermissionsTable.$PermissionItem
}
$AccessMask
}
<#
.SYNOPSIS
Converts an access mask back into a permission set
.PARAMETER AccessMask
The access mask to convert to permissions
#>
function Get-PermissionFromAccessMask {
param(
[int]
$AccessMask
)
$PermissionsTable = @{
Enable = $WBEM_ENABLE
MethodExecute = $WBEM_METHOD_EXECUTE
FullWrite = $WBEM_FULL_WRITE_REP
PartialWrite = $WBEM_PARTIAL_WRITE_REP
ProviderWrite = $WBEM_WRITE_PROVIDER
RemoteAccess = $WBEM_REMOTE_ACCESS
ReadSecurity = $READ_CONTROL
WriteSecurity = $WRITE_DAC
}
foreach ( $Key in $PermissionsTable.Keys ) {
if ( $AccessMask -band $PermissionsTable[$Key] ) { $Key }
}
}
<#
.SYNOPSIS
Add security access on WMI name space.
.PARAMETER Namespace
The WMI namespace
.PARAMETER Account
The account to add. Should be DOMAIN\user format, but also accepts other standard formats.
.PARAMETER Permissions
The permissions to set.
.EXAMPLE
Add-WmiNamespaceAccessRule -Namespace root/CIMv2 -Account "CONTOSO\ServiceAccount" -Permissions Enable, RemoteAccess -ComputerName REMOTE_DC01 -Credential (Get-Credential)
.EXAMPLE
Add-WmiNamespaceAccessRule root/CIMv2 ServiceAccount Enable,RemoteAccess
#>
function Add-WmiNameSpaceAccessRule {
param(
[Parameter(Mandatory=$true,Position=0)]
[string]
$Namespace,
[Parameter(Mandatory=$true,Position=1)]
[string]
$Account,
[Parameter(Mandatory=$true, Position=2)]
[ValidateSet('Enable','MethodExecute','FullWrite','PartialWrite','ProviderWrite','RemoteAccess','ReadSecurity','WriteSecurity')]
[string[]]
$Permissions,
[switch]
$AllowInherit,
[switch]
$Deny,
[string]
$ComputerName,
[pscredential]
$Credential
)
process {
$ErrorActionPreference = "Stop"
$CredentialSplat = @{}
if ( $Credential ) { $CredentialSplat.Credential = $Credential }
$ComputerNameSplat = @{}
if ( $ComputerName ) { $ComputerNameSplat.ComputerName = $ComputerName }
$InvokeParams = @{
Namespace = $Namespace
Path = "__systemsecurity=@"
}
$Output = Invoke-WmiMethod @InvokeParams @ComputerNameSplat @CredentialSplat -Name GetSecurityDescriptor
if ( $Output.ReturnValue -ne 0 ) {
throw ( 'GetSecurityDescriptor failed: {0}' -f $Output.ReturnValue )
}
$Acl = $Output.Descriptor
$ComputerName = (Get-WmiObject @ComputerNameSplat @CredentialSplat Win32_ComputerSystem).Name
if ( $Account.Contains('@') ) {
$AccountName, $Domain = $Account.Split('@')
} elseif ( $Account.Contains('\') ) {
$Domain, $AccountName = $Account.Split('\')
} else {
$Domain = $ComputerName
$AccountName = $Account
}
if ( $Domain -eq '.' -or $Domain -eq 'BUILTIN' ) {
$Domain = $ComputerName
}
if ( $Domain.Contains('.') ) {
Write-Warning 'Domain provided appears to be a DNS domain, attempting to resolve NETBIOS domain name. This can cause a delay in processing, in the future provide the NETBIOS domain for faster processing.'
$Domain = Get-WmiObject -Class win32_ntdomain |
Where-Object { $_.DomainName -eq $Domain -or $_.DnsForestName -eq $Domain } |
Select-Object -ExpandProperty DomainName
}
$GetAccountParams = @{
Class = 'Win32_Account'
Filter = 'Domain="{0}" and Name="{1}"' -f $Domain, $AccountName
}
if ( -not( $Win32Account = Get-WmiObject @GetAccountParams ) ) {
throw ( 'Account was not found: {0}' -f $Account )
}
$AccessMask = Get-AccessMaskFromPermission -Permission $Permissions
$Ace = (New-Object System.Management.ManagementClass('Win32_Ace')).CreateInstance()
$Ace.AccessMask = $AccessMask
$Ace.AceFlags = ( 0, ( $OBJECT_INHERIT_ACE_FLAG + $CONTAINER_INHERIT_ACE_FLAG ) )[ $AllowInherit.IsPresent ]
$Trustee = (New-Object System.Management.ManagementClass('Win32_Trustee')).CreateInstance()
$Trustee.SidString = $Win32Account.Sid
$Ace.Trustee = $Trustee
$Ace.AceType = ( $ACCESS_ALLOWED_ACE_TYPE, $ACCESS_DENIED_ACE_TYPE )[ $Deny.IsPresent ]
$Acl.DACL += $Ace.psobject.immediateBaseObject
$SetParams = @{
Name = 'SetSecurityDescriptor'
ArgumentList = $Acl.psobject.immediateBaseObject
}
$Output = Invoke-WmiMethod @SetParams @InvokeParams @ComputerNameSplat @CredentialSplat
if ( $Output.ReturnValue -ne 0 ) {
throw ( 'SetSecurityDescriptor failed: {0}' -f $Output.ReturnValue )
}
}
}
<#
.SYNOPSIS
Remove security access on WMI name space.
.PARAMETER Namespace
The WMI namespace
.PARAMETER Account
The account to add. Should be DOMAIN\user format, but also accepts other standard formats.
.EXAMPLE
Remove-WmiNamespaceAccessRule -Namespace root/CIMv2 -Account "CONTOSO\ServiceAccount" -ComputerName REMOTE_DC01 -Credential (Get-Credential)
.EXAMPLE
Remove-WmiNamespaceAccessRule root/CIMv2 ServiceAccount
#>
function Remove-WmiNameSpaceAccessRule {
param(
[Parameter(Mandatory=$true,Position=0)]
[string]
$Namespace,
[Parameter(Mandatory=$true,Position=1)]
[string]
$Account,
[string]
$ComputerName,
[pscredential]
$Credential
)
process {
$ErrorActionPreference = "Stop"
$CredentialSplat = @{}
if ( $Credential ) { $CredentialSplat.Credential = $Credential }
$ComputerNameSplat = @{}
if ( $ComputerName ) { $ComputerNameSplat.ComputerName = $ComputerName }
$InvokeParams = @{
Namespace = $Namespace
Path = "__systemsecurity=@"
}
$Output = Invoke-WmiMethod @InvokeParams @ComputerNameSplat @CredentialSplat -Name GetSecurityDescriptor
if ( $Output.ReturnValue -ne 0 ) {
throw ( 'GetSecurityDescriptor failed: {0}' -f $Output.ReturnValue )
}
$Acl = $Output.Descriptor
$ComputerName = (Get-WmiObject @ComputerNameSplat @CredentialSplat Win32_ComputerSystem).Name
if ( $Account.Contains('@') ) {
$AccountName, $Domain = $Account.Split('@')
} elseif ( $Account.Contains('\') ) {
$Domain, $AccountName = $Account.Split('\')
} else {
$Domain = $ComputerName
$AccountName = $Account
}
if ( $Domain -eq '.' -or $Domain -eq 'BUILTIN' ) {
$Domain = $ComputerName
}
if ( $Domain.Contains('.') ) {
Write-Warning 'Domain provided appears to be a DNS domain, attempting to resolve NETBIOS domain name. This can cause a delay in processing, in the future provide the NETBIOS domain for faster processing.'
$Domain = Get-WmiObject -Class win32_ntdomain |
Where-Object { $_.DomainName -eq $Domain -or $_.DnsForestName -eq $Domain } |
Select-Object -ExpandProperty DomainName
}
$GetAccountParams = @{
Class = 'Win32_Account'
Filter = 'Domain="{0}" and Name="{1}"' -f $Domain, $AccountName
}
if ( -not( $Win32Account = Get-WmiObject @GetAccountParams ) ) {
throw ( 'Account was not found: {0}' -f $Account )
}
[System.Management.ManagementBaseObject[]]$NewDACL = @()
foreach ( $Ace in $Acl.DACL ) {
if ( $Ace.Trustee.SidString -ne $Win32Account.Sid ) {
$NewDACL += $Ace.psobject.immediateBaseObject
}
}
$Acl.DACL = $NewDACL.psobject.immediateBaseObject
$SetParams = @{
Name = 'SetSecurityDescriptor'
ArgumentList = $Acl.psobject.immediateBaseObject
}
$Output = Invoke-WmiMethod @SetParams @InvokeParams @ComputerNameSplat @CredentialSplat
if ( $Output.ReturnValue -ne 0 ) {
throw ( 'SetSecurityDescriptor failed: {0}' -f $Output.ReturnValue )
}
}
}
<#
.SYNOPSIS
Get security access on WMI name space.
.PARAMETER Namespace
The WMI namespace
.PARAMETER Account
The account to retrieve access for. Should be DOMAIN\user format, but also accepts other standard formats.
.EXAMPLE
Get-WmiNamespaceAccessRule -Namespace root/CIMv2 -Account "CONTOSO\ServiceAccount" -ComputerName REMOTE_DC01 -Credential (Get-Credential)
.EXAMPLE
Get-WmiNamespaceAccessRule root/CIMv2 ServiceAccount
#>
function Get-WmiNameSpaceAccessRule {
param(
[Parameter(Mandatory=$true,Position=0)]
[string]
$Namespace,
[Parameter(Mandatory=$true,Position=1)]
[string]
$Account,
[string]
$ComputerName,
[pscredential]
$Credential
)
process {
$ErrorActionPreference = "Stop"
$CredentialSplat = @{}
if ( $Credential ) { $CredentialSplat.Credential = $Credential }
$ComputerNameSplat = @{}
if ( $ComputerName ) { $ComputerNameSplat.ComputerName = $ComputerName }
$InvokeParams = @{
Namespace = $Namespace
Path = "__systemsecurity=@"
}
$Output = Invoke-WmiMethod @InvokeParams @ComputerNameSplat @CredentialSplat -Name GetSecurityDescriptor
if ( $Output.ReturnValue -ne 0 ) {
throw ( 'GetSecurityDescriptor failed: {0}' -f $Output.ReturnValue )
}
$Acl = $Output.Descriptor
$ComputerName = (Get-WmiObject @ComputerNameSplat @CredentialSplat Win32_ComputerSystem).Name
if ( $Account.Contains('@') ) {
$AccountName, $Domain = $Account.Split('@')
} elseif ( $Account.Contains('\') ) {
$Domain, $AccountName = $Account.Split('\')
} else {
$Domain = $ComputerName
$AccountName = $Account
}
if ( $Domain -eq '.' -or $Domain -eq 'BUILTIN' ) {
$Domain = $ComputerName
}
if ( $Domain.Contains('.') ) {
Write-Warning 'Domain provided appears to be a DNS domain, attempting to resolve NETBIOS domain name. This can cause a delay in processing, in the future provide the NETBIOS domain for faster processing.'
$Domain = Get-WmiObject -Class win32_ntdomain |
Where-Object { $_.DomainName -eq $Domain -or $_.DnsForestName -eq $Domain } |
Select-Object -ExpandProperty DomainName
}
$GetAccountParams = @{
Class = 'Win32_Account'
Filter = 'Domain="{0}" and Name="{1}"' -f $Domain, $AccountName
}
if ( -not( $Win32Account = Get-WmiObject @GetAccountParams ) ) {
throw ( 'Account was not found: {0}' -f $Account )
}
$Ace = $Acl.DACL |
Where-Object { $_.Trustee.SidString -eq $Win32Account.Sid } |
ForEach-Object { $_.psobject.immediateBaseObject }
$ReturnObj = 0 | Select-Object Account, SID, Permissions, AllowInherit, Deny
$ReturnObj.Account = '{0}\{1}' -f $Ace.Trustee.Domain, $Ace.Trustee.Name
$ReturnObj.SID = $Ace.Trustee.SIDString
$ReturnObj.Permissions = Get-PermissionFromAccessMask -AccessMask $Ace.AccessMask
$ReturnObj.AllowInherit = $Ace.AceFlags -eq ( $OBJECT_INHERIT_ACE_FLAG + $CONTAINER_INHERIT_ACE_FLAG )
$ReturnObj.Deny = [bool]$Ace.AceType
$ReturnObj
}
}
Export-ModuleMember -Function 'Add-WmiNameSpaceAccessRule', 'Remove-WmiNameSpaceAccessRule', 'Get-WmiNameSpaceAccessRule'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment