One will see in many places in Microsoft documentation and in several books out there that PowerShell has security system called Execution Policy, I personally do not agree this is a security measure but just a simple control to protect from accidental execution of code not specifically allowed thru normal means. First lets cover what are the security minded default configurations that PowerShell has:
- It does not execute scripts by double clicking on them by default.
- All scripts must be digitally signed with a trusted digital certificate by the host system so as to be able to execute.
- All script when executed in a PowerShell session must be executed by providing the path of the script wither relative or full they cannot be executed just by name.
- Code is executed under the context of the user.
- Code that is downloaded via a web browser or thru emails clients that mark the file as downloaded from the Internet in the file meta-data the file will blocked from execution unless specifically allowed.
These defaults settings provide the following protections:
- Control of Execution - Control the level of trust for executing scripts.
- Command Highjack - Prevent injection of commands in my path.
- Identity - Is the script created and signed by a developer I trust and/or a signed with a certificate from a Certificate Authority I trust.
- Integrity - Scripts cannot be modified by malware or malicious user.
Microsoft took great care and attention to minimize the attack surface of PowerShell when an attacker tries to trick a user in to executing a possibly malicious script. Once on the system things change since these controls cannot protect from:
- Copy pasting the content of the script in to PowerShell.
- Encoding the script in Base64 and running it from the command line as an argument to the powershell.exe
- Enter each command by hand and execute it.
Sadly PowerShell does not provide a way to block specific cmdlets or .NET APIs from users to do a more fine grained control on system. This allows say malware already present on the system or an attacker that has been able to get a foothold on the system to leverage PowerShell. An example of this is the first known use of Powershell Code as Malware in the wild
http://nakedsecurity.sophos.com/2013/03/05/russian-ransomware-windows-powershell/ in addition to this PowerShell has also been added to what I call dual purpose tools like Metasploit and Social Engineering Toolkit that are written primarily for Penetration testers and researchers but sadly can also be used by a malicious attacker does why I refer to them as dual purpose tools.
Changing Execution Policy
To control the validation of scripts and cmdlets that be use the
Set-ExecutionPolicy cmdlet is used. There are several Policies that can be used:
- Restricted No Script either local, remote or downloaded can be executed on the system.
- AllSigned All script that are ran require to be digitally signed.
- RemoteSigned All remote scripts (UNC) or downloaded need to be signed.
- Unrestricted No signature for any type of script is required.
Each of these policies can be applied to different scopes to control who is affected by them, the scopes are:
- MachinePolicy: The execution policy set by a Group Policy for all users.
- UserPolicy: The execution policy set by a Group Policy for the current user.
- Process: The execution policy that is set for the current Windows PowerShell process.
- CurrentUser: The execution policy that is set for the current user.
- LocalMachine: The execution policy that is set for all users.
The default scope is LocalMachine and it will apply to everyone on the machine when set via PowerShell it self. To get the current execution policy we use the
Get-ExecutionPolicy cmdlet running it in a session as administrator and we give it the
–list parameter to list all scopes
C:\Windows\system32> Get-ExecutionPolicy -List | ft -AutoSize Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Undefined
LocalMachine RemoteSigned
Typically for admin workstations I recommend RemoteSigned since any code downloaded from the internet I will not execute it by accident causing harm to my machine. Lets change it from RemoteSigned to Restricted, for this we use the Set-Executionpolicy and give it the policy name, we can use the –Force parameter so it will not ask for confirmation and we can conform by traying to execute a script:
C:\Windows\system32> Set-ExecutionPolicy Restricted -Force
C:\Windows\system32> C:\Users\Carlos\Desktop\hello.ps1
C:\Users\Carlos\Desktop\hello.ps1 : File C:\Users\Carlos\Desktop\hello.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
+ C:\Users\Carlos\Desktop\hello.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
Code Signing
Code signing allows us to use cryptographic signatures to gain the following capabilities:
- Provides an identity of the source of code.
- Ensure detection of script modification.
To be able to add a digital signature to a script we must use a Authenticode Digital Certificate, the certificate can come from:
- Certificate from a Certificate Authority - This type of certificate allows the signing and sharing of scripts. If a Commercial CA is used the script could be shared outside of an organization.
- Self-Signed Certificate - This certificate is generated by a CA hosted in he computer it self where it is used.
Self-Signed Certificates
Lets look at generating a self signed certificate for use in code signing. We start by using the makecert.exe tool from the Windows SDK that can be downloaded from Microsoft for free.
- Run a Windows Command Prompt as Administrator
- Run the following command in the Command Prompt. It creates a local certificate authority for your computer:
makecert -n "CN=PowerShell Local Certificate Root" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine
- When prompted for a Private Key password provide one of your choosing that you are able to remember and confirm the password.
- When prompted again for the password enter the password you entered in the previous step.
Next we need to use MMC for opening the local certificate store and saving the certificate:
- Open Windows MMC on Windows
- In the console window click on File and select Add/Remove Snap-in
- Select “Certificates” and click on “Add”
- When prompted accept the default of “My user account” and click on “Finish”
- Click on OK. This will give you a Management Console for your current user Certificate Store so we can look at the results from the commands and manage the certificates from the Windows GUI with ease.
- Go in to the MMC Console and Select “Trusted Root Certification Authorities” -> “Certificates” and on the right pane ensure there is a Root Certificate for “PowerShell Local Certificate Root”
- Run the following from a Command Prompt, give in the Common name filed a friendly name including your username/handle. It generates a personal certificate from the above certificate authority:
makecert -pe -n "CN=Carlos PowerShell CSC" -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer
- You will be prompted for the certificate Private Key Password, enter the password that you provided when creating the CA Private Key.
- To verify that the certificate was created and stored in the proper location go in to the console MMC Console and Select “Personal” -> “Certificates” and on the right pane ensure there is a certificate with the name that you specified in the command to create the signing certificate
You are now ready to use the self signed certificate.
Exporting Self-Signed Certificate
If you want you can export the self signed certificate for use in other systems, for that follow these steps:
- Expand “Personal”, right click on the appropriate code signing certificate and select “All Tasks” -> “Export…”.
- Choose the option “Yes, export the private key” when prompted.
- Accept the default options on the “Export File Format” screen.
- Enter a password for the private key, which will need to be entered when importing the certificate
- Save the certificate to an appropriate location.
- Right click “Trusted Publishers” and select “All Tasks” -> “Import…”
- Follow the wizard to import the exported certificate, and enter in the accompanying password that was used when the certificate was exported.
- Accept all the default values for the remaining steps in the wizard.
- If the certificate is no longer required to be imported by other machines, it is highly recommended that the exported file is deleted.
- Verify that the certificate was properly installed under the correct location
Signing Certificates via Active Directory Certificate Services
- From Administration Tools select the Certificate Authority Console on your Enterprise Root Certificate Authority
- Right click on Certificate Templates and select Manage
- Double click on the Code Signing template to open it’s Properties
- Add the group that you want to be able to request code signing certificates
- Allow Read and Enroll
- Right Click on Certificate Templates -> New -> Certificate Template to Issue
- Click on the Code Signing template
- Click on OK and close the Certificate Authority Console
On the developers machine:
- Open a new MMC console
- From the File Menu select Add/Remove Snap-in
- Select Certificates, click on Add and click on Ok
- Make sure that My user account is selected
- Click on Finish
- Click on Ok
- Right click on Personal Select All Tasks -> Request New Certificate
- Click Next on the screen that appears
- Select Active Directory Enrollment Policy and click on Next
- Select the Code Signing certificate template
- Expand Details and click on Properties
- Select the Private Key
- Select Make private key exportable
- Click on Ok
- Click on Enroll
- Click on Finish
Using the Code Signing Certificate
Since the certificate store is mapped as a PSDrive automatically we can check if a code signing certificate is available directly from PowerShell. Having PowerShell have access to the certificate store allows to very easy manipulation and signing scripts and other files, the cmdlets for working with Authenticode are:
- Get-AuthenticodeSignature checks the Authenticode signatures for files that support Subject Interface Package (EXE, PS1, PSXML, DLL, VBS ..etc.
- Set-AuthenticodeSignature adds an Authenticode signature for files that support Subject Interface Package
In fact the cmdlets as we can see not only allow us to sign PowerShell Scripts and Modules but we can also sing several windows files.
To list the certificates we can just use the certificate store like any drive and ask to only show code signing certificates using the –CodeSigningCert parameter when Certificate Store PSDrive is used
For signin the certificate must be passed as a object to the Set-AuthenticodeSignature cmdlet so we may need to save it in to a variable. Signing of a script would be like this:
PS C:\>
$acert =(dir Cert:\CurrentUser\My -CodeSigningCert)[0]PS C:\> Set-AuthenticodeSignature .\hello.ps1 -Certificate $acert
Now when we check our script we will see it is signed:
PS C:\>
Get-AuthenticodeSignature .\hello.ps1 | ft -AutoSizeDirectory: C:\Users\Carlos Perez\Desktop
SignerCertificate Status Path
----------------- ------ ----
9854ABA48101875C7D9A7F79F8DD0B71C911F73C Valid hello.ps1
The script should now look like this:
So I hope you liked the blog post and found it informative on the second part I will cover how to bypass the execution policy and how an attacker or malware may abuse it.