Photographee.eu - Fotolia
PowerShell attacks in the form of fileless malware continue to grow, but disabling PowerShell is generally not feasible for most organizations. How do you keep critical PowerShell functionality without opening the network up to attack?
PowerShell is deeply entwined with the Windows operating system. It's an essential management tool with incredible reach that attackers often use to launch malicious campaigns. Microsoft has embedded multiple protection mechanisms into the PowerShell environment to address this issue. One of the most effective PowerShell security features is its Constrained Language mode.
What is PowerShell Constrained Language mode?
PowerShell features several language modes that determine the language elements allowable in a specific PowerShell session. You change PowerShell language modes by modifying a property of the session configuration -- or endpoint -- used to create the session. This property is stored in the environment variables as __PSLockdownPolicy. Environment variables are stored in the registry key and can be enforced via registry protections. Whether you access PowerShell remotely via the PSSession cmdlet or locally via the default sessions that appear when you start PowerShell, the session will be subject to the variable housed in this property field.
PowerShell includes four language modes. The default is Full Language mode in all versions of Windows except for Windows RT. In Full Language mode, all cmdlets and other PowerShell language elements may be utilized. Next, Restricted Language mode lets users can run commands including cmdlets, functions, CIM commands and workflows, but not script blocks. Restricted Language mode limits the use of default PowerShell variables. Next, No Language mode only allows the use of PowerShell through the API; you cannot use script text of any form. The final language mode is Constrained Language mode, which allows all cmdlets and PowerShell Language elements; however, it limits them to permitted types to curb the malicious use of PowerShell.
Enabling Constrained Language mode to increase PowerShell security
This tutorial will show you how to enable PowerShell Constrained Language mode on a Windows 10 Enterprise system. This version of Windows 10 is critical to use AppLocker, which allows legitimate scripts to run all PowerShell commands.
In this example, the Windows 10 system is not on a domain; this type of setup is not a requirement for implementing Constrained Language mode, but it makes it easier to follow when all settings are implemented on a single machine. The Windows 10 machine is also configured with User Account Control turned on to make it clear to identify the difference between the administrative and user sessions of PowerShell.
How to set the Constrained Language mode
To start, identify the current language mode with the $ExecutionContext.SessionState.LanguageMode command.
By default, the Windows 10 machine is in Full Language mode.
Next, open a second PowerShell console window as an administrator and enter the command regedit to open the registry editor.
In the registry editor, add the following as a DWORD and set the hexadecimal value to 4:
Go back to the PowerShell console window with administrator privileges and run the gpupdate /force command.
Next, check the language mode in the user PowerShell console by running the $ExecutionContext.SessionState.LanguageMode command. You should get Constrained Language for a result; the administrative PowerShell console will still have Full Language rights.
Test Constrained Language mode
To see Constrained Language mode in action, we will use a function this mode blocks.
In the PowerShell console window running as a user, execute the following command:
The command should be blocked.
In the PowerShell console window running as administrator, execute the same command, which should execute as the administrator and will have Full Language mode permissions.
Copy the command into two .ps1 files, one named allowed.ps1 and one named denied.ps1 at the root folder of the C drive.
From the command prompt running as a user, execute both files. Both will still run, despite the Constrained Language mode settings because the .ps1 script is not part of the session.
You've disabled an attacker's ability to run commands as a user directly from the console, but how do you put a limit on these script files? The answer is AppLocker.
Configure AppLocker to block scripts
AppLocker is an application whitelisting feature built into the enterprise version of Windows 10. It controls which applications and files users can run or have access to, including executables, scripts, MSI Windows Installer files, DLL files and packaged applications such as Microsoft Store apps.
It is possible to set up AppLocker to manage scripts only. AppLocker defines script rules to include only the .ps1, .bat, .cmd, .vbs and .js file formats. If you block a script file by the rules defined under the AppLocker script policy, then the user cannot execute it.
You enable AppLocker through Group Policy. In the PowerShell console running as administrator, run gpedit.msc then go to Local Computer Policy > Computer Configuration > Windows Settings > Security Settings > Application Control Policies > AppLocker.
Complete the following steps:
- Highlight Script Rules and delete all existing rules.
- Right-click on Script Rules and click Create Default Rules.
- Right-click in the right-hand pane and add choose Create New Rule. Click Next.
- Choose Allow. Set the user or group to Everyone. Click Next.
- Choose the Path radio button. Click Next.
- Browse to the C:\Allowed.ps1 file and click Create.
Once you configure the script rules, you will need to enable the Application Identity service. In the PowerShell console running as administrator, run services.msc. Confirm the Application Identity service is set to start automatically. Reboot the computer.
Testing the PowerShell security arrangement
To test the new policy, open a PowerShell console as a user. Confirm Constrained Language mode is active by running the following command:
Next, execute the command:
It should be blocked.
Next, run the following script:
This script should run without issue because it was defined as a trusted script in the AppLocker settings. This will allow commands from Full Language mode to be executed as part of a trusted script.
Attempt to execute the following script:
This PowerShell script should fail because it is not on the AppLocker whitelist.
How to prevent a circumvention attempt
Only later versions of PowerShell support language modes. A savvy attacker can easily bypass these protections by forcing a command to execute via PowerShell 2.0 by using this command:
powershell –version 2 –command [system.console]::WriteLine("test")
This will not work on Windows 10 because the underlying .NET Framework 2.0 is not part of the system.
For other Windows systems, remove the PowerShell version 2 feature set by running the following command:
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2