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
3 changed files with 248 additions and 457 deletions
|
@ -1132,12 +1132,16 @@ namespace System.Management.Automation
|
||||||
internal Breakpoint NewCommandBreakpoint(string path, string command, ScriptBlock action)
|
internal Breakpoint NewCommandBreakpoint(string path, string command, ScriptBlock action)
|
||||||
{
|
{
|
||||||
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||||
|
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddCommandBreakpoint(new CommandBreakpoint(path, pattern, command, action));
|
return AddCommandBreakpoint(new CommandBreakpoint(path, pattern, command, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Breakpoint NewCommandBreakpoint(string command, ScriptBlock action)
|
internal Breakpoint NewCommandBreakpoint(string command, ScriptBlock action)
|
||||||
{
|
{
|
||||||
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||||
|
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddCommandBreakpoint(new CommandBreakpoint(null, pattern, command, action));
|
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");
|
Diagnostics.Assert(path != null, "caller to verify path is not null");
|
||||||
|
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddLineBreakpoint(new LineBreakpoint(path, line, action));
|
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");
|
Diagnostics.Assert(path != null, "caller to verify path is not null");
|
||||||
|
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddLineBreakpoint(new LineBreakpoint(path, line, column, action));
|
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)
|
internal Breakpoint NewVariableBreakpoint(string path, string variableName, VariableAccessMode accessMode, ScriptBlock action)
|
||||||
{
|
{
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddVariableBreakpoint(new VariableBreakpoint(path, variableName, accessMode, action));
|
return AddVariableBreakpoint(new VariableBreakpoint(path, variableName, accessMode, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Breakpoint NewVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action)
|
internal Breakpoint NewVariableBreakpoint(string variableName, VariableAccessMode accessMode, ScriptBlock action)
|
||||||
{
|
{
|
||||||
|
CheckForBreakpointSupport();
|
||||||
return AddVariableBreakpoint(new VariableBreakpoint(null, variableName, accessMode, action));
|
return AddVariableBreakpoint(new VariableBreakpoint(null, variableName, accessMode, action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1761,12 +1769,6 @@ namespace System.Management.Automation
|
||||||
originalLanguageMode = _context.LanguageMode;
|
originalLanguageMode = _context.LanguageMode;
|
||||||
_context.LanguageMode = PSLanguageMode.FullLanguage;
|
_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
|
// Update the prompt to the debug prompt
|
||||||
if (hadDefaultPrompt)
|
if (hadDefaultPrompt)
|
||||||
|
@ -2060,6 +2062,17 @@ namespace System.Management.Automation
|
||||||
{
|
{
|
||||||
lock (_syncObject)
|
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)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case InternalDebugMode.InPushedStop:
|
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
|
#region Enable debug stepping
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
@ -2323,6 +2354,15 @@ namespace System.Management.Automation
|
||||||
{
|
{
|
||||||
lock (_syncObject)
|
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);
|
base.SetDebugMode(mode);
|
||||||
|
|
||||||
if (!CanEnableDebugger)
|
if (!CanEnableDebugger)
|
||||||
|
|
|
@ -18,10 +18,89 @@ try
|
||||||
$defaultParamValues = $PSDefaultParameterValues.Clone()
|
$defaultParamValues = $PSDefaultParameterValues.Clone()
|
||||||
$PSDefaultParameterValues["it:Skip"] = !$IsWindows
|
$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 {
|
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
|
# Debugger test type definition
|
||||||
$debuggerTestTypeDef = @'
|
$debuggerTestTypeDef = @'
|
||||||
using System;
|
using System;
|
||||||
|
@ -33,29 +112,14 @@ try
|
||||||
public class DebuggerTester
|
public class DebuggerTester
|
||||||
{
|
{
|
||||||
private Runspace _runspace;
|
private Runspace _runspace;
|
||||||
private readonly string _privateFnName;
|
|
||||||
|
|
||||||
[Flags]
|
public int DebuggerStopHitCount
|
||||||
public enum TestResults
|
|
||||||
{
|
|
||||||
NoResult = 0x0,
|
|
||||||
DebuggerStopHandled = 0x1,
|
|
||||||
PrivateFnFound = 0x2
|
|
||||||
};
|
|
||||||
|
|
||||||
public TestResults TestResult
|
|
||||||
{
|
{
|
||||||
private set;
|
private set;
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception ScriptException
|
public DebuggerTester(Runspace runspace)
|
||||||
{
|
|
||||||
private set;
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebuggerTester(Runspace runspace, string privateFnName)
|
|
||||||
{
|
{
|
||||||
if (runspace.Debugger == null)
|
if (runspace.Debugger == null)
|
||||||
{
|
{
|
||||||
|
@ -63,363 +127,160 @@ try
|
||||||
}
|
}
|
||||||
|
|
||||||
_runspace = runspace;
|
_runspace = runspace;
|
||||||
_privateFnName = privateFnName;
|
|
||||||
_runspace.Debugger.DebuggerStop += (sender, args) =>
|
_runspace.Debugger.DebuggerStop += (sender, args) =>
|
||||||
{
|
{
|
||||||
try
|
DebuggerStopHitCount += 1;
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'@
|
'@
|
||||||
|
|
||||||
$modulePath = Join-Path $TestDrive Modules
|
$script = @'
|
||||||
if (Test-Path -Path $modulePath)
|
"Hello"
|
||||||
{
|
Wait-Debugger
|
||||||
try { Remove-Item -Path $modulePath -Recurse -Force -ErrorAction SilentlyContinue } catch { }
|
"Goodbye"
|
||||||
}
|
'@
|
||||||
|
$scriptFilePath = Join-Path $TestDrive TScript.ps1
|
||||||
|
$script > $scriptFilePath
|
||||||
|
|
||||||
# Trusted module
|
# Define debugger test type
|
||||||
$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
|
|
||||||
Add-Type -TypeDefinition $debuggerTestTypeDef
|
Add-Type -TypeDefinition $debuggerTestTypeDef
|
||||||
}
|
}
|
||||||
|
|
||||||
AfterAll {
|
AfterAll {
|
||||||
|
|
||||||
if ($runspace -ne $null) { $runspace.Dispose() }
|
if (($script:moduleDirectory -ne $null) -and (Test-Path $script:moduleDirectory))
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
$ps.AddScript($script).BeginInvoke()
|
try { Remove-Item -Path $moduleDirectory -Recurse -Force -ErrorAction SilentlyContinue } catch { }
|
||||||
|
|
||||||
# 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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' {
|
It "Verifies that Set-PSBreakpoint Line is disabled on locked down system" {
|
||||||
|
|
||||||
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" {
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
|
||||||
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
|
||||||
|
|
||||||
# Set breakpoint on script
|
Set-PSBreakpoint -Script $scriptFilePath -Line 1
|
||||||
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()
|
|
||||||
throw "No Exception!"
|
throw "No Exception!"
|
||||||
}
|
}
|
||||||
catch
|
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
|
finally
|
||||||
{
|
{
|
||||||
if ($defaultParamValues -ne $null)
|
if ($null -ne $defaultParamValues)
|
||||||
{
|
{
|
||||||
$Global:PSDefaultParameterValues = $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' {
|
Describe "Engine events in constrained language mode" -Tags 'Feature','RequireAdminOnWindows' {
|
||||||
|
|
||||||
It "Verifies engine event in constrained language mode, its action runs as constrained" {
|
It "Verifies engine event in constrained language mode, its action runs as constrained" {
|
||||||
|
|
Loading…
Reference in a new issue