Tests/Pester.MaliciousContent.Tests.ps1

function RunRuleForCommand
{
    param([String] $Command)

    $outputPath = Join-Path $env:TEMP ([IO.Path]::GetRandomFileName() + ".ps1")
    try
    {
        Set-Content -Path $outputPath -Value $Command

        Invoke-ScriptAnalyzer -Path $outputPath `
            -CustomizedRulePath (Resolve-Path $PSScriptRoot\..\MaliciousContentRules.psm1) `
            -ExcludeRule PS*

    }
    finally
    {
        Remove-Item $outputPath
    }
}

Describe "Tests for validating malicious content techniques" {

    It "Should detect suspicious commands (Invoke-Expression)" {
        $result = RunRuleForCommand '$content = "A"; Invoke-Expression $content'
        $result.RuleName | Should be "MaliciousContent.DangerousCommand"
    }

    It "Should detect suspicious commands (Add-Type)" {
        $result = RunRuleForCommand '$content = "A"; Add-Type $content'
        $result.RuleName | Should be "MaliciousContent.DangerousCommand"
    }

    It "Should detect dynamic script block" {
        $result = RunRuleForCommand '& ([ScriptBlock]::Create("1+2"))'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocation"
    }

    It "Should detect dynamic script block - NewScriptBlock" {
        $result = RunRuleForCommand '$a = "A"; $ExecutionContext.InvokeCommand.NewScriptBlock($a)'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect dynamic script block - InvokeScript" {
        $result = RunRuleForCommand '$a = "A"; $ExecutionContext.InvokeCommand.InvokeScript($a)'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect dynamic script block - ExpandString" {
        $result = RunRuleForCommand '$a = "A"; $ExecutionContext.InvokeCommand.ExpandString($a)'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect dynamic script block - GetPowerShell" {
        $result = RunRuleForCommand '$a = { "A" }.GetPowerShell()'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect dynamic method invocation" {
        $result = RunRuleForCommand '[Object]::("Equ" + "als")(1, 2)'
        $result.RuleName | Should be "MaliciousContent.DynamicMethodInvocation"
    }

    It "Should detect dynamic method invocation variable" {
        $result = RunRuleForCommand '$method = "Equals"; [Object]::$method(1, 2)'
        $result.RuleName | Should be "MaliciousContent.DynamicMethodInvocation"
    }

    It "Should detect access to [Type] via cast" {
        $result = RunRuleForCommand '$foo = [Type] "Object"'
        $result.RuleName | Should be "MaliciousContent.DynamicTypeConversion"
    }

    It "Should detect access to [Type] via -as" {
        $result = RunRuleForCommand '$foo = "Object" -as [Type]'
        $result.RuleName | Should be "MaliciousContent.DynamicTypeCast"
    }

    It "Should detect dynamic member access variable" {
        $result = RunRuleForCommand '$member = "CommandLine"; [Environment]::$member'
        $result.RuleName | Should be "MaliciousContent.DynamicMemberAccess"
    }

    It "Should detect dynamic member access" {
        $result = RunRuleForCommand '[Environment]::("Command" + "Line")'
        $result.RuleName | Should be "MaliciousContent.DynamicMemberAccess"
    }

    It "Should detect static access on a variable" {
        $result = RunRuleForCommand '$foo = [Environment]; $foo::CommandLine'
        $result.RuleName | Should be "MaliciousContent.DynamicMemberAccess"
    }

    It "Should not flag unobfuscated static access" {
        $result = RunRuleForCommand '[Environment]::CommandLine'
        $result | Should be $null
    }

    It "Should detect suspicious token" {
        $result = RunRuleForCommand '[System.Runtime.InteropServices.Marshal]::ReadIntPtr(0)'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect suspicious token case insenstive" {
        $result = RunRuleForCommand '[system.runtime.interopservices.marshal]::readintptr(0)'
        $result.RuleName | Should be "MaliciousContent.SuspiciousContentText"
    }

    It "Should detect multiple suspicious tokens" {
        $results = RunRuleForCommand '[system.runtime.interopservices.marshal]::readintptr(0); Add-Type Foo'
        ($results.Count -gt 2) | Should be $true
    }

    It "Should detect command indirection - invocation" {
        $result = RunRuleForCommand '$command = "Add-" + "Type"; & $command'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocation"
    }    

    It "Should detect command indirection - new alias" {
        $result = RunRuleForCommand 'New-Alias Foo ("Add-" + "Type")'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAlias"
    }    

    It "Should detect command indirection - import alias" {
        $result = RunRuleForCommand 'Import-Alias c:\temp\aliases.clixml'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationImportAlias"
    }    

    It "Should detect command indirection - Set-Content provider syntax for aliases" {
        $result = RunRuleForCommand '$Alias:blah = ("Add-" + "Type")'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAliasProviderSyntax"
    }    

    It "Should detect command indirection - Set-Content cmdlet on aliases" {
        $result = RunRuleForCommand 'Set-Content alias:\foo -Value ("Add-" + "Type")'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAliasProviderCmdlet"
    }    

    It "Should detect command indirection - New-Item cmdlet on aliases" {
        $result = RunRuleForCommand 'New-Item alias:\foo -Value ("Add-" + "Type")'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAliasProviderCmdlet"
    }    

    It "Should detect command indirection - New-PSDrive for aliases" {
        $result = RunRuleForCommand 'New-PSDrive -Name Secret -PSProvider Alias -Root ""'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAliasNewDrive"
    }

    It "Should detect command indirection - New-PSDrive for aliases with variable" {
        $result = RunRuleForCommand '$drive = "Ali" + "as"; New-PSDrive Secret $drive ""'
        $result.RuleName | Should be "MaliciousContent.DynamicCommandInvocationNewAliasNewDriveVariable"
    }
    
    ## Possible attacks
    ## We've put some non-malicious warnings on an ignore list:
    ## - And they do something (i.e.: insert some lines) to invalidate the whole ignore list
    ## - And they add some new content (maybe under the same parent?) that gets also ignored by the ignore list
    ## We've ignored a non-malicious type conversion (for example), but then they change it to be malicious without
    ## chaging the raw hit. I.e.: $type = [String]; $type.GetProperties() -> $type = [Type]; $type.GetProperties()
}