PowerShell security - PowerShell management - PowerShell in Depth, Second Edition (2015)

PowerShell in Depth, Second Edition (2015)

Part 2. PowerShell management

Chapter 17. PowerShell security

This chapter covers

· Command execution

· Script signing

· Execution policy

Security is important in any computer software, and PowerShell is no exception. That said, you may have some confusion about what PowerShell’s security is meant to accomplish. We’ll clear that up in this chapter.

17.1. PowerShell security goals

Let’s start by defining exactly what PowerShell’s security is meant to accomplish and outlining a few things that it’s explicitly not intended to provide.

PowerShell’s only security goal is to prevent an uninformed user from unintentionally executing scripts. That’s it. The goal is to try to stop PowerShell from becoming an easy source for malicious scripts, as VBScript was back in the days of the “Melissa” and “I Love You” viruses. Keep in mind that PowerShell is security neutral, meaning that it neither adds to nor takes away from the existing security of the Windows operating system. In other words, if you have permission to delete users in Active Directory, PowerShell will let you do so—as will many other tools that havenothing to do with PowerShell. One reason that PowerShell doesn’t attempt to become a security gateway is because it’s almost never the only way in which you can do something. It makes no sense for PowerShell to act as a security system when it’s so easily bypassed by simply choosing to use other tools. If you’re concerned about your users using PowerShell to, say, delete every user in Active Directory, we can give you an easy fix: Don’t give them the permissions they’d need to do that. That way, they won’t be able to use PowerShell or any other tool to create that kind of havoc.

PowerShell is also not intended to stop an informed user from intentionally doing something stupid or dangerous. It’s like users having the keys to a nuclear missile: If they deliberately turn the key because they possess the necessary privilege or authority, lift the cover over the “fire” switch, and press the button, well, that’s hardly an accidental series of events, is it? If you don’t trust users to not do something stupid on purpose, they shouldn’t be in the missile silo in the first place. PowerShell is no different. If an administrator attempts to stop a mission-critical service, and they have the necessary rights and privileges, PowerShell won’t stop them, whether or not they’re using a script. The script means they can screw up faster with less typing. The bottom line is, don’t expect PowerShell to do your job when it comes to security.

17.2. PowerShell security mechanisms

So what exactly does PowerShell do to accomplish its security goals? Three levels of protection exist, each designed to thwart a particular type of attack that’s commonly targeted against uninformed users. To be clear, when we talk about uninformed users, we’re referring to someone lacking the necessary skills or experience to manage a modern Windows-based computer. This could be an end user or your summer intern.

PowerShell’s security mechanisms are layered and enabled by default out of the box. Some of them you can modify. But be warned: If you turn off these mechanisms and are burned by a malicious event, the blame is on you. As a general rule, PowerShell security is weakened only by changes you make. And although we understand that some of these mechanisms may require extra work on your part, don’t trade security for convenience. With experience you’ll find it’s not that difficult.

17.2.1. Script execution requires a path

To begin with, PowerShell never searches the current directory for a script. So, if you just run Dir, PowerShell will look to see if there’s a command by that name, then an alias by that name, and then it will stop. If there happens to be a script named Dir.ps1 in the current folder, PowerShell won’t execute it. So, whenever you see a command that’s not prefixed by a directory path, you can be sure it’s a command being run out of memory and not a script.

Script execution extends to other scripts as well such as batch files or VBScript. If you had a script in the current directory called Dir.bat, you’d ensure it too wouldn’t be executed simply by typing DIR.bat.

Windows search path

PowerShell will execute scripts that are on the Windows search path. If you create a script called test3.ps1, copy it into C:\Windows, and then type test3 at the PowerShell prompt, the script will execute. The full path to the script isn’t needed.

You can view the search path by typing this:

$env:path

A better view of the contents is supplied by using this:

$env:path -split ";"

This command will display one folder per line to make it easier to read.

If you want to add another folder to the Windows search path specifically for your PowerShell scripts, put a line like the following into your PowerShell profile:

