History has conclusively demonstrated that cyberattackers will take advantage of any perceived weakness in a system in order to gain access to a system. Although some have downplayed the idea that PowerShell can be used as a tool for attacking a system, there have been quite a few documented instances of attackers doing just that. Since PowerShell can sometimes be of interest to those who have bad intent, it is a good idea to harden PowerShell whenever possible in an effort to prevent it from being exploited. One option for doing so is to enable constrained language mode within your PowerShell sessions.
What is constrained language mode?
So what is the constrained language mode? Simply put, the mode is a tool that can be used to limit the types of commands that can be executed within a PowerShell session. Let me give you a really simple example.
If you look at the figure below, you can see that I have opened two separate PowerShell windows. The top window is completely normal. As you can see, I entered a mathematical expression into this window. This expression asks for the square root of 64. PowerShell returned a value of 8, which is the correct answer. The figure’s lower PowerShell window has the constrained language mode enabled. I entered exactly the same mathematical expression into this window, but received an error message telling me that PowerShell was unable to invoke the method.
OK, so what’s really going on here? How come the command failed in one window, but succeeded in another? The simple explanation is that constrained language mode allows you to continue using basic PowerShell cmdlets but blocks commands that can be used to interact with various APIs. The reason why the mathematical expression shown in the previous figure produced an error is because I was attempting to use .NET. Had I entered a simpler expression such as 2+2, the operation would have been allowed.
There is a long list of items that PowerShell either limits or blocks when constrained language mode is enabled. I won’t bore you with the full list, but some of the more noteworthy items include:
• COM objects
• Unapproved .NET types
• XAML based workflows
• PowerShell classes
Setting PowerShell’s constrained language mode
If you want to determine which language mode PowerShell is currently using, enter this command:
As you can see in the screenshot below, PowerShell is set to use full language mode, which means that constrained language mode is not enabled.
If you decide that you want to enable constrained delegation, you can do so by using a variation of the command shown above. Simply enter the command as before, but append =”ConstrainedLanguage”. The full command looks like this:
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
You can see what a switch from full language to constrained language looks like in the screenshot below.
PowerShell’s Achilles’ heel
The problem with using constrained delegation as a security feature is that depending on how it is implemented, it can be relatively easy for an attacker to get around it. The reason for this is that PowerShell’s language mode is session specific. Let me show you what I mean.
If you look at the screenshot below, you can see that I have two PowerShell windows open. I set the language mode of the window on the left to Constrained Language. I then verified the language mode to make sure that PowerShell really had been properly set to use constrained language. Next, I opened up the window on the right and checked PowerShell’s language mode. The PowerShell session running in that window is using full language, even though the session within the window on the left remains limited to using constrained language. In other words, if an attacker wants to gain access to the full language, then they need only to open another PowerShell session.
Although constrained language mode can improve security, its use presents two big problems. First, as previously noted, constrained language mode is easy to circumvent because it is session specific. If an attacker wants to avoid constrained language mode, they can simply launch a new PowerShell session.
The second problem with using constrained language mode is that it can break your existing PowerShell scripts. I showed you an example earlier of a mathematical expression that could not be evaluated because doing so required the use of .NET. Just imagine if that expression had been a part of a script.
The trick to making the use of constrained language practical is to accept the idea that constrained language mode is not designed to be used by itself. Instead, constrained language mode is designed to be used in conjunction with Device Guard User Mode Code Integrity (UMCI).
For those who might not be familiar with UMCI, it is a tool that allows administrators to set policies describing the code that is allowed to run on the system. Because Device Guard runs on a system wide basis, it is ideal for enforcing the use of constrained language mode. An attacker will not be able to circumvent constrained language mode by opening a new PowerShell session, because Device Guard policies apply to the system as a whole, not solely to the current PowerShell session.
A powerful tool
Constrained language mode can be a very powerful tool for locking down PowerShell in an effort to prevent it from being used for nefarious purposes. Even so, it is completely ineffective by itself, because an attacker could bypass it by launching another PowerShell session, or simply disable it by entering a single command. In spite of its weaknesses however, constrained language mode can greatly enhance PowerShell’s security when it is used in conjunction with Device Guard User Mode Code Integrity.
Featured image: Shutterstock