Fix 4 issues for the WDAC logging feature (#20913)

This commit is contained in:
Dongbo Wang
2023-12-13 11:46:21 -08:00
committed by GitHub
parent ecbe1b9dcb
commit 7d62cf54ac
7 changed files with 77 additions and 35 deletions
@@ -3986,44 +3986,46 @@ namespace System.Management.Automation
// - It's in FullLanguage but not because it's part of a parameter binding that is transitioning from ConstrainedLanguage to FullLanguage
// When this is invoked from a parameter binding in transition from ConstrainedLanguage environment to FullLanguage command, we disallow
// the property conversion because it's dangerous.
if (ecFromTLS == null || (ecFromTLS.LanguageMode == PSLanguageMode.FullLanguage && !ecFromTLS.LanguageModeTransitionInParameterBinding))
bool canProceedWithConversion = ecFromTLS == null || (ecFromTLS.LanguageMode == PSLanguageMode.FullLanguage && !ecFromTLS.LanguageModeTransitionInParameterBinding);
if (!canProceedWithConversion)
{
result = _constructor();
var psobject = valueToConvert as PSObject;
if (psobject != null)
if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Audit)
{
// Use PSObject properties to perform conversion.
SetObjectProperties(result, psobject, resultType, CreateMemberNotFoundError, CreateMemberSetValueError, formatProvider, recursion, ignoreUnknownMembers);
}
else
{
// Use provided property dictionary to perform conversion.
// The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2
IDictionary properties = valueToConvert as IDictionary;
SetObjectProperties(result, properties, resultType, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: false);
throw InterpreterError.NewInterpreterException(
valueToConvert,
typeof(RuntimeException),
errorPosition: null,
"HashtableToObjectConversionNotSupportedInDataSection",
ParserStrings.HashtableToObjectConversionNotSupportedInDataSection,
resultType.ToString());
}
typeConversion.WriteLine("Constructor result: \"{0}\".", result);
}
else
{
if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Audit)
{
SystemPolicy.LogWDACAuditMessage(
// When in audit mode, we report but don't enforce, so we will proceed with the conversion.
SystemPolicy.LogWDACAuditMessage(
context: ecFromTLS,
title: ExtendedTypeSystem.WDACHashTypeLogTitle,
message: StringUtil.Format(ExtendedTypeSystem.WDACHashTypeLogMessage, resultType.FullName),
fqid: "LanguageHashtableConversionNotAllowed",
dropIntoDebugger: true);
}
else
{
RuntimeException rte = InterpreterError.NewInterpreterException(valueToConvert, typeof(RuntimeException), null,
"HashtableToObjectConversionNotSupportedInDataSection", ParserStrings.HashtableToObjectConversionNotSupportedInDataSection, resultType.ToString());
throw rte;
}
}
result = _constructor();
var psobject = valueToConvert as PSObject;
if (psobject != null)
{
// Use PSObject properties to perform conversion.
SetObjectProperties(result, psobject, resultType, CreateMemberNotFoundError, CreateMemberSetValueError, formatProvider, recursion, ignoreUnknownMembers);
}
else
{
// Use provided property dictionary to perform conversion.
// The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2
IDictionary properties = valueToConvert as IDictionary;
SetObjectProperties(result, properties, resultType, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: false);
}
typeConversion.WriteLine("Constructor result: \"{0}\".", result);
return result;
}
catch (TargetInvocationException ex)
@@ -5,6 +5,7 @@ using System.Collections.ObjectModel;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Runspaces.Internal;
using System.Management.Automation.Security;
using Dbg = System.Management.Automation.Diagnostics;
@@ -94,7 +95,22 @@ namespace System.Management.Automation.Remoting
// to be parsed and evaluated on the remote session (not in the current local session).
RemoteRunspace remoteRunspace = _runspaceRef.Value as RemoteRunspace;
bool isConfiguredLoopback = remoteRunspace != null && remoteRunspace.IsConfiguredLoopBack;
bool isTrustedInput = !isConfiguredLoopback && (localRunspace.ExecutionContext.LanguageMode == PSLanguageMode.FullLanguage);
bool inFullLanguage = context.LanguageMode == PSLanguageMode.FullLanguage;
if (context.LanguageMode == PSLanguageMode.ConstrainedLanguage
&& SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Audit)
{
// In audit mode, report but don't enforce.
inFullLanguage = true;
SystemPolicy.LogWDACAuditMessage(
context: context,
title: RemotingErrorIdStrings.WDACGetPowerShellLogTitle,
message: RemotingErrorIdStrings.WDACGetPowerShellLogMessage,
fqid: "GetPowerShellMayFail",
dropIntoDebugger: true);
}
bool isTrustedInput = !isConfiguredLoopback && inFullLanguage;
// Create PowerShell from ScriptBlock.
ScriptBlock scriptBlock = ScriptBlock.Create(context, line);
@@ -350,7 +366,7 @@ namespace System.Management.Automation.Remoting
/// <param name="eventArgs"></param>
private void HandleHostCall(object sender, RemoteDataEventArgs<RemoteHostCall> eventArgs)
{
System.Management.Automation.Runspaces.Internal.ClientRemotePowerShell.ExitHandler(sender, eventArgs);
ClientRemotePowerShell.ExitHandler(sender, eventArgs);
}
#region Robust Connection Support
@@ -2278,7 +2278,7 @@ namespace Microsoft.PowerShell.Commands
// Semantic checks on the using statement have already validated that there are no arbitrary expressions,
// so we'll allow these expressions in everything but NoLanguage mode.
bool allowUsingExpressions = (Context.SessionState.LanguageMode != PSLanguageMode.NoLanguage);
bool allowUsingExpressions = Context.SessionState.LanguageMode != PSLanguageMode.NoLanguage;
object[] usingValuesInArray = null;
IDictionary usingValuesInDict = null;
@@ -2428,7 +2428,7 @@ namespace Microsoft.PowerShell.Commands
// GetExpressionValue ensures that it only does variable access when supplied a VariableExpressionAst.
// So, this is still safe to use in ConstrainedLanguage and will not result in arbitrary code
// execution.
bool allowVariableAccess = (Context.SessionState.LanguageMode != PSLanguageMode.NoLanguage);
bool allowVariableAccess = Context.SessionState.LanguageMode != PSLanguageMode.NoLanguage;
foreach (var varAst in paramUsingVars)
{
@@ -5524,7 +5524,7 @@ namespace System.Management.Automation.Language
return target.ThrowRuntimeError(args, moreTests, errorID, resourceString);
}
string targetName = (targetValue as Type)?.FullName;
string targetName = (targetValue as Type)?.FullName ?? targetValue?.GetType().FullName;
SystemPolicy.LogWDACAuditMessage(
context: context,
title: ParameterBinderStrings.WDACBinderInvocationLogTitle,
@@ -714,6 +714,18 @@ namespace System.Management.Automation
// of invoking it. So the trustworthiness is defined by the trustworthiness of the
// script block's language mode.
bool isTrusted = scriptBlock.LanguageMode == PSLanguageMode.FullLanguage;
if (scriptBlock.LanguageMode == PSLanguageMode.ConstrainedLanguage
&& SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Audit)
{
// In audit mode, report but don't enforce.
isTrusted = true;
SystemPolicy.LogWDACAuditMessage(
context: context,
title: ParserStrings.WDACGetSteppablePipelineLogTitle,
message: ParserStrings.WDACGetSteppablePipelineLogMessage,
fqid: "GetSteppablePipelineMayFail",
dropIntoDebugger: true);
}
foreach (var commandAst in pipelineAst.PipelineElements.Cast<CommandAst>())
{
@@ -729,7 +741,7 @@ namespace System.Management.Automation
var exprAst = (ExpressionAst)commandElement;
var argument = Compiler.GetExpressionValue(exprAst, isTrusted, context);
var splatting = (exprAst is VariableExpressionAst && ((VariableExpressionAst)exprAst).Splatted);
var splatting = exprAst is VariableExpressionAst && ((VariableExpressionAst)exprAst).Splatted;
commandParameters.Add(CommandParameterInternal.CreateArgument(argument, exprAst, splatting));
}
@@ -797,8 +809,8 @@ namespace System.Management.Automation
}
object argumentValue = Compiler.GetExpressionValue(argumentAst, isTrusted, context);
bool spaceAfterParameter = (errorPos.EndLineNumber != argumentAst.Extent.StartLineNumber ||
errorPos.EndColumnNumber != argumentAst.Extent.StartColumnNumber);
bool spaceAfterParameter = errorPos.EndLineNumber != argumentAst.Extent.StartLineNumber ||
errorPos.EndColumnNumber != argumentAst.Extent.StartColumnNumber;
return CommandParameterInternal.CreateParameterWithArgument(commandParameterAst, commandParameterAst.ParameterName,
errorPos.Text, argumentAst, argumentValue,
spaceAfterParameter);
@@ -1361,4 +1361,10 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent
<data name="WDACParserForEachOperatorLogMessage" xml:space="preserve">
<value>The ForEach keyword will fail '{0}' iteration item method invocation when run in Constrained Language mode.</value>
</data>
<data name="WDACGetSteppablePipelineLogTitle" xml:space="preserve">
<value>Expression Evaluation May Fail</value>
</data>
<data name="WDACGetSteppablePipelineLogMessage" xml:space="preserve">
<value>Creating a steppable pipeline from a script block may require evaluating some expressions within the script block. The expression evaluation will silently fail and return 'null' in Constrained Language mode, unless the expression represents a constant value.</value>
</data>
</root>
@@ -1714,4 +1714,10 @@ SSH client process terminated before connection could be established.</value>
<data name="DISCUnknownConfigName" xml:space="preserve">
<value>The session configuration file contains an unknown configuration option: {0}.</value>
</data>
<data name="WDACGetPowerShellLogTitle" xml:space="preserve">
<value>Expression Evaluation May Fail</value>
</data>
<data name="WDACGetPowerShellLogMessage" xml:space="preserve">
<value>Creating a PowerShell object from a script block may require evaluating some expressions within the script block. The expression evaluation will silently fail and return 'null' in Constrained Language mode, unless the expression represents a constant value.</value>
</data>
</root>