$env:Path = "C:\Scripts\;" + $env:Path

The moral of the story is to be careful where you store your scripts so that you don’t inadvertently make it easier to run code by accident.

To run a script, you have to provide a path to it. That can be a complete absolute path like C:\Scripts\MyScript.ps1, or if you’re in the folder where the script lives, you might just use a simple relative path like .\MyScript.ps1. You don’t need to include the filename extension; running.\MyScript from the script’s folder will also run the script. But if there’s a chance you might have two scripts with the same name, perhaps a PowerShell and VBScript, then go ahead and use the extension. Take advantage of tab expansion and you don’t have to type that much. Start typing the path and the first part of the script name and then press Tab. PowerShell will expand the name. Keep pressing Tab until you find the script you want. Using this technique you’ll find there’s no misunderstanding about what command you intend to execute.

Whenever you see a path in front of a command name, you know it’s a script being run from disk and not an internal command being run from memory. So if someone tries to get you to run .\dir, you’ll know it’s a script named Dir.ps1, not the internal Dir alias to the Get-ChildItemcommand. The whole point of this is to prevent command hijacking—unintentionally executing a malicious script with the same name as a common command.

If by chance you have scripts like Dir.bat or Dir.vbs in the same folder, they won’t run unless you specify the full name with the extension. Given this, we hope you’ll use common sense and not name your script files using command names like DIR.

17.2.2. Filename extension associations

PowerShell defines a number of filename extensions for the various files it uses. This list shows most of the ones you are likely to come across:

· .PS1—Script file

· .PSM1—Script module

· .PSD1—Module manifest

· .PS1XML—XML file, usually view and type extension definitions

· .PSC1—Console file

· .PSSC—PowerShell session configuration file

· .CDXML—Cmdlet definition file (PowerShell v3 or 4 only)

Note

The “1” in these filename extensions indicates that they rely on version 1 of PowerShell’s language engine. That’s the same engine included in versions 1, 2, 3, 4, and 5 of PowerShell. A script written for PowerShell v1 is compatible with PowerShell v4 primarily because both versions use the same language engine. Differences exist between the PowerShell versions, so a script written using new functionality from v5 will fail if run in v1. You can think of the language engine as a subcomponent of PowerShell. This is also why versions 1, 2, 3, and 4 of PowerShell are installed in a folder named v1.0.

By default, none of these filename extensions are associated with PowerShell.exe, and they’re not registered with Windows as executable file types. Simply put, that means you can’t just double-click a script to run it. Out of the box, double-clicking one of these files will open them for editing, usually in Notepad. But that’s just the default, and it can certainly be changed. Installing third-party script editors, for example, may modify a filename extension so that it opens in that editor. You may or not want that behavior, so pay close attention when installing PowerShell-related software; the setting to stop the editor from grabbing the file association is often hard to find.

Tip

We see a lot questions in forums along the lines of “How can I run a PowerShell script when I double-click it?” The questioner is thinking ease of use rather than security. We always advise that this is something you shouldn’t change. This is an area where we practice what we preach—we don’t enable running a PowerShell file by double clicking it, ever!

The goal of this security mechanism is to keep users from getting emails with a “Postcard_from_Mom.ps1” file attachment, double-clicking the attachment, and running a potentially malicious script. The user could certainly save the file, open PowerShell, and run the script from disk—but that’s hardly an unintentional act. Remember that PowerShell isn’t designed to prevent intentional stupidity! Again, PowerShell will only execute what a user has permissions, privileges, and rights to perform. A script simply makes it easier.

17.3. Execution policy

The last, and perhaps most important, security mechanism is PowerShell’s execution policy. We need to cover this in depth, but before we do so we’ll explain a bit about some of its underlying technologies—including digital signatures.

17.3.1. A digital signature crash course

