Wednesday, November 25, 2015

Here is my custom Powershell prompt

Get-Help about_Prompts
<#
LONG DESCRIPTION
    The Windows PowerShell command prompt indicates that Windows PowerShell
    is ready to run a command:

        PS C:\>

    The Windows PowerShell prompt is determined by the built-in Prompt
    function. You can customize the prompt by creating your own Prompt
    function and saving it in your Windows PowerShell profile.
#>

One of my biggest issues with the default prompt is that I work with a lot of nested folders and network shares. It makes the path so long because the path is in there. So I change my prompt to just show the current folder and place the full path in the tittle bar.

One other thing I do is add basic command logging. I would use transcripts, but I don't want something that verbose. So I just save my last command to a text file whenever I run it.

The last thing I so is calculate where in the history the next command will be and add that to my prompt.

Here is my prompt function:

$PSLogPath = ("{0}{1}\Documents\WindowsPowerShell\log\{2:yyyyMMdd}-{3}.log" -f $env:HOMEDRIVE, $env:HOMEPATH,  (Get-Date), $PID)
Add-Content -Value "# $(Get-Date) $env:username $env:computername" -Path $PSLogPath
Add-Content -Value "# $(Get-Location)" -Path $PSLogPath

function prompt
{
    $LastCmd = Get-History -Count 1
    if($LastCmd)
    {
        $lastId = $LastCmd.Id
       
        Add-Content -Value "# $($LastCmd.StartExecutionTime)" -Path $PSLogPath
        Add-Content -Value "$($LastCmd.CommandLine)" -Path $PSLogPath
        Add-Content -Value "" -Path $PSLogPath
    }

    $nextCommand = $lastId + 1
    $currentDirectory = Split-Path (Get-Location) -Leaf
    $host.UI.RawUI.WindowTitle = Get-Location
    "$nextCommand PS:$currentDirectory>"







Monday, November 16, 2015

Powershell: Script injection with ScriptBlock.CheckRestrictedLanguage Method

I just had a post about importing hashtables from files. It basically loads the file into a script block and executes it. 

$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create($content)
$scriptBlock.CheckRestrictedLanguage([string[]]@(), [string[]]@(), $false)
Write-Output (& $scriptBlock

If that sounds dangerous, that is because it is. Your module or script my be trusted, but it may be loading files that are not trusted. This could be a stealthy way for an attacker to use your script.

I took the extra step of using $scriptBlock.CheckRestrictedLanguage([string[]]@(), [string[]]@(), $false) to make sure the hashtable is not containing Powershell commands. There is one important gotcha to be aware of with this command. The arguments are not intuitive. 

Lets take this command. Here is how the function is defined using a C# sample:

public:
void CheckRestrictedLanguage (
        IEnumerable allowedCommands,
        IEnumerable allowedVariables,
        bool allowEnvironmentVariables
)

The first argument is the allowed commands and the second is the allowed variables. One could reasonably assume that a $null value for the allowed commands would mean that nothing is allowed.

If you look at my code, I create an empty string array. That may look like a very strange thing to do and I kind of agree. This is because a $null value indicates that it should allow some default commands to execute. The only way to know this one is to read the documentation very closely. https://msdn.microsoft.com/en-us/library/system.management.automation.scriptblock.checkrestrictedlanguage(v=vs.85).aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-2

By using an empty list of strings, I do not allow any Powershell commands. When importing a hashtable, this is exactly what I want.

Sunday, November 08, 2015

Powershell: Importing hashtable from file or a psd1 file

Have you ever wanted to import a hashtable from a file? A module manifest that is saved in a *.psd1 file is a hashtable. If you ever wanted to read the meta data in it, this trick may help.

You import the contents into a script block, validate the script block, execute it and capture the resulting hashtable into a variable. Here is the sample code below:


$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create($content)
$scriptBlock.CheckRestrictedLanguage([string[]]@(), [string[]]@(), $false)
Write-Output (& $scriptBlock)



If you target a module manifest, you can access all the attributes in it. 

Name                           Value
----                           -----
Copyright                      (c) 2015 Kevin.Marquette. All rights reserved.
CompanyName                    Self
GUID                           6ab379f9-41ed-4c1e-beda-7855d1c1e3c8
Author                         Kevin.Marquette
FunctionsToExport              *
VariablesToExport              *
RootModule                     .\my_module.psm1
AliasesToExport                *
CmdletsToExport                *
ModuleVersion                  1.0.1 

The CheckRestrictedLanguage will throw an error if it finds any powershell commands in the hashtable. Because you are executing code from a un-trusted source in the middle of your script, you should validate it.

There is a second quick and dirty way to do the same thing without the validation. I almost don't want to mention it because it is so dangerous. So if you see this in the wild, know that there is a better way.

$HashTable = Invoke-Expression (Get-Content $Path -raw)

This blindly executes a file as if it was a script. This is just asking to be exploited. Think CSS cross site or SQL injection type of vulnerability.