Disable the debugger when in system lock-down mode (#9645)
Disable the debugger when in system lock-down mode Fixing master for CVE-2019-0733
This commit is contained in:
parent
dbbb5110bf
commit
bb726da6fd
|
@ -1132,12 +1132,16 @@ namespace System.Management.Automation
|
|||
internal Breakpoint NewCommandBreakpoint(string path, string command, ScriptBlock action)
|
||||
{
|
||||
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||
|
||||
CheckForBreakpointSupport();
|
||||
return AddCommandBreakpoint(new CommandBreakpoint(path, pattern, command, action));
|
||||
}
|
||||
|
||||
internal Breakpoint NewCommandBreakpoint(string command, ScriptBlock action)
|
||||
{
|
||||
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||
|
||||
CheckForBreakpointSupport();
|
||||
return AddCommandBreakpoint(new CommandBreakpoint(null, pattern, command, action));
|
||||
}
|
||||
|
||||
|
@ -1175,6 +1179,7 @@ namespace System.Management.Automation
|
|||
{
|
||||
Diagnostics.Assert(path != null, "caller to verify path is not null");
|
||||
|
||||
CheckForBreakpointSupport();
|
||||
return AddLineBreakpoint(new LineBreakpoint(path, line, action));
|
||||
}
|
||||
|
||||
|
@ -1182,6 +1187,7 @@ namespace System.Management.Automation
|
|||
{
|
||||
Diagnostics.Assert(path != null, "caller to verify path is not null");
|
||||
|
||||
CheckForBreakpointSupport();
|
||||
return AddLineBreakpoint(new LineBreakpoint(path, line, column, action));
|
||||
}
|
||||
|
||||
|
@ -1201,11 +1207,13 @@ namespace System.Management.Automation
|
|||
|
||||
internal Breakpoint NewVariableBreakpoint(string path, string variableName, VariableAccessMode accessMode, ScriptBlock action)
|
||||
{
|
||||
CheckForBreakpointSupport();
|
||||
return AddVariableBreakpoint(new VariableBreakpoint(path, variableName, accessMode, action));
|
||||
}
|
||||
|
||||
internal Breakpoint NewVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action)
|
||||
{
|
||||
CheckForBreakpointSupport();
|
||||
return AddVariableBreakpoint(new VariableBreakpoint(null, variableName, accessMode, action));
|
||||
}
|
||||
|
||||
|
@ -1761,12 +1769,6 @@ namespace System.Management.Automation
|
|||
originalLanguageMode = _context.LanguageMode;
|
||||
_context.LanguageMode = PSLanguageMode.FullLanguage;
|
||||
}
|
||||
else if (System.Management.Automation.Security.SystemPolicy.GetSystemLockdownPolicy() ==
|
||||
System.Management.Automation.Security.SystemEnforcementMode.Enforce)
|
||||
{
|
||||
// If there is a system lockdown in place, enforce it
|
||||
originalLanguageMode = Utils.EnforceSystemLockDownLanguageMode(this._context);
|
||||
}
|
||||
|
||||
// Update the prompt to the debug prompt
|
||||
if (hadDefaultPrompt)
|
||||
|
@ -2060,6 +2062,17 @@ namespace System.Management.Automation
|
|||
{
|
||||
lock (_syncObject)
|
||||
{
|
||||
// Disable script debugger when in system lock down mode
|
||||
if (IsSystemLockedDown)
|
||||
{
|
||||
if (_context._debuggingMode != (int)InternalDebugMode.Disabled)
|
||||
{
|
||||
_context._debuggingMode = (int)InternalDebugMode.Disabled;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case InternalDebugMode.InPushedStop:
|
||||
|
@ -2086,6 +2099,24 @@ namespace System.Management.Automation
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsSystemLockedDown
|
||||
{
|
||||
get
|
||||
{
|
||||
return (System.Management.Automation.Security.SystemPolicy.GetSystemLockdownPolicy() ==
|
||||
System.Management.Automation.Security.SystemEnforcementMode.Enforce);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckForBreakpointSupport()
|
||||
{
|
||||
if (IsSystemLockedDown)
|
||||
{
|
||||
// Local script debugging is not supported in locked down mode
|
||||
throw new PSNotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
#region Enable debug stepping
|
||||
|
||||
[Flags]
|
||||
|
@ -2323,6 +2354,15 @@ namespace System.Management.Automation
|
|||
{
|
||||
lock (_syncObject)
|
||||
{
|
||||
// Restrict local script debugger mode when in system lock down.
|
||||
// DebugModes enum flags provide a combination of values. To disable local script debugging
|
||||
// we have to disallow 'LocalScript' and 'Default' flags and only allow 'None' or 'RemoteScript'
|
||||
// flags exclusively. This allows only no debugging 'None' or remote debugging 'RemoteScript'.
|
||||
if (IsSystemLockedDown && (mode != DebugModes.None) && (mode != DebugModes.RemoteScript))
|
||||
{
|
||||
mode = DebugModes.RemoteScript;
|
||||
}
|
||||
|
||||
base.SetDebugMode(mode);
|
||||
|
||||
if (!CanEnableDebugger)
|
||||
|
|
|
@ -18,10 +18,89 @@ try
|
|||
$defaultParamValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:Skip"] = !$IsWindows
|
||||
|
||||
Describe "Trusted module on locked down machine should not expose private functions to script debugger command processing" -Tags 'CI','RequireAdminOnWindows' {
|
||||
Describe "Local script debugger is disabled in system lock down mode" -Tags 'CI','RequireAdminOnWindows' {
|
||||
|
||||
BeforeAll {
|
||||
|
||||
# Invoke-LanguageModeTestingSupportCmdlet definition
|
||||
$languageModeCmdletDef = @'
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Management.Automation;
|
||||
|
||||
/// <summary>Adds a new type to the Application Domain</summary>
|
||||
[Cmdlet("Invoke", "LanguageModeTestingSupportCmdlet")]
|
||||
public sealed class InvokeLanguageModeTestingSupportCmdlet : PSCmdlet
|
||||
{
|
||||
[Parameter()]
|
||||
public SwitchParameter EnableFullLanguageMode
|
||||
{
|
||||
get { return enableFullLanguageMode; }
|
||||
set { enableFullLanguageMode = value; }
|
||||
}
|
||||
private SwitchParameter enableFullLanguageMode;
|
||||
|
||||
[Parameter()]
|
||||
public SwitchParameter SetLockdownMode
|
||||
{
|
||||
get { return setLockdownMode; }
|
||||
set { setLockdownMode = value; }
|
||||
}
|
||||
private SwitchParameter setLockdownMode;
|
||||
|
||||
[Parameter()]
|
||||
public SwitchParameter RevertLockdownMode
|
||||
{
|
||||
get { return revertLockdownMode; }
|
||||
set { revertLockdownMode = value; }
|
||||
}
|
||||
private SwitchParameter revertLockdownMode;
|
||||
|
||||
protected override void BeginProcessing()
|
||||
{
|
||||
if(enableFullLanguageMode)
|
||||
{
|
||||
SessionState.LanguageMode = PSLanguageMode.FullLanguage;
|
||||
}
|
||||
|
||||
if(setLockdownMode)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", EnvironmentVariableTarget.Machine);
|
||||
}
|
||||
|
||||
if(revertLockdownMode)
|
||||
{
|
||||
Environment.SetEnvironmentVariable("__PSLockdownPolicy", null, EnvironmentVariableTarget.Machine);
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
if (-not (Get-Command Invoke-LanguageModeTestingSupportCmdlet -ea Ignore))
|
||||
{
|
||||
$languageModeModuleName = "LanguageModeModule"
|
||||
$modulePath = [System.IO.Path]::GetFileNameWithoutExtension([IO.Path]::GetRandomFileName())
|
||||
$script:moduleDirectory = join-path "$PSScriptRoot\$modulePath" $languageModeModuleName
|
||||
if (-not (Test-Path $moduleDirectory))
|
||||
{
|
||||
$null = New-Item -ItemType Directory $moduleDirectory -Force
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Add-Type -TypeDefinition $languageModeCmdletDef -OutputAssembly $moduleDirectory\TestCmdletForConstrainedLanguage.dll -ErrorAction Ignore
|
||||
} catch {}
|
||||
|
||||
Import-Module -Name $moduleDirectory\TestCmdletForConstrainedLanguage.dll
|
||||
}
|
||||
|
||||
# Debugger test type definition
|
||||
$debuggerTestTypeDef = @'
|
||||
using System;
|
||||
|
@ -33,29 +112,14 @@ try
|
|||
public class DebuggerTester
|
||||
{
|
||||
private Runspace _runspace;
|
||||
private readonly string _privateFnName;
|
||||
|
||||
[Flags]
|
||||
public enum TestResults
|
||||
{
|
||||
NoResult = 0x0,
|
||||
DebuggerStopHandled = 0x1,
|
||||
PrivateFnFound = 0x2
|
||||
};
|
||||
|
||||
public TestResults TestResult
|
||||
public int DebuggerStopHitCount
|
||||
{
|
||||
private set;
|
||||
get;
|
||||
}
|
||||
|
||||
public Exception ScriptException
|
||||
{
|
||||
private set;
|
||||
get;
|
||||
}
|
||||
|
||||
public DebuggerTester(Runspace runspace, string privateFnName)
|
||||
public DebuggerTester(Runspace runspace)
|
||||
{
|
||||
if (runspace.Debugger == null)
|
||||
{
|
||||
|
@ -63,363 +127,160 @@ try
|
|||
}
|
||||
|
||||
_runspace = runspace;
|
||||
_privateFnName = privateFnName;
|
||||
_runspace.Debugger.DebuggerStop += (sender, args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Within the debugger stop handler, make sure trusted private functions are not accessible.
|
||||
string commandText = string.Format(@"Get-Command ""{0}""", _privateFnName);
|
||||
PSCommand command = new PSCommand();
|
||||
command.AddCommand(new Command(commandText, true));
|
||||
PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
|
||||
|
||||
_runspace.Debugger.ProcessCommand(command, output);
|
||||
if ((output.Count > 0) && (output[0].BaseObject is CommandInfo))
|
||||
{
|
||||
TestResult |= TestResults.PrivateFnFound;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ScriptException = e;
|
||||
System.Console.WriteLine(e.Message);
|
||||
}
|
||||
TestResult |= TestResults.DebuggerStopHandled;
|
||||
DebuggerStopHitCount += 1;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$modulePath = Join-Path $TestDrive Modules
|
||||
if (Test-Path -Path $modulePath)
|
||||
{
|
||||
try { Remove-Item -Path $modulePath -Recurse -Force -ErrorAction SilentlyContinue } catch { }
|
||||
}
|
||||
$script = @'
|
||||
"Hello"
|
||||
Wait-Debugger
|
||||
"Goodbye"
|
||||
'@
|
||||
$scriptFilePath = Join-Path $TestDrive TScript.ps1
|
||||
$script > $scriptFilePath
|
||||
|
||||
# Trusted module
|
||||
$trustedModuleName = "TrustedModule_System32"
|
||||
$trustedModuleDirectory = Join-Path $modulePath $trustedModuleName
|
||||
New-Item -ItemType Directory -Path $trustedModuleDirectory -Force -ErrorAction SilentlyContinue
|
||||
$trustedModuleFilePath = Join-Path $trustedModuleDirectory "$($trustedModuleName).psm1"
|
||||
$trustedManifestFilePath = Join-Path $trustedModuleDirectory "$($trustedModuleName).psd1"
|
||||
@'
|
||||
function PublicFn {
|
||||
Write-Output PrivateFn "PublicFn"
|
||||
}
|
||||
|
||||
function PrivateFn {
|
||||
param ([string] $msg)
|
||||
|
||||
Write-Output $msg
|
||||
}
|
||||
'@ > $trustedModuleFilePath
|
||||
$modManifest = "@{ ModuleVersion = '1.0'" + ("; RootModule = '{0}'" -f $trustedModuleFilePath) + "; FunctionsToExport = 'PublicFn' }"
|
||||
$modManifest > $trustedManifestFilePath
|
||||
|
||||
# Create test runspace
|
||||
[runspace] $runspace = [runspacefactory]::CreateRunspace()
|
||||
$runspace.Open()
|
||||
|
||||
# Create debugger test object
|
||||
# Define debugger test type
|
||||
Add-Type -TypeDefinition $debuggerTestTypeDef
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
|
||||
if ($runspace -ne $null) { $runspace.Dispose() }
|
||||
}
|
||||
|
||||
It "Verifies that private trusted module function is not available in script debugger" {
|
||||
|
||||
# Run debugger access test
|
||||
$debuggerTester = [TestRunner.DebuggerTester]::new($runspace, "PrivateFn")
|
||||
|
||||
# Script to invoke the script debugger so that $debuggerTester can handle
|
||||
# the debugger stop event and test for access of private functions within the
|
||||
# script debugger command processor.
|
||||
$script = @'
|
||||
Import-Module -Name HelpersSecurity
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
Import-Module -Name {0} -Force
|
||||
Set-PSBreakpoint -Command PublicFn
|
||||
PublicFn
|
||||
'@ -f $trustedManifestFilePath
|
||||
|
||||
[powershell] $ps = [powershell]::Create()
|
||||
$ps.Runspace = $runspace
|
||||
|
||||
try
|
||||
if (($script:moduleDirectory -ne $null) -and (Test-Path $script:moduleDirectory))
|
||||
{
|
||||
$ps.AddScript($script).BeginInvoke()
|
||||
|
||||
# Wait for debugger test result for up to ten seconds
|
||||
$count = 0
|
||||
while (($debuggerTester.TestResult -eq 0) -and ($count++ -lt 40))
|
||||
{
|
||||
Start-Sleep -Milliseconds 250
|
||||
}
|
||||
try { Remove-Item -Path $moduleDirectory -Recurse -Force -ErrorAction SilentlyContinue } catch { }
|
||||
}
|
||||
finally
|
||||
{
|
||||
# Revert lockdown
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
# Verify that PrivateFn function name is not accessible
|
||||
$debuggerTester.TestResult | Should -Match "DebuggerStopHandled"
|
||||
$debuggerTester.TestResult | Should -Not -Match "PrivateFnFound"
|
||||
$debuggerTester.ScriptException | Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Cross language debugger get-item commands should not have access to FullLanguage trusted functions through provider" -Tags 'Feature','RequireAdminOnWindows' {
|
||||
|
||||
BeforeAll {
|
||||
|
||||
# Trusted module that will always run in FullLanguage mode
|
||||
$scriptModuleName = "ImportTrustedModuleForTestA_System32"
|
||||
$moduleFilePath = Join-Path $TestDrive ($scriptModuleName + ".psm1")
|
||||
$script = @'
|
||||
function PublicFn { "PublicFn"; PrivateFn }
|
||||
function PrivateFn { "PrivateFn" }
|
||||
|
||||
Export-ModuleMember -Function PublicFn
|
||||
'@
|
||||
$script > $moduleFilePath
|
||||
|
||||
# Import and run module function script
|
||||
$scriptIM = @'
|
||||
Import-Module -Name {0} -Force
|
||||
$null = Set-PSBreakpoint -command PublicFn
|
||||
PublicFn
|
||||
'@ -f $moduleFilePath
|
||||
|
||||
# Debugger stop event handler object.
|
||||
$type = @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Runspaces;
|
||||
|
||||
public class DebuggerStopEventHandler
|
||||
{
|
||||
private Runspace _runspace;
|
||||
public object GetItemResult
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public object GetChildItemResult
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public object CopyItemResult
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public object FunctionVariableResult
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public object RenameItemResult
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
public DebuggerStopEventHandler(Runspace runspace)
|
||||
{
|
||||
_runspace = runspace;
|
||||
_runspace.Debugger.DebuggerStop += (sender, args) =>
|
||||
{
|
||||
var debugger = sender as Debugger;
|
||||
|
||||
PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
|
||||
PSCommand command = new PSCommand();
|
||||
|
||||
command.AddScript(@"Get-Item -Path function:\PrivateFn 2>&1");
|
||||
debugger.ProcessCommand(command, output);
|
||||
GetItemResult = (output.Count > 0) ? (output[0].BaseObject) : null;
|
||||
|
||||
command.Clear();
|
||||
output.Clear();
|
||||
command.AddScript(@"Get-ChildItem -Path function:\PrivateFn 2>&1");
|
||||
debugger.ProcessCommand(command, output);
|
||||
GetChildItemResult = (output.Count > 0) ? (output[0].BaseObject) : null;
|
||||
|
||||
command.Clear();
|
||||
output.Clear();
|
||||
command.AddScript(@"Copy-Item -Path function:\PrivateFn -Destination function:\MyPrivateFn 2>&1");
|
||||
debugger.ProcessCommand(command, output);
|
||||
CopyItemResult = (output.Count > 0) ? (output[0].BaseObject) : null;
|
||||
|
||||
command.Clear();
|
||||
output.Clear();
|
||||
command.AddScript(@"${function:\PrivateFn}");
|
||||
debugger.ProcessCommand(command, output);
|
||||
FunctionVariableResult = (output.Count > 0) ? (output[0].BaseObject): null;
|
||||
|
||||
command.Clear();
|
||||
output.Clear();
|
||||
command.AddScript(@"Rename-Item -Path function:\PrivateFn -NewName function:\MyPrivateFn -Passthru 2>&1");
|
||||
debugger.ProcessCommand(command, output);
|
||||
RenameItemResult = (output.Count > 0) ? (output[0].BaseObject) : null;
|
||||
};
|
||||
}
|
||||
public void Reset() { GetItemResult = null; GetChildItemResult = null; CopyItemResult = null; }
|
||||
}
|
||||
'@
|
||||
|
||||
try { Add-Type -TypeDefinition $type } catch { }
|
||||
|
||||
# Create runspace and debugger event handler
|
||||
[runspace] $rs = [runspacefactory]::CreateRunspace($host)
|
||||
$rs.Open()
|
||||
$rs.Debugger.SetDebugMode(@('LocalScript','RemoteScript'))
|
||||
$debuggerStopHandler = [DebuggerStopEventHandler]::New($rs)
|
||||
|
||||
# Create PowerShell to run module script
|
||||
[powershell] $ps = [powershell]::Create()
|
||||
$ps.Runspace = $rs
|
||||
$ps.AddScript($scriptIM)
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
|
||||
if ($rs -ne $null) { $rs.Dispose() }
|
||||
if ($ps -ne $null) { $ps.Dispose() }
|
||||
}
|
||||
|
||||
It "Verifies that same language mode trusted public functions *are* accessible from debugger through Get-Item, Get-ChildItem, Copy-Item, Rename-Item, Variable" {
|
||||
|
||||
# Test
|
||||
$results = $ps.Invoke()
|
||||
|
||||
# Results. Only PublicFn is returned since PrivateFn is renamed.
|
||||
$results[0] | Should Be "PublicFn"
|
||||
|
||||
# Expected Get-Item function:\PrivateFn returns FunctionInfo object
|
||||
($debuggerStopHandler.GetItemResult -is [System.Management.Automation.FunctionInfo]) | Should Be $true
|
||||
|
||||
# Expected Get-ChildItem function:\PrivateFn returns FunctionInfo object
|
||||
($debuggerStopHandler.GetChildItemResult -is [System.Management.Automation.FunctionInfo]) | Should Be $true
|
||||
|
||||
# Expected Copy-Item function:\PrivateFn succeeds with no error output
|
||||
$debuggerStopHandler.CopyItemResult | Should Be $null
|
||||
|
||||
# Expected function variable succeeds
|
||||
($debuggerStopHandler.FunctionVariableResult -is [scriptblock]) | Should Be $true
|
||||
|
||||
# Expected Rename-Item function:\PrivateFn returns FunctionInfo object
|
||||
($debuggerStopHandler.RenameItemResult -is [System.Management.Automation.FunctionInfo]) | Should Be $true
|
||||
}
|
||||
|
||||
It "Verifies that cross language mode trusted public functions *are not* accessible through Get-Item, Get-ChildItem, Copy-Item, Rename-Item, Variable" {
|
||||
|
||||
# Test
|
||||
$debuggerStopHandler.Reset()
|
||||
try
|
||||
{
|
||||
$rs.LanguageMode = "ConstrainedLanguage"
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
|
||||
$results = $ps.Invoke()
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
# Results
|
||||
$results[0] | Should Be "PublicFn"
|
||||
$results[1] | Should Be "PrivateFn"
|
||||
|
||||
# Expected Get-Item function:\PrivateFn returns error
|
||||
$debuggerStopHandler.GetItemResult.FullyQualifiedErrorId | Should Be "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand"
|
||||
|
||||
# Expected Get-ChildItem function:\PrivateFn returns error
|
||||
$debuggerStopHandler.GetChildItemResult.FullyQualifiedErrorId | Should Be "PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand"
|
||||
|
||||
# Expected Copy-Item fails with error output
|
||||
$debuggerStopHandler.CopyItemResult.FullyQualifiedErrorId | Should Be "PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand"
|
||||
|
||||
# Expected function variable fails
|
||||
$debuggerStopHandler.FunctionVariableResult | Should Be $null
|
||||
|
||||
# Expected Rename-Item function:\PrivateFn fails with error
|
||||
$debuggerStopHandler.RenameItemResult.FullyQualifiedErrorId | Should Be "PathNotFound,Microsoft.PowerShell.Commands.RenameItemCommand"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Cross language debugger Action scripts should not have access to FullLanguage trusted functions through provider" -Tags 'Feature','RequireAdminOnWindows' {
|
||||
|
||||
BeforeAll {
|
||||
|
||||
# Trusted script that will always run in FullLanguage mode
|
||||
$scriptFileName = "TrustedScriptForTestB_System32"
|
||||
$scriptFilePath = Join-Path $TestDrive ($scriptFileName + ".ps1")
|
||||
$script = @'
|
||||
function PublicFn { PrivateFn -typeDef 'public class Hello { public new static void ToString() { System.Console.WriteLine("Hello!"); } }'; [Hello]::ToString(); }
|
||||
function PrivateFn { param ([string]$typeDef) Add-Type -TypeDefinition $typeDef }
|
||||
PublicFn
|
||||
"Complete"
|
||||
'@
|
||||
$script > $scriptFilePath
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
|
||||
Get-PSBreakpoint | Remove-PSBreakpoint
|
||||
}
|
||||
|
||||
It "Verifies that debugger stop Action scriptblock cannot access PrivateFn" {
|
||||
It "Verifies that Set-PSBreakpoint Line is disabled on locked down system" {
|
||||
|
||||
try
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
|
||||
# Set breakpoint on script
|
||||
Set-PSBreakpoint -Script $scriptFilePath -Line 4 -Action {
|
||||
& (Get-Item -Path function:\PrivateFn) -typeDef @'
|
||||
public class Foo {
|
||||
public new static void ToString() {
|
||||
System.Console.WriteLine("pwnd!");
|
||||
}
|
||||
}
|
||||
'@
|
||||
}
|
||||
|
||||
# Run script
|
||||
& $scriptFilePath
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
# Verify that Action scriptblock did not create Foo type using PrivateFn
|
||||
[Foo]::ToString()
|
||||
Set-PSBreakpoint -Script $scriptFilePath -Line 1
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.FullyQualifiedErrorId | Should Be "TypeNotFound"
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should Be 'NotSupported,Microsoft.PowerShell.Commands.SetPSBreakpointCommand'
|
||||
}
|
||||
|
||||
It "Verifies that Set-PSBreakpoint Statement is disabled on locked down system" {
|
||||
|
||||
try
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
|
||||
Set-PSBreakpoint -Script $scriptFilePath -Line 1 -Column 1
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should Be 'NotSupported,Microsoft.PowerShell.Commands.SetPSBreakpointCommand'
|
||||
}
|
||||
|
||||
It "Verifies that Set-PSBreakpoint Command is disabled on locked down system" {
|
||||
|
||||
try
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
|
||||
Set-PSBreakpoint -Command $scriptFilePath
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should Be 'NotSupported,Microsoft.PowerShell.Commands.SetPSBreakpointCommand'
|
||||
}
|
||||
|
||||
It "Verifies that Set-PSBreakpoint Variable is disabled on locked down system" {
|
||||
|
||||
try
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
|
||||
Set-PSBreakpoint -Variable HelloVar
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should Be 'NotSupported,Microsoft.PowerShell.Commands.SetPSBreakpointCommand'
|
||||
}
|
||||
|
||||
It "Verifies that Wait-Debugger is disabled on locked down system" {
|
||||
|
||||
try
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
|
||||
# Create test runspace
|
||||
[runspace] $runspace = [runspacefactory]::CreateRunspace()
|
||||
$runspace.Open()
|
||||
|
||||
# Attach TestRuner.DebuggerTester DebugStop event handler to runspace
|
||||
$debuggerTester = [TestRunner.DebuggerTester]::new($runspace)
|
||||
|
||||
# Run $scriptFilePath script with 'Wait-Debugger' in locked down mode
|
||||
[powershell] $ps = [powershell]::Create()
|
||||
$ps.Runspace = $runspace
|
||||
$null = $ps.AddScript('"Hello"; Wait-Debugger; "Goodbye"').Invoke()
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
if ($runspace -ne $null) { $runspace.Dispose() }
|
||||
if ($ps -ne $null) { $ps.Dispose() }
|
||||
}
|
||||
|
||||
# Debugger should not have been active in lockdown mode
|
||||
$debuggerTester.DebuggerStopHitCount | Should Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($defaultParamValues -ne $null)
|
||||
if ($null -ne $defaultParamValues)
|
||||
{
|
||||
$Global:PSDefaultParameterValues = $defaultParamValues
|
||||
}
|
||||
|
|
|
@ -365,116 +365,6 @@ try
|
|||
}
|
||||
}
|
||||
|
||||
Describe "Script debugging in constrained language" -Tags 'Feature','RequireAdminOnWindows' {
|
||||
|
||||
It "Verifies that a debugging breakpoint cannot be set in constrained language and no system lockdown" {
|
||||
|
||||
try
|
||||
{
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
function MyDebuggerFunction {}
|
||||
|
||||
Set-PSBreakpoint -Command MyDebuggerFunction
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should -BeExactly "CannotSetBreakpointInconsistentLanguageMode,Microsoft.PowerShell.Commands.SetPSBreakpointCommand"
|
||||
}
|
||||
|
||||
It "Verifies that a debugging breakpoint can be set in constrained language with system lockdown" {
|
||||
|
||||
try
|
||||
{
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
|
||||
function MyDebuggerFunction2 {}
|
||||
$Global:DebuggingOk = $null
|
||||
$null = Set-PSBreakpoint -Command MyDebuggerFunction2 -Action { $Global:DebuggingOk = "DebuggingOk" }
|
||||
MyDebuggerFunction2
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$Global:DebuggingOk | Should -BeExactly "DebuggingOk"
|
||||
}
|
||||
|
||||
It "Verifies that debugger commands do not run in full language mode when system is locked down" {
|
||||
|
||||
try
|
||||
{
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
|
||||
function MyDebuggerFunction3 {}
|
||||
|
||||
& {
|
||||
$null = Set-PSBreakpoint -Command MyDebuggerFunction3 -Action { $Global:dbgResult = [object]::Equals("A", "B") }
|
||||
$restoreEAPreference = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Stop"
|
||||
MyDebuggerFunction3
|
||||
}
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedError = $_
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode
|
||||
if ($restoreEAPreference -ne $null) { $ErrorActionPreference = $restoreEAPreference }
|
||||
}
|
||||
|
||||
$expectedError.FullyQualifiedErrorId | Should -BeExactly "CannotSetBreakpointInconsistentLanguageMode,Microsoft.PowerShell.Commands.SetPSBreakpointCommand"
|
||||
}
|
||||
|
||||
It "Verifies that debugger command injection is blocked in system lock down" {
|
||||
|
||||
$trustedScriptContent = @'
|
||||
function Trusted
|
||||
{
|
||||
param ($UserInput)
|
||||
|
||||
Add-Type -TypeDefinition $UserInput
|
||||
try { $null = New-Object safe_738057 -ErrorAction Ignore } catch {}
|
||||
try { $null = New-Object pwnd_738057 -ErrorAction Ignore } catch {}
|
||||
}
|
||||
|
||||
Trusted -UserInput 'public class safe_738057 { public safe_738057() { System.Environment.SetEnvironmentVariable("pwnd_738057", "False"); } }'
|
||||
|
||||
"Hello World"
|
||||
'@
|
||||
$trustedFile = Join-Path $TestDrive CommandInjectionDebuggingBlocked_System32.ps1
|
||||
|
||||
try
|
||||
{
|
||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||
|
||||
Set-Content $trustedScriptContent -Path $trustedFile
|
||||
$env:pwnd_738057 = "False"
|
||||
Set-PSBreakpoint -Script $trustedFile -Line 12 -Action { Trusted -UserInput 'public class pwnd_738057 { public pwnd_738057() { System.Environment.SetEnvironmentVariable("pwnd_738057", "Pwnd"); } }' }
|
||||
& $trustedFile
|
||||
}
|
||||
finally
|
||||
{
|
||||
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
|
||||
}
|
||||
|
||||
$env:pwnd_738057 | Should -Not -Be "Pwnd"
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Engine events in constrained language mode" -Tags 'Feature','RequireAdminOnWindows' {
|
||||
|
||||
It "Verifies engine event in constrained language mode, its action runs as constrained" {
|
||||
|
|
Loading…
Reference in a new issue