For years now, Microsoft has promoted the idea of signed software as a security mechanism. Signed software carries an encrypted bit of information called a digital signature. That signature contains information on the identity of the signer and also ensures that the software itself hasn’t changed in any way since the signature was applied. The practical upshot of this is that a signature tells you (a) who’s responsible for the software and (b) that it hasn’t changed since that responsible person distributed it. Any problems with the software can therefore be blamed on that responsible party, and the ID information contained within the signature enables you to track them down.

Signatures don’t prevent malware. But in a perfect world, only an extremely stupid person would apply a digital signature to a piece of malware because the signature lets you track them down. That’s in a perfect world.

This whole business with digital signatures comes down to your trust in a process. Let’s use an analogy: In the United States, driver’s licenses are the primary form of identification that most people carry. Among other things, they include your birth date, and so bars and similar establishments will use them to verify your age before serving you an alcoholic beverage. In computer terms, the United States has about 52 certification authorities (CAs): each of the 50 states, along with Washington, DC, and the U.S. military (which issues photo IDs to service members and their dependents). If you’re a resident of Nevada, you go to the Nevada Department of Motor Vehicles (DMV) to get your license. You’re perfectly able to take that license to California and order a beer, because California trusts the Nevada DMV. In reality, all of the states trust each other’s CAs, meaning your license is good throughout the entire country. Why is that? Well, there are obviously some legal reasons, but the reality comes down to this: The states trust each other because they all use basically the same process to verify your identity, and your age, prior to issuing you that certificate. It’s not exactly that the states trust each other but that they each trust the process that they all share. If it came out in the news that one state was issuing certificates—sorry, driver’s licenses—using a less-trustworthy process, then the residents of that state might not be able to order a beer in their neighboring states, because the trust would break down.

Okay, let’s take that back to computers. In the world of digital security there are different classes of certificate. Each class is generally based on how bad things would be if a certificate was issued to the wrong person. A Class 1 certificate is used to encrypt email, and obtaining one isn’t hard because the worst that could happen is that someone could read your email when you didn’t want them to. Bad for you, but not that bad for society as a whole.

The certificates needed to apply a signature to software are of the Class 3 variety. These are issued only to organizations, not to individuals, and they’re issued only after a fairly detailed process of verifying that the organization is who they say they are. CAs will often check a company’s credit score through Dun & Bradstreet, check the company’s business registration with their state authorities, and so forth. So if you have a certificate for Microsoft Corporation, folks can be pretty sure that you represent that corporation.

This is where the trust comes in. Certificates can be issued by a variety of commercial and private CAs; Windows is configured to have a list of CAs that it trusts. By default, Windows Vista and later have a small list of trusted CAs. It’d be easy for you to examine that list, contact each CA, find out what their verification process involves, and decide whether you trust that process. If you don’t, you remove the CA from your “trusted” list, essentially saying, “I don’t think you do a good job of verifying people’s identities before issuing them a certificate.” It’s as if that state just started handing out driver’s licenses with whatever you wanted printed on them—the process fails, and so the trust fails.

Assuming that your computer only trusts CAs that do a good job of identity verification, you can be sure that any digitally signed software did come from whatever organization that certificate was issued to. If the software is malicious, you can easily track down the responsible organization and take appropriate action. But if you trust a CA that doesn’t do a good job of identity verification, it’s entirely possible you’ll get a malicious piece of software that claims, perhaps, to be from “Adobe, Inc.” When you track them down (not too hard to do), you’ll discover that they have no idea what you’re talking about—someone must have fraudulently obtained a certificate with their name on it, because some CA that you trusted didn’t do a very good job of checking that identity.

Signatures also don’t prevent bad code. A signed script doesn’t necessarily mean it’s good PowerShell or that it’s safe to run in your environment. All you know from the signature is who wrote it and that it hasn’t been modified since it was signed.

17.3.2. Understanding script signing

At this point, it might be helpful to look at the script-signing process in a bit more detail. In order to sign a PowerShell script, you need a Class 3 code-signing certificate. For testing purposes, get your hands on a copy of the command-line tool Makecert.exe, which is usually part of Visual Studio. You can use this tool to create a self-signed certificate that’s only good for your computer. But this is still a handy tool for testing PowerShell security and digital signatures.

To begin, open a PowerShell or command prompt and navigate to the directory that contains Makecert.exe. The first step is to create a local certification authority. Type the following command. You can change the CN value if you’d like.

.\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, enter a password for the private key and then again when prompted. Now you’ll create a digital signature and store it in the local certificate store. Type this command as is, changing the CN value if you wish:

.\makecert -pe -n "CN=PowerShell Script Signer" -ss MY -a sha1 -eku

1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer

Enter the password when prompted. You can check the CERT: PSDrive for the new certificate:

PS C:\> dir Cert:\CurrentUser\My -CodeSigningCert

Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint Subject

---------- -------

0E04B179F42F4B080B0FCC47C54C4A7FD0AD45DE CN=PowerShell Script Signer

You can have multiple script-signing certificates, but generally all you need is one trusted in your domain. To sign scripts, you’re going to need this certificate, so save it to a variable:

PS C:\> $cert=dir Cert:\CurrentUser\My -CodeSigningCert

To sign a script, use the Set-AuthenticodeSignature cmdlet, specifying a file and a certificate. This cmdlet supports –Whatif.

PS C:\scripts> Set-AuthenticodeSignature .\TestScript.ps1 -Certificate

$cert -whatif

What if: Performing operation "Set-AuthenticodeSignature" on Target

"C:\scripts\TestScript.ps1".

Looks okay, so now do it for real:

PS C:\scripts> Set-AuthenticodeSignature .\TestScript.ps1 -Certificate

$cert

Directory: C:\scripts

SignerCertificate Status Path

----------------- ------ ----

0E04B179F42F4B080B0FCC47C54C4A7FD0AD45DE Valid TestScript.ps1

You can use the Get-AuthenticodeSignature cmdlet to view signature status:

PS C:\scripts> dir *.ps1 | Get-AuthenticodeSignature | Format-Table -Auto

Directory: C:\scripts

SignerCertificate Status Path

----------------- ------ ----

NotSigned Backup-EventLogv2.ps1

NotSigned Backup-VM.ps1

NotSigned BackupAllEventLogs.ps1

NotSigned BalloonTip.ps1

NotSigned get-computers.ps1

NotSigned get-computers2.ps1

NotSigned get-computers3.ps1

NotSigned get-computers4.ps1

NotSigned get-computers5.ps1

0E04B179F42F4B080B0FCC47C54C4A7FD0AD45DE Valid TestScript.ps1

...

As you can see, you have a number of other files that need to be signed, so go ahead and sign them:

PS C:\scripts> dir *.ps1 | Set-AuthenticodeSignature -Certificate $cert

This will sign all PowerShell scripts in the current directory. When you sign a script, a special comment block will be appended:

PS C:\scripts> Get-Content .\TestScript.ps1

#requires -version 2.0

$s="Hello {0}. Are you ready for some PowerShell today?" -f $env:username

write-host $s -ForegroundColor Green

# SIG # Begin signature block

# MIIEPAYJKoZIhvcNAQcCoIIELTCCBCkCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB

# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR

# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUrwnikA6r8TeOkIS7piC+KAS1

# kgWgggJGMIICQjCCAa+gAwIBAgIQ/xSr8g37e4hD3fK6vR/IcTAJBgUrDgMCHQUA

# MCwxKjAoBgNVBAMTIVBvd2VyU2hlbGwgTG9jYWwgQ2VydGlmaWNhdGUgUm9vdDAe

# Fw0xMjAxMTEyMTMwMTlaFw0zOTEyMzEyMzU5NTlaMCMxITAfBgNVBAMTGFBvd2Vy

# U2hlbGwgU2NyaXB0IFNpZ25lcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA

# vAqvNgzQ3VvU2VS4BwWPVzHYatVpI1ugAvy/uagppZmDoKTVIL4UiCfpP3tFWCLn

# 8r3Xfoldlcfqp0jkITU+ODJz9pH6tfS6WY+QB2GCFzXBOxj4nLsTqNYCH/G/mUHY

# iN1TtpGINOs5Akg4fWgo9xUfFSQCwY17OLMA2mEahOkCAwEAAaN2MHQwEwYDVR0l

# BAwwCgYIKwYBBQUHAwMwXQYDVR0BBFYwVIAQCWSVak4+ihZF92BFueq106EuMCwx

# KjAoBgNVBAMTIVBvd2VyU2hlbGwgTG9jYWwgQ2VydGlmaWNhdGUgUm9vdIIQqyHb

# dM1K1aFNW5MDoN5HwTAJBgUrDgMCHQUAA4GBADRtl+ccCCb+/Itds9iabZIyISDi

# nfN2mNkSnlrd5BdIorTMgonCYlQax5/htjGFeelD1T4u0iHfDhA3/xJOgd6aPNf4

# zSgqza8a8FEYVV8NCJZcyC0DXCJsllECpXvhQICR0sLd5z7eCNUF+7Gry78P6jdv

# mPDBAwYAtbp4/nzvMYIBYDCCAVwCAQEwQDAsMSowKAYDVQQDEyFQb3dlclNoZWxs

# IExvY2FsIENlcnRpZmljYXRlIFJvb3QCEP8Uq/IN+3uIQ93yur0fyHEwCQYFKw4D

# AhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwG

# CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZI

# hvcNAQkEMRYEFJC2WAEM4wvx98CaNLrvHK7BM4NgMA0GCSqGSIb3DQEBAQUABIGA

# tewnic/hZcuJoe22VxHDqjjLdrjyiaVuPFSYcPUpunTX3c8COeLfU6Yrq5QEGp8V

# 8wKFFFcp4o9ifSfRFxUqUV6CPZEr3udEhgiKugsYGv/GLOWAh1rSV0lD3g2HuocS

# f2g1Bd0fcXfzMIOCOmzjkx7H6zRbo9+B4QdWO5yL7e8=

# SIG # End signature block

PS C:\scripts>

If you edit the file, even by changing a single character or space, the signature will break:

PS C:\scripts> Get-AuthenticodeSignature .\TestScript.ps1

Directory: C:\scripts

SignerCertificate Status Path

----------------- ------ ----

0E04B179F42F4B080B0FCC47C54C4A7FD0AD45DE HashMismatch TestScript.ps1

The solution is to simply resign the script:

PS C:\scripts> Set-AuthenticodeSignature .\TestScript.ps1 -Cert $cert

Some editors such as SAPIEN’s PowerShell Studio can be configured to automatically sign scripts whenever you save them. Reading the help file about_Signing is recommended. Now, how does all of this relate to PowerShell?

17.3.3. The execution policy in depth

PowerShell’s execution policy can be set to one of five levels, all of which correspond to some degree of digital signature checking and script execution:

· RestrictedThis is the out-of-the-box execution policy for client operating systems and most servers, and it means that scripts won’t run. This includes scripts started locally or using PowerShell remoting. It also includes your profile scripts! The one exception is on Windows Server 2012 R2 where the default execution policy is RemoteSigned.

· RemoteSignedWith this policy, scripts created on the local computer will execute just fine. Scripts created from a remote computer, including network shares, will run only if they carry a digital signature, and that signature must have been made by using a certificate issued from a trusted CA. The signature must also be intact, meaning the script can’t have changed one tiny bit since it was signed. Note that some applications, notably Firefox, Internet Explorer, and Outlook, place a special flag into the header of files they download. Those files are considered “remote” by PowerShell and may be blocked. We’ll explain how to handle blocked files a bit later in the chapter.

· AllSignedBasically the same as RemoteSigned, except that all scripts must be signed, no matter where they came from. This won’t prevent a malicious but signed script from executing.

· UnrestrictedAll scripts will run without a signature.

· BypassThis shuts down PowerShell’s execution policy entirely. It’s mainly intended to be used by developers who are hosting PowerShell inside another application, when that application will provide its own security and PowerShell’s isn’t needed.

There’s also a setting of Undefined, which means nothing is set for the current scope. Depending on how a PowerShell script is executed, you might end up with different execution policies in different scopes. If the setting is Undefined, generally this will have the same effect as Restricted. You can read more about the execution policy types in the help file about_Execution_Policies.

You can see the current execution policy by running Get-ExecutionPolicy:

PS C:\> Get-Executionpolicy

Restricted

To modify it, run Set-ExecutionPolicy in an elevated session and follow the prompts:

PS C:\> Set-ExecutionPolicy RemoteSigned

Execution Policy Change

The execution policy helps protect you from scripts that you do not trust.

Changing the execution policy might expose you to the security risks

described in the about_Execution_Policies help topic at

http://go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the

execution policy?

[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):

PS C:\> get-executionpolicy

RemoteSigned

If you prefer not to be prompted, use the –Force parameter:

PS C:\> Set-ExecutionPolicy AllSigned -Force

PS C:\> Get-ExecutionPolicy

AllSigned

The change is immediate. Note that the execution policy is stored in the HKEY_ LOCAL_MACHINE portion of the Registry, which normally means that you have to be a local Administrator to change it. We don’t recommend modifying the Registry directly, but you can certainly check it with this one-line command:

PS C:\> Get-ItemProperty HKLM:\SOFTWARE\Microsoft\PowerShell\1\

ShellIds\Microsoft.PowerShell -Name executionpolicy |

select ExecutionPolicy

ExecutionPolicy

---------------

RemoteSigned

This is a handy command that you could use to query a remote computer using Invoke-Command or other .NET remote Registry tricks. Of course, the easiest way to check a remote computer’s execution policy is to use PowerShell Remoting.

PS C:\> Invoke-Command {Get-ExecutionPolicy} -computer Client2

PSComputername RunspaceID Value

-------------- ---------- -----

Client2 5b704b5c-cf6... Restricted

The execution policy can also be deployed through an Active Directory Group Policy Object (GPO). When configured in that fashion, the GPO setting will override any local setting or any attempt to change it.

Finally, you can also change the execution policy for a single PowerShell session by using the –ExecutionPolicy switch of the PowerShell.exe executable:

C:\windows\system32>powershell -Executionpolicy allsigned

Windows PowerShell

Copyright (C) 2013 Microsoft Corporation. All rights reserved.

. : File C:\Users\Richard\Documents\WindowsPowerShell\profile.ps1 cannot be

loaded. The file C:\Users\Richard\Documents\WindowsPowerShell\profile.ps1 is not digitally signed. You cannot run this script on the current system. For more information about running scripts and setting execution policy, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.

At line:1 char:3

+ . 'C:\Users\Richard\Documents\WindowsPowerShell\profile.ps1'

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : SecurityError: (:) [], PSSecurityException

+ FullyQualifiedErrorId : UnauthorizedAccess

PS C:\windows\system32>

In this example we started a new PowerShell session from the CMD prompt, specifying an AllSigned policy. We can tell it worked because the profile scripts, which aren’t signed, failed to run.

Note

If you’re using a GPO to apply execution policies, you won’t get an error message, but your setting also won’t be applied.

PowerShell isn’t intended to stop an informed user from intentionally doing anything—and adding a command-line parameter in that fashion is definitely the sign of an informed user doing something very much on purpose. If at this point you’re still concerned about a savvy user getting hold of this to run scripts, then all we can ask is why haven’t you limited their access and permissions by now? Remember, commands executed in a PowerShell script are generally no different than what a user could type interactively in a console. If users can’t run a script, and they’re savvy enough, there’s nothing to prevent them from copying and pasting the script contents into a PowerShell console and executing them (other than permissions and privileges).

So what’s the effect of all of this? Well, it depends on the execution policy and the validity of any digital signatures. If the execution policy is anything but AllSigned, PowerShell will run any script, signed or not, even if the signature isn’t valid. But with AllSigned, you’ll get errors if the script isn’t signed:

PS C:\scripts> Set-ExecutionPolicy Allsigned -Force

PS C:\scripts> .\NewScript.ps1

File C:\scripts\NewScript.ps1 cannot be loaded. The file

C:\scripts\NewScript.ps1 is not digitally signed. The script will not execute on the system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.

At line:1 char:1

+ .\NewScript.ps1

+ ~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], PSSecurityException

+ FullyQualifiedErrorId : UnauthorizedAccess

PS C:\scripts>

Or if the signature is invalid:

PS C:\scripts> .\TestScript.ps1

File C:\scripts\TestScript.ps1 cannot be loaded. The contents of file

C:\scripts\TestScript.ps1 may have been tampered because the hash of the

File does not match the hash stored in the digital signature. The script

will not execute on the system. Please see "get-help about_signing" for

more details..

At line:1 char:1

+ .\TestScript.ps1

+ ~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (:) [], PSSecurityException

+ FullyQualifiedErrorId : UnauthorizedAccess

PS C:\scripts>

Don’t ignore these error messages. They’re telling you something important, which is why you might want to use an AllSigned policy. The solution, after verifying the file, is to resign it:

PS C:\scripts> Set-AuthenticodeSignature .\TestScript.ps1 -Cert $cert

PS C:\scripts> .\TestScript.ps1

Do you want to run software from this untrusted publisher?

File C:\scripts\TestScript.ps1 is published by CN=PowerShell Script Signer

And is not trusted on your system. Only run scripts from trusted publishers.

[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help

(default is "D"):r

Hello Administrator. Are you ready for some PowerShell today?

PS C:\scripts>

Because in this example you’re using a self-signed certificate, you get a warning about the publisher. But because you recognize the publisher, you can go ahead and run the script. Oh, and notice that you had to specify the path to the script file, even though you were in the same directory?

To sum up these mechanisms, if you want to execute a PowerShell script you must have an appropriate execution policy. If you’re using digital signatures, the signature must be valid. Then, to execute the script you need to specify the script path.

17.4. Blocked files

On a related note, you’ll also run into issues if you try to run a script that you’ve downloaded from the internet. On a Windows 8.1 desktop, if we try to run this downloaded script we receive an error:

PS C:\scripts> .\Get-CIMFile3.ps1

.\Get-CIMFile3.ps1 : File C:\scripts\Get-CIMFile3.ps1 cannot be loaded.

The file C:\scripts\Get-CIMFile3.ps1 is not digitally signed. You cannot

run this script on the current system. For more information about running

scripts and setting execution policy, see about_Execution_Policies at

http://go.microsoft.com/fwlink/?LinkID=135170.

At line:1 char:1

+ .\Get-CIMFile3.ps1

+ ~~~~~~~~~~~~~~~~~~

+ CategoryInfo : SecurityError: (:) [], PSSecurityException

+ FullyQualifiedErrorId : UnauthorizedAccess

If you download a lot of files, you might want an easy way to identify them. The Get-Item cmdlet includes the parameter –Stream, which will display any alternate stream data. Downloaded files will have a Zone.Identifier stream.

PS C:\scripts> dir *.ps1 | Get-Item -Stream zone.identifier

-ErrorAction SilentlyContinue | where Stream

FileName: C:\scripts\Get-CIMFile3.ps1

Stream Length

------ ------

Zone.Identifier 26

FileName: C:\scripts\Get-VMMemoryReport.ps1

Stream Length

------ ------

Zone.Identifier 26

This command passes all PowerShell scripts to Get-Item looking for the Zone .Identifier stream. We’re setting the ErrorAction to SilentlyContinue to suppress error messages for scripts that don’t have the stream. Once identified, and after we’re convinced of their safety, we can unblock them:

PS C:\scripts> dir *.ps1 | Get-Item -Stream zone.identifier

-ErrorAction SilentlyContinue |

foreach { Unblock-File $_.filename }

If you’ve downloaded a set of files into a new folder so you know that all the files will be blocked, you can simplify the process:

Get-ChildItem -Path c:\testdata | Unblock-File

From this point, execution will depend on your policy.

Note

Any file downloaded through Internet Explorer will be blocked—including Word and Excel files. This technique can also be applied to those files, not just PowerShell scripts.

17.5. The PowerShell security debate

Microsoft has generally recommended the RemoteSigned execution policy, suggesting that it offers a good balance between security and convenience. After all, with AllSigned you have to sign every single script you run, normally using the Set-AuthenticodeSignature cmdlet to do so. What a pain in the neck! You also have to have a certificate, and those can be expensive—about $800 per year from most commercial CAs. You can also create your own local-use-only certificate using the Makecert .exe utility; run help about_signing in PowerShell to read more about that. And of course, if your organization has its own internal Public Key Infrastructure (PKI), that can be used to issue the necessary Class 3 certificates.

Other folks, including Microsoft’s own Scripting Guy, suggest using Unrestricted instead. Their argument is that the execution policy provides little in the way of protection, because it’s easily bypassed. That’s certainly true: If you were going to deploy a piece of malware that relied on a PowerShell script, you’d do it as a piece of .NET Framework code that hosted the shell and bypassed the execution policy entirely. Attackers are informed enough to do that. The point is that PowerShell’s execution policy isn’t a substitute for antimalware utilities, and if you have a good antimalware utility, then the execution policy seems less useful.

Our take? We usually go for the RemoteSigned policy. We know plenty of clients who use AllSigned, and they use it as a kind of change control mechanism: Only certain administrators possess the Class 3 certificate needed to sign scripts, and so any script released to the production network must be reviewed by one of them, in compliance with the organization’s change management processes. So they’re using the execution policy more as a process enforcement tool than a security mechanism, which is just fine. The other advantage, which Jeff firmly believes in, is that digital signatures guarantee script integrity. If the script has been modified in any way, even by changing a single character, the signature will fail and the script won’t execute. To Jeff’s way of thinking, he’d rather have a script fail to execute than start running only to fail partway through because of some bit of corruption, leaving you stuck between a rock and hard place. Granted, Jeff is an old-school “belt and suspenders” kind of IT pro, but the point is that a signed script can guarantee it hasn’t been modified in any fashion, either deliberately or not. And regardless of your execution policy, you must review scripts acquired elsewhere before running them and ideally only then in a controlled test environment.

Tip

Remember that many of the files that ship with PowerShell, such as the format files, are digitally signed by Microsoft. Don’t make any changes to those files or you will have problems running PowerShell.

We’ll point out one other consideration: PowerShell profile scripts. Keep in mind that these scripts are stored in your Documents folder, which you obviously have full control over. Even if you’re logging on with a lesser-privileged account (in keeping with the principle of least privilege), that account by definition has full control over the Documents folder and your profile scripts. A simple piece of malware could thus modify your profile script, inserting malicious commands. The next time you run PowerShell—which you’d likely be doing with elevated privileges—those inserted commands would run automatically. Using the AllSigned execution policy helps thwart this specific attack, because your profile would also have to be signed, and the malicious insertions would break the signature, causing an error the next time you open the shell. Now, we’ll also freely admit that AllSigned provides only the barest kind of protection against this attack, and the fact is that in order for it to happen you have to have uncaught malware on your machine! If you have malware, your PowerShell profile is far from your biggest problem; “Once you’re 0wned, you’re 0wned,” as the saying goes. But it’s a consideration, and an illustration of the complexity of the PowerShell security debate.

17.6. Summary

This chapter gave you an overview of what PowerShell security is meant to accomplish and how it attempts to do so. We hope you now have a better idea of what to expect from PowerShell’s security features and how you’d like to use them in your organization.