In a previous article on PowerShell remoting, we reviewed the authentication security options to run PowerShell commands and scripts on remote computers.

MCSE Training – Resources (Intense)

This article concentrates on the PowerShell security environment configuration, including execution policies and scopes and how they can be used to prevent accidental or unintentional script execution. Without careful consideration of PowerShell execution policies, you risk running scripts that may have detrimental effects not only on a single computer but across multiple systems in the network.

PowerShell Scripting Security

PowerShell security can be used as a barrier that gets in the way of an inexperienced user who may inadvertently try to run an untrusted script. This is very important to understand because the shell security settings do not deter a malicious user with access privileges from executing any command at the prompt. For example, you could copy a PowerShell script content, paste it on the shell and run it even if the strictest PowerShell script execution policy has been applied to that computer. In other words, the shell security does not prevent harmful commands from being entered interactively.

Regardless of the PowerShell version that you use, all PowerShell scripts use a .ps1 extension. By default, the .ps1 extension is associated with Notepad, so when you double-click on a .ps1 file, a Notepad instance opens to show you the content of the script. This behavior is by design and it aims to ward off accidental execution of untrusted or malicious scripts.

PowerShell Execution Policy Settings

Configuring the PowerShell execution policy is the most common way to enforce scripting security on the shell. To verify the current execution policy setting, run the command below:

PS C:\Get-ExecutionPolicy

The execution policy has five configuration options:

Restricted—Prevents running any PowerShell script.
That includes files with the following extensions: .ps1 (regular PowerShell scripts), .ps1xml (formatting and configuration files), and .psm1 (module script files). Restricted is the default execution policy for all Windows clients and Windows Server versions previous to Windows Server 2012 R2.

RemoteSigned—Does not require digital signatures to run scripts created on the local computer. However scripts downloaded from the internet can only run if they have been digitally signed by a publisher using a certificate that is trusted by the local computer. PowerShell identifies remote scripts because applications like Internet Explorer, Outlook, Lync, and Firefox attach a Zone.Identifier alternate data stream to files when they are downloaded. Windows Server 2012 R2 is the only Windows Server version with RemoteSigned as the default execution policy.

One way to run downloaded scripts without changing the RemoteSigned policy is by using the PowerShell Unblock-File cmdlet. This command allows you to unblock files that were downloaded from the internet. For example, to unblock a PowerShell script named clean.ps1 stored on the “D” drive on a folder named scripts, you can run the following command:

PS C:\Unblock-File –Path D:\Scripts\clean.ps1

AllSigned—Requires
all scripts to be digitally signed regardless of their origin. The signature must come from a trusted publisher using a certificate issued by a trusted certification authority (CA). A digital signature is used to guarantee the authenticity of the script. Once the script is signed, it cannot be changed by even one extra period without breaking the signature. A broken signature indicates that the script has been tampered with and PowerShell will not run it. A script with a broken signature is rendered unusable and it’s no longer trusted.

Unrestricted—allows all scripts to run and make changes to the system as long as the user running the script has user rights or permission to make the changes.

Bypass—This is a
distinct option mainly used by applications that host Windows PowerShell. The Bypass setting disables shell security and enables the application to handle script security.

Undefined: No policy has been assigned to the scope or the assigned policy has been removed from the scope. More on scopes later in this article.

There are three main options to configure PowerShell script execution policies:

1. Use a Group Policy Object (GPO) to set the execution policy for domain computers. When you configure PowerShell security using Group Policy Management, the GPO settings will override any local setting and will affect all future PowerShell sessions for the specified users or computers. To configure Script execution using a GPO, from the Group Policy Management Editor navigate to: Computer Configuration—Policies—Administrative Templates—Windows Components—Windows PowerShell. On Windows PowerShell under Settings, double-click “Turn on Script Execution.”

On the “Turn on Script Execution” configuration window, click on “Enabled” and then, under “Execution Policy,” click on the drop-down menu to select your security setting.

2. Run PowerShell.exe by using the –ExecutionPolicy parameter. For example, to set the shell execution policy to AllSigned, you type:

C:\PowerShell.exe -ExecutionPolicy AllSigned

The preceding command only affects the current shell session. However, it overrides any local or GPO PowerShell security settings. If you close and start PowerShell again, the new session will not retain the execution policy settings configured using PowerShell.exe in the previous session.

3. Run the Set-ExecutionPolicy cmdlet as a local administrator to set the execution policy for the computer. For example, to set the user preference for the shell execution policy to unrestricted, you can run the following:

PS C:\Set-ExecutionPolicy Unrestricted

The previous command sets a persistent setting for PowerShell that
will remain in effect after you close the current shell session.

The Set-ExecutionPolicy cmdlet allows you to configure the settings at different Execution Policy scopes. Running the following command shows those scopes.

PS C:\ Get-ExecutionPolicy—List | Ft—AutoSize

The default is LocalMachine, which affects all users on that computer. The CurrentUser option affects only the current user running the PowerShell session. The Process scope is only valid for the current session and goes away when the session is closed. The MachinePolicy and the UserPolicy scopes are group policy configuration settings that apply to domain joined computers. When multiple policies are configured for different scopes, PowerShell follows an order of precedence to determine the effective execution policy. Lower number indicates higher priority:

  1. Group Policy: Computer Configuration
  2. Group Policy: User Configuration
  3. Execution Policy: Process
  4. Execution Policy: CurrentUser
  5. Execution Policy: LocalMachine

For example, to set the CurrentUser Execution Policy to AllSigned, run the following scope:

PS C:\ Set-ExecutionPolicy -Scope CurrentUser AllSigned

To verify the changes, run Get-ExecutionPolicy cmdlet again

According to the above output, the effective execution policy is AllSigned because the execution policy set for the current user takes precedence over the execution policy for the local computer. The “Undefined” value is used with the Set-ExecutionPolicy cmdlet to remove the execution policy for a specific scope.

The Execution Policy scopes are different from the Windows PowerShell scopes. To better understand the security context on which PowerShell scripts run, let’s review how Windows PowerShell scopes affects script execution.

Windows PowerShell Scopes

In Windows PowerShell, a scope is a logical container within which shell elements like scripts, functions, and variables operate. You may think of a scope as a security boundary designed to protect specific objects within the environment, such as variables, aliases, functions, and PowerShell drives from being accidentally modified by scripts or functions. Everything you do on PowerShell is executed inside a scope, so understanding how PowerShell reads and writes information using these scopes is very important to ensure proper PowerShell script execution and to avoid unexpected results that may come from not understanding the PowerShell scope rules. Windows PowerShell provides the following scopes:

Global—This is the shell itself that becomes available when PowerShell starts. Any variable, alias, function, or PowerShell drive created on the shell belongs to this scope. Also all built-in variables, aliases, functions, and PSDrives, along with items from your PowerShell profile, live within this global container.

Local—It refers to the scope currently in use or executing commands. For example, if you are working at the shell (global scope), then that is your local scope. However, if you are running a script, your script scope is now your local scope.

Script—Each script that you run is given its own separate and exclusive scope. Only commands that are in the script operate within the script scope.

Private—A private scope is used to create items that can be accessed only within the current scope. This prevents commands or scripts from other scopes from seeing the items within a private scope.

By default, everything that a PowerShell command creates is created in its own scope. If a script creates two new variables and a new function, those three elements are created in the script’s scope. When the script finishes running, those three elements and the script’s scope are all removed from memory.

Scope Rules

Each scope functions as a wrapper with definite rules that define the read and write access to variables, functions, aliases, and PowerShell drives within it. By default, any item that you create or write within a scope can be modified only inside that scope; however, it is possible to create an item within a scope with a label that corresponds to a different scope. For example, any variable created within a script scope only exists within that script, but you can define that variable as global to make it available from the global scope.

Another important rule relates to the parent/child relationship of PowerShell scopes. Any element that you create in a scope can be read in that scope or in any child scope, except when the element is defined as private. Now, it is important to clarify that a child scope does not inherit the elements like functions, variables, and aliases that you create in the parent scope, the child scope can only read or view these elements as they exist in the parent scope. The figure below shows a typical parent/child scope association in PowerShell.

Using PowerShell Scopes

Let’s say that we define a variable on the shell as $M = 100. This variable will be created in the global scope.

As you see on the above screenshot, $M is assigned a value of 100; when you type $M, the shell echoes back the value of 100.

Now let’s create a variable with the same name in a script and another variable using the same name within a function in the script.

The following script will be run from a file called Script1.ps1.

# This verifies that the script can read the value of $M
"The value of `$M in the Global scope is $M"
# This creates the $M variable within a function

Function Functionvariable
{
$M = 25

“The value of `$M in the function is $M”
}

# This creates the $M variable within the script:
$M = 50
“The value of `$M in the script scope is $M”

Functionvariable

 

Below is the value of $M before and after running the script.

As you can see from the shell output the $M variables created in the script and using the function within the script disappeared after the script ran. The $M variable in the global scope remains with the same value.

There may be situations in which you want to create or modify variables beyond the current or local scope. For those cases, you can enter the scope name at the start of the variable name, separating the scope name from the variable name with a colon. For example:

$Global:M

$Script:M

$Local:M

Or you can use the New-Variable cmdlet:

New-Variable –Name “M” –Scope Global

New-Variable –Name “M” –Scope Script

New-Variable –Name “M” –Scope local.

Another option to run a script in the current scope is by using the dot sourcing PowerShell functionality. As you saw in this article, variables in a script disappear after the script runs. However, dot sourcing allows you to run a script in the current scope as an alternative to the default script scope. To dot source a script, all you need to do is type a dot (.) and a space before the script path. For instance:

PS C:\. . \script1.ps1

Or

PS C:\. C:\script1.ps1

Closing Remarks

Understanding the execution policy and scope configuration settings allows you to prevent undesirable results that may come from unintentionally running untrusted or malicious scripts. These settings also provide you with multiple options to better control script execution on your systems. Our next article will review script writing techniques to foolproof PowerShell scripts. We will review some of the tools to write PowerShell scripts that other people can use, even if they have never used PowerShell before.