Quantcast
Channel: clymb3r » clymb3r
Viewing all articles
Browse latest Browse all 9

Cracking Open PowerShell’s Constrained Runspace

$
0
0

Recently at the PowerShell Summit, Lee Holmes and I did a talk on PowerShell security. One of the demonstrations we did showed how to find and exploit a command injection bug in a constrained runspace. I figured I’d write a short blog post on how to use a command injection bug to turn a constrained runspace in to an unconstrained runspace.

For those unaware, constrained runspaces are a pretty neat feature of PowerShell. A constrained runspace is a PowerShell Remoting endpoint that only allows the user to execute whitelisted PowerShell commands. The endpoint can optionally be configured to run these commands under a delegated account (such as an administrator account). This allows non-administrator users to connect to the endpoint and run specific, whitelisted commands, under a more privileged account. This makes constrained runspaces an excellent way to manage systems without requiring unfettered administrator access on every system.

For this blog, I’ve created an easy to install constrained runspace to practice attacking that can be found here: https://github.com/clymb3r/Demos/tree/master/VulnerableConstrainedRunspace. When you connect to this constrained runspace, you will have access to a limit set of commands (some of which are custom functions that I’ve written). Here is the output of “Get-Command” when running in my constrained runspace:

Running Get-Command in a constrained runspace.

Running Get-Command in a constrained runspace.

As you can see, very few commands are available. In a typical enterprise setup, I’d expect to see more commands then this available (and many of them might be custom functions written by the administrators). One thing to check is if the commands exposed allow for command injection. If you’ve read my previous blog post on safe PowerShell coding (http://clymb3r.wordpress.com/2013/09/07/avoiding-powershell-command-injection-unicode-issues/), you’ll know that sometimes programmers concatenate user input (taken from the function parameters) with other data and use the resulting string to call Invoke-Expression (or similar functions).

Below is an example of command injection in the runspace I have provided. The function Get-ChildItemProxy1 is only supposed to execute “Get-ChildItem” with the user supplied path.

The Get-ChildItemProxy1 function. The script is only intended to execute Get-ChildItem but can be made to execute arbitrary code due to a command injection bug.

The Get-ChildItemProxy1 function. The script is only intended to execute Get-ChildItem but can be made to execute arbitrary code due to a command injection bug.

Due to a programming error, an attacker is able to inject arbitrary commands to execute. In the example below, the attacker forces the function to also execute “Get-Process”:

Basic PowerShell command injection in a constrained runspace.

Basic PowerShell command injection in a constrained runspace.

This is obviously extremely powerful, but it’s also annoying to use. For every command you wish to execute you must properly escape your input as to properly exploit the command injection vulnerability. Can we do better? Of course!

If you run Get-Command, you’ll notice one of the properties that functions/cmdlets have is a “Visibility” property. In a normal PowerShell session, all functions are marked “Public”. When you setup a constrained runspace PowerShell sets the visibility of all non-whitelisted functions to “Private”. This prevents you from using the function/cmdlet from the constrained runspace, but commands that are whitelisted (such as Get-ChildItemProxy1) are allowed to call “Private” functions.

All you need to do to turn a constrained runspace in to an unconstrained runspace is mark all functions as “Public”. How do you do that? Check this out:

Get-ChildItemProxy1 "c:\'; Get-Command | ForEach-Object { `$_.Visibility='Public' };'"

This command once again exploits the command injection. It uses command injection to call “Get-Command”. Remember that because “Get-Command” is being called from inside a whitelisted function, it is able to see all functions/cmdlets (including those that have been marked “Private”). For each command, it sets the “Visibility” to “Public”. Now if you run “Get-Command”, you’ll see you have significantly more commands available to use.

How to turn a constrained runspace in to an unconstrained runspace with PowerShell command injection.

How to turn a constrained runspace in to an unconstrained runspace with PowerShell command injection.

Thanks to Lee Holmes (@Lee_Holmes) for showing me some of these tricks!



Viewing all articles
Browse latest Browse all 9

Trending Articles