Managing Privileges using PoshPrivilege

A recent project of mine has been to write a module to manage privileges on a local system. What I came up is a module called PoshPrivilege that allows you to not only look at what user rights are available on a local or remote system, but also provide the ability to Add, Remove, Enable and Disable the privileges as well.

If you are running PowerShell V5, you can download this module from the PowerShell Gallery:

Install-Module –Name PoshPrivilege

Otherwise, check out my GitHub page where I am maintaining this project:

https://github.com/proxb/PoshPrivilege

I won’t spend time talking about how I wrote this module and my reasons behind it. What I will say is that instead of writing out C# code and then using Add-Type to compile it, I went with the Reflection approach of building out everything from the pinvoke signatures for methods to the Structs and even the Enums.

Let’s get started by looking at what is available in this module. The first function that is available is Get-Privilege and it comes with a few parameters. This function’s purpose is to let you view what privileges are currently available on the system (local or remote) as well as what is currently applied to your current process token.

image

A quick run through of using this function with various parameters:

Get-Privilege

image

Get-Privilege –Privilege SeAuditPrivilege, SeServiceLogonRight | Format-List

image

Get-Privilege –CurrentUser

image

If this one looks familiar, then it is probably likely that you have used the following command:

whoami /priv /fo csv | ConvertFrom-CSV

image

I opted for boolean values instead to determine the state for easier filtering if needed.

Up next are the Enable/Disable-Privilege functions. These work to Enable or Disable the privileges that are currently available on your local system to your process token. This means that if something like SeDebugPrivilege isn’t available on your system (such as being removed via Group Policy), then you cannot use Enable-Privilege to add your process token to this privilege. As in the previous image where we can see what is enabled and disabled, these are the only privileges that are available for me to work with.

To show this point, I am going to enable both SeSecurityPrivilege and SeDebugPrivilege so you can see that while the first privilege will show as Enabled, the other will not appear as it has not been made available.

Enable-Privilege -Privilege SeSecurityPrivilege,SeDebugPrivilege

SNAGHTMLd2422

As you can see from the picture, SeSecurityPrivilege has been enabled as expected, but SeDebugPrivilege is nowhere to be found. If we want SeDebugPrivilege, we will need to go about this another way which will be shown shortly.

Disabling a privilege can be done using Disable-Privilege as shown in the example below.

Disable-Privilege –Privilege SeSecurityPrivilege

SNAGHTMLfdf1c

Now that I have covered Enabling and Disabling of the privileges and their limitations, I will move onto the Add/Remove-Privilege functions which allow you to add a privilege for a user or group or remove them on a local system. Note that this only works up until it gets reverted if set by group policy. This will also note show up if you look at the privileges available on your current process token (you will log off and log back in to see it).

Remember that I do not have SeDebugPrivilege available to use? Well, now we can add it to my own account using Add-Privilege.

Add-Privilege –Privilege SeDebugPrivilege –Accountname boe-pc\proxb

image

We can see it is now available, but as I mentioned before, it doesn’t show up in my current process. A logoff and login now shows that it is not only available, but already enabled.

image

With this now enabled, we could disable it as well if needed using Disable-Privilege. I added my account for show, but we can also add groups this was as well.

As with Adding a privilege, we can remove privileges as well using Remove-Privilege.

Remove-Privilege –Privilege SeDebugPrivilege –AccountName boe-pc\proxb

image

As with Add-Privilege, you will need to log off and log back in to see the change take effect on your account.

Again, you can install this module using Install-Module if running PowerShell V5 and this project is out on GitHub to download (and contribute to as well). Enjoy!

Posted in powershell | Tagged , , , , , | 4 Comments

Another Way to Show Bitwise Operation Results

This is something that came to me as I was working to do some bitwise operations for a project that had a lot of pInvoke stuff and required that I do some shifting of bits. Being that I wasn’t all that familiar with this (I’m more familiar with BOR, BAND and XOR), I wanted to learn more about the before and after of the binary representation of the data.

What is a bitwise operation you ask? Well, it is

a bitwise operation operates on one or more bit patterns or binary numerals at the level of their individual bits. It is a fast, primitive action directly supported by the processor, and is used to manipulate values for comparisons and calculations. –From Wikipedia Article on Bitwise Operations

I decided that it would be nice to have a function that would allow you to provide the parameters needed for a bitwise operation, but rather than just outputting a value as a result of the operation, it would instead show you more information about the results such as showing the values in binary and hex form. But that wasn’t all I wanted. I saw the about_Comparison_Operators help file and liked the examples that they showed when doing an operation.

image

Putting this all together led me to a function I wrote called Invoke-BitwiseOperation which accepts a –First and –Second parameter which is for the two values, a –BitwiseOperator parameter that accepts the following values: BAND,BOR,BXOR,BNOT,SHL,SHR and finally there is the –Graphical parameter which gives you the output like you see in the help files.

I start out by using a helper function called ConvertToBinary which takes an integer, converts it to a binary form and then uses a RegEx pattern to split out the binary data into chunks of 8.

Function ConvertToBinary {
    Param(
        [parameter(ValueFromPipeline=$True)]
        $Item
    )
    Process {
        $String = [Convert]::ToString($Item,2)
        $Reverse = -join (-join $string[$String.length..0] -split '(?<=\G.{8})').PadRight(8,'0')
        -join $Reverse[$Reverse.length..0] -split '(?<=\G.{8})'
    }
}

(8956 | ConvertToBinary) -join ' '

bitwise_2

I opted to not join the output prior so I could do some more stuff with the collection of values later on in my code.

If ($BitWiseOperator -eq 'BNOT') {
    $Result = [Math]::Abs((Invoke-Expression "-$($BitWiseOperator) $($First)"))
} Else {
    If ($PSBoundParameters.ContainsKey('First') -AND $PSBoundParameters.ContainsKey('Second')){
        $Result = Invoke-Expression "$($First) -$($BitWiseOperator) $($Second)"
    } Else {
        Write-Warning "You must specify both a First and Second parameter!"
        BREAK
    }
}
$First_Bin = $First | ConvertToBinary
$First_Hex = "0x{0:x}" -f $First
If ($PSBoundParameters.ContainsKey('Second') -AND $BitWiseOperator -notmatch 'SHL|SHR') {
    $Second_Bin = $Second | ConvertToBinary
    $Second_Hex = "0x{0:x}" -f $Second
}
$Result_Bin = $Result | ConvertToBinary   
$Result_Hex = "0x{0:x}" -f $Result

This portion looks at what kind of Bitwise operator that I specified and takes an action based on that, such as when using BNOT. I also perform the conversion of the values to binary and also convert the data to hex for later use.

If ($PSBoundParameters.ContainsKey('Graphical')) {
    $Padding = '00000000'
    $FirstList = New-Object System.Collections.ArrayList
    $SecondList = New-Object System.Collections.ArrayList
    $ResultList = New-Object System.Collections.ArrayList

    $FirstHex = "0x{0:x}" -f $First
    $SecondHex = "0x{0:x}" -f $Second
    $ResultHex = "0x{0:x}" -f $Result
 
    $First_Bin | ForEach {
        [void]$FirstList.Add($_)
    }
    $Second_Bin | ForEach {
        [void]$SecondList.Add($_)
    }
    $Result_Bin | ForEach {
        [void]$ResultList.Add($_)
    }
   


    $Count = $FirstList, $SecondList, $ResultList | ForEach {
        [pscustomobject]@{
            Count = $_.count
        }
    } | Sort Count -Descending | Select -First 1 -ExpandProperty Count
 
    $FirstList, $SecondList, $ResultList | ForEach {
        $ToPad = $Count - $_.Count
        If ($ToPad -gt 0) {                
            For ($i=1; $i -le $ToPad; $i++) {
                $_.Insert(0,$Padding)
            }
        }
    }
 
    $Line = '-' * (($ResultList -join ' ').Length)
 
 
    "$($FirstList -join ' ')  ($First, $($FirstHex)"
    If ($BitWiseOperator -notmatch 'BNOT|SHL|SHR') {
        "$($SecondList -join ' ')  ($Second, $($SecondHex)"
    }
    "$line  $BitWiseOperator"
    "$($ResultList -join ' ')  ($result, $($ResultHex)"
 
} Else {
    [pscustomobject]@{
        First = $First
        First_Bin = $First_Bin
        First_Hex = $First_Hex
        BitWiseOperator = $BitWiseOperator
        Second = $Second
        Second_Bin = $Second_Bin
        Second_Hex = $Second_Hex
        Result = $Result
        Result_Bin = $Result_Bin
        Result_Hex = $Result_Hex
    }
}

The final portion of the script takes a turn based on if the –Graphical parameter is used or not. If used, some more data processing occurs so it can display the data like what you saw in the help file. Otherwise, it just outputs an object with the converted values. Some examples are shown below.

Invoke-BitWiseOperation -First 32 -Second 16 -BitWiseOperator SHL –Graphical

BitWiseDemo2

Invoke-BitWiseOperation -First 7 -BitWiseOperator BNOT

Bitwise_demo

Download Invoke-BitwiseOperation

https://gallery.technet.microsoft.com/scriptcenter/Invoke-BitwiseOperation-bc2f3c80

Posted in powershell | Tagged , , , | 5 Comments

Port Checking and Implementing Pipeline Support in Functions on MCPMag

I’ve had a couple of other articles come out on MCPMag with one that deals with port checking using PowerShell while the latest one talks about implementing pipeline support in a PowerShell function. Check them out and let me know what you think!

Port Checking Using PowerShell

Building PowerShell Functions That Support the Pipeline

Posted in powershell | Tagged , , , , | Leave a comment

Looking at Local Accounts Using PowerShell on MCPMag

I have a couple of articles out on MCPMag.com talking about working with local user accounts and reporting on accounts. Check the out and let me know what you think!

Reporting on Local Accounts Using PowerShell

Managing Local User Accounts with PowerShell

Posted in powershell | Tagged , , | Leave a comment

A Look at Implementing $Using: Support in PowerShell for PoshRSJob

After I made my initial release of PoshRSJob, one of the things left that I wanted to accomplish was implementing the $Using: scope that is found when using Invoke-Command or Start-Job which allows you to use an existing variable in the current console session and send that over to the scriptblock for the PSJob. This is pretty cool and allows for easier flow of sending data to a scriptblock in a PSJob. Naturally, if my module was going to be an alternative to the current infrastructure, it needed this capability as well. The problem is that determining how this works isn’t quite as easy as it seems. A couple of options would include looking at using RegEx or the PSParser to maybe make this happen. I will say that to provide support for PowerShell V2, I had to resort to these approaches, but more on that later.

After some digging  around in the language using DotPeek, I found where call was being made to take an existing scriptblock (with parameters) that have $Using and then does some conversion behind the scenes that will add the variable automatically to the Param() block and adjusts the $Using: variable within the scriptblock as well.

Of course, converting this C# code to PowerShell isn’t always straight forward, especially when we cannot just use some of the methods that they use without bringing some reflection skills into play.

V3+ Attempt

So, lets get going with taking the following scriptblock and converting it so we can take advantage of $Using: and see how it works.

## Scriptblock
$Data = 42
$Computername = $env:COMPUTERNAME
$ScriptBlock = {
    [pscustomobject]@{
        Computername = $Using:Computername
        Output = ($Using:Data * 2)
    }
}

Here we have our data and then our script block which contains our $Using: variables. Note that in this example I do not have anything in a Param() block (I will show another example after this with a Param() block) so we will have an extra step at the end to build this out.

Next up is using some AST magic (System.Management.Automation.Language.UsingExpressionAst) to parse out the Using variables by looking at the AST on the script block:

$UsingVariables = $ScriptBlock.ast.FindAll({
    $args[0] -is [System.Management.Automation.Language.UsingExpressionAst]
},$True)

What that gives us is the following output:

image

With this information, I now need to go and grab the actual values for each variable and then put that into an object that includes what the converted $Using variable will look like in the new script block.

$UsingVariableData = ForEach ($Var in $UsingVariables) {
    Try {
        $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop
        [pscustomobject]@{
            Name = $Var.SubExpression.Extent.Text
            Value = $Value.Value
            NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
            NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
        }
    } Catch {
        Throw "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!"
    }
}

image

Notice how these are now renamed with the $__using_ now instead of their original name. This is important with how we convert the script block later on. Keep in mind that the original script block still has the original values.

Now we start setting up for the script block conversion by creating a couple of collections to hold some data. One of those required is a Tuple type that will hold a particular AST type of VariableExpressionAST.

$List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]'
$Params = New-Object System.Collections.ArrayList

Assuming that we have Using variables, we can begin adding each item into our list collection.

If ($UsingVariables) {        
    ForEach ($Ast in $UsingVariables) {
        [void]$list.Add($Ast.SubExpression)
    }

With that done, next up is to work with our soon to be new parameters and adding the current values with the new values into our created Tuple.

[void]$Params.AddRange(($UsingVariableData.NewName | Select -Unique))
$NewParams = $Params -join ', '
$Tuple=[Tuple]::Create($list,$NewParams)

image

Now for some fun stuff! We need to use Reflection to hook into a nonpublic method that basically does all of the magic of converting the script block data into something that will be usable.

$bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance"
$GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags))
$StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple

))

image

Pretty cool, right? As I mentioned earlier, we need to build out a param() block still. This is due to one not already existing. Had I added a Param() block in the script block:

$ScriptBlock = {
    Param($Param1)
    [pscustomobject]@{
        Computername = $Using:Computername
        Output = ($Using:Data * 2)
    }
}

The output would look more like this:

image

Note how the $Param1 has been pushed to the very end while all of the new $__using variables are near the beginning.

Ok, with that out of the way, we will go back to dealing with our first example and adding our new Param() block.

If (-NOT $ScriptBlock.Ast.ParamBlock) {
    Write-Verbose "Creating Param() block" -Verbose
    $StringScriptBlock = "Param($($NewParams))`n$($StringScriptBlock)"
    [scriptblock]::Create($StringScriptBlock)
} Else {
    Write-Verbose "Param() will be magically updated!" -Verbose
    [scriptblock]::Create($StringScriptBlock)
}
}

And with that, we now have a script block that has been completely converted to support the $Using: variables! Of course, since I am using a nonpublic method to accomplish this, it does mean that this could be changed in a future release and could potentially break what I am doing.

What about V2 you ask? Well, since we do not have access to AST or the nonpublic method of ‘GetWithInputHandlingForInvokeCommandImpl’ to use for the conversion, we must resort to doing some of our own magic using the PSParser and RegEx.

V2 Using Attempt

We are going to use the same script block with just a minor change to full support V2:

$Data = 42
$Computername = $env:COMPUTERNAME
$ScriptBlock = {
    Param($TestParam)
    New-Object PSObject -Property @{
        Computername = $Using:Computername
        Output = ($Using:Data * 2)
    }
}

I am also going to use a function as well to make working with PSParser easier to convert a script block.

Function IsExistingParamBlock {
    Param([scriptblock]$ScriptBlock)
    $errors = [System.Management.Automation.PSParseError[]] @()
    $Tokens = [Management.Automation.PsParser]::Tokenize($ScriptBlock.tostring(), [ref] $errors)       
    $Finding=$True
    For ($i=0;$i -lt $Tokens.count; $i++) {       
        If ($Tokens[$i].Content -eq 'Param' -AND $Tokens[$i].Type -eq 'Keyword') {
            $HasParam = $True
            BREAK
        }
    }
    If ($HasParam) {
        $True
    } Else {
        $False
    }
}

Now we start pulling the $Using: variables by using the PSParser and looking at the tokens. I will also go ahead and pull the actual values of each variable and do the naming conversion just like I did with the previous example.

$errors = [System.Management.Automation.PSParseError[]] @()
$Tokens = [Management.Automation.PsParser]::Tokenize($ScriptBlock.tostring(), [ref] $errors)
$UsingVariables = $Tokens | Where {
    $_.Content -match '^Using:' -AND $_.Type -eq 'Variable'
}
$UsingVariable = $UsingVariables | ForEach {
    $Name = $_.Content -replace 'Using:'
    New-Object PSObject -Property @{
        Name = $Name
        NewName = '$__using_{0}' -f $Name
        Value = (Get-Variable -Name $Name).Value
        NewVarName = ('__using_{0}') -f $Name
    }
}

image

Now we get to the meaty part of this article by using quite a bit of code to work through the script block conversion.

$StringBuilder = New-Object System.Text.StringBuilder
$UsingHash = @{}
$UsingVariable | ForEach {
    $UsingHash["Using:$($_.Name)"] = $_.NewVarName
}
$HasParam = IsExistingParamBlock -ScriptBlock $ScriptBlock
$Params = New-Object System.Collections.ArrayList
If ($Script:Add_) {
    [void]$Params.Add('$_')
}
If ($UsingVariable) {        
    [void]$Params.AddRange(($UsingVariable | Select -expand NewName))
} 
$NewParams = $Params -join ', '  
If (-Not $HasParam) {
    [void]$StringBuilder.Append("Param($($NewParams))")
}
For ($i=0;$i -lt $Tokens.count; $i++){
    #Write-Verbose "Type: $($Tokens[$i].Type)"
    #Write-Verbose "Previous Line: $($Previous.StartLine) -- Current Line: $($Tokens[$i].StartLine)"
    If ($Previous.StartLine -eq $Tokens[$i].StartLine) {
        $Space = " " * [int]($Tokens[$i].StartColumn - $Previous.EndColumn)
        [void]$StringBuilder.Append($Space)
    }
    Switch ($Tokens[$i].Type) {
        'NewLine' {[void]$StringBuilder.Append("`n")}
        'Variable' {
            If ($UsingHash[$Tokens[$i].Content]) {
                [void]$StringBuilder.Append(("`${0}" -f $UsingHash[$Tokens[$i].Content]))
            } Else {
                [void]$StringBuilder.Append(("`${0}" -f $Tokens[$i].Content))
            }
        }
        'String' {
            [void]$StringBuilder.Append(("`"{0}`"" -f $Tokens[$i].Content))
        }
        'GroupStart' {
            $Script:GroupStart++
            If ($Script:AddUsing -AND $Script:GroupStart -eq 1) {
                $Script:AddUsing = $False
                [void]$StringBuilder.Append($Tokens[$i].Content)                    
                If ($HasParam) {
                    [void]$StringBuilder.Append("$($NewParams),")
                }
            } Else {
                [void]$StringBuilder.Append($Tokens[$i].Content)
            }
        }
        'GroupEnd' {
            $Script:GroupStart--
            If ($Script:GroupStart -eq 0) {
                $Script:Param = $False
                [void]$StringBuilder.Append($Tokens[$i].Content)
            } Else {
                [void]$StringBuilder.Append($Tokens[$i].Content)
            }
        }
        'KeyWord' {
            If ($Tokens[$i].Content -eq 'Param') {
                $Script:Param = $True
                $Script:AddUsing = $True
                $Script:GroupStart=0
                [void]$StringBuilder.Append($Tokens[$i].Content)
            } Else {
                [void]$StringBuilder.Append($Tokens[$i].Content)
            }                
        }
        Default {
            [void]$StringBuilder.Append($Tokens[$i].Content)         
        }
    } 
    $Previous = $Tokens[$i]   
}

Now we take that string and make it into a script block.

[scriptblock]::Create($StringBuilder.ToString())

image

Of course, using the PSParser does mean that it is possible for things to get missed if the variables are nested deep in something such as this:

Write-Verbose $($Using:Computername) –Verbose

Our $Using:Computername variable will be completely skipped, thus making it harder to go through with the conversion. Just something to keep in mind if you are using V2 and trying to get PoshRSJob to work properly.

This is definitely a one-off thing that I am doing, but wanted to share some of how I was able to provide some $Using support in my module. Hopefully this provides a little more insight into that and maybe gets you looking to explore more about the internals of PowerShell!

Posted in powershell | Tagged , , , | 1 Comment