Skip to content

Commit

Permalink
Port Security bypass fixes from 6.1.3 (PowerShell#8915)
Browse files Browse the repository at this point in the history
  • Loading branch information
TravisEz13 committed Feb 21, 2019
1 parent 8617363 commit 57aeca4
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 6 deletions.
1 change: 1 addition & 0 deletions .vsts-ci/templates/windows-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ jobs:
Invoke-CIxUnit -SkipFailing
displayName: xUnit Tests
condition: succeeded()
continueOnError: true
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ internal ScriptBlockAst GetScriptBlockAst()

// If we are in ConstrainedLanguage mode but the defining language mode is FullLanguage, then we need
// to parse the script contents in FullLanguage mode context. Otherwise we will get bogus parsing errors
// such as "Configuration keyword not allowed".
// such as "Configuration or Class keyword not allowed".
var context = LocalPipeline.GetExecutionContextFromTLS();
if (context != null && context.LanguageMode == PSLanguageMode.ConstrainedLanguage &&
DefiningLanguageMode == PSLanguageMode.FullLanguage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,19 @@ protected override void EndProcessing()
// Create a module from a scriptblock...
if (_scriptBlock != null)
{
// Check ScriptBlock language mode. If it is different than the context language mode
// then throw error since private trusted script functions may be exposed.
if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage &&
_scriptBlock.LanguageMode == PSLanguageMode.FullLanguage)
{
this.ThrowTerminatingError(
new ErrorRecord(
new PSSecurityException(Modules.CannotCreateModuleWithScriptBlock),
"Modules_CannotCreateModuleWithFullLanguageScriptBlock",
ErrorCategory.SecurityError,
null));
}

string gs = System.Guid.NewGuid().ToString();
if (string.IsNullOrEmpty(_name))
{
Expand Down
16 changes: 16 additions & 0 deletions src/System.Management.Automation/engine/hostifaces/History.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,8 +1114,19 @@ protected override void EndProcessing()
ps.Streams.Verbose.DataAdded += verboseAdded;
ps.Streams.Warning.DataAdded += warningAdded;

LocalRunspace localRunspace = ps.Runspace as LocalRunspace;

try
{
// Indicate to the system that we are in nested prompt mode, since we are emulating running the command at the prompt.
// This ensures that the command being run as nested runs in the correct language mode, because CreatePipelineProcessor()
// always forces CommandOrigin to Internal for nested running commands, and Command.CreateCommandProcessor() forces Internal
// commands to always run in FullLanguage mode unless in a nested prompt.
if (localRunspace != null)
{
localRunspace.InInternalNestedPrompt = ps.IsNested;
}

Collection<PSObject> results = ps.Invoke();
if (results.Count > 0)
{
Expand All @@ -1126,6 +1137,11 @@ protected override void EndProcessing()
}
finally
{
if (localRunspace != null)
{
localRunspace.InInternalNestedPrompt = false;
}

ps.Streams.Debug.DataAdded -= debugAdded;
ps.Streams.Error.DataAdded -= errorAdded;
ps.Streams.Information.DataAdded -= informationAdded;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,21 @@ internal override bool InNestedPrompt
return false;
}

return context.InternalHost.HostInNestedPrompt();
return context.InternalHost.HostInNestedPrompt() || InInternalNestedPrompt;
}
}

/// <summary>
/// Allows internal nested commands to be run as "HostInNestedPrompt" so that CreatePipelineProcessor() does
/// not set CommandOrigin to Internal as it normally does by default. This then allows cmdlets like Invoke-History
/// to replay history command lines in the current runspace with the same language mode context as the host.
/// </summary>
internal bool InInternalNestedPrompt
{
get;
set;
}

#endregion protected_properties

#region internal_properties
Expand Down
11 changes: 11 additions & 0 deletions src/System.Management.Automation/engine/parser/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4161,6 +4161,17 @@ private StatementAst ClassDefinitionRule(List<AttributeBaseAst> customAttributes
// G class-member new-lines:opt
// G class-member-list class-member

// PowerShell classes are not supported in ConstrainedLanguage
if (Runspace.DefaultRunspace?.ExecutionContext?.LanguageMode == PSLanguageMode.ConstrainedLanguage)
{
ReportError(classToken.Extent,
nameof(ParserStrings.ClassesNotAllowedInConstrainedLanguage),
ParserStrings.ClassesNotAllowedInConstrainedLanguage,
classToken.Kind.Text());

return null;
}

SkipNewlines();
Token classNameToken;
var name = SimpleNameRule(out classNameToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1008,8 +1008,15 @@ internal bool IsUsingDollarInput()
if ((this.LanguageMode.HasValue) &&
(this.LanguageMode != context.LanguageMode))
{
oldLanguageMode = context.LanguageMode;
newLanguageMode = this.LanguageMode;
// Don't allow context: ConstrainedLanguage -> FullLanguage transition if
// this is dot sourcing into the current scope, unless it is within a trusted module scope.
if (this.LanguageMode != PSLanguageMode.FullLanguage ||
createLocalScope ||
(context.EngineSessionState.Module?.LanguageMode == PSLanguageMode.FullLanguage))
{
oldLanguageMode = context.LanguageMode;
newLanguageMode = this.LanguageMode;
}
}

Dictionary<string, PSVariable> backupWhenDotting = null;
Expand Down
3 changes: 3 additions & 0 deletions src/System.Management.Automation/resources/Modules.resx
Original file line number Diff line number Diff line change
Expand Up @@ -624,4 +624,7 @@
<data name="CannotExportMembersAccrossLanguageBoundaries" xml:space="preserve">
<value>Cannot export module members from a module that has a different language mode from the running session.</value>
</data>
<data name="CannotCreateModuleWithScriptBlock" xml:space="preserve">
<value>Cannot create new module while the session is in ConstrainedLanguage mode.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/System.Management.Automation/resources/ParserStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1497,4 +1497,7 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent
<data name = "CantInvokeCallOperatorAcrossLanguageBoundaries" xml:space="preserve">
<value>Cannot use '&amp;' or '.' operators to invoke a module scope command across language boundaries.</value>
</data>
<data name="ClassesNotAllowedInConstrainedLanguage" xml:space="preserve">
<value>Class keyword is not allowed in ConstrainedLanguage mode.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,80 @@ try
{ New-Module -ScriptBlock $testScriptBlock -ErrorAction Stop } | Should -Not -Throw -Because "Scriptblock without execution context is allowed in Full Language"
}
}

Describe "New-Module should not create module from trusted scriptblock when running in ConstrainedLanguage context" -Tags 'Feature','RequireAdminOnWindows' {

BeforeAll {

$script = @'
function ScriptFn { Write-Output $ExecutionContext.SessionState.LanguageMode }
'@

$scriptFileNameT = "NewModuleTrustedScriptBlock_System32"
$scriptFilePathT = Join-Path $TestDrive ($scriptFileNameT + ".ps1")
$script | Out-File -FilePath $scriptFilePathT

$scriptFileNameU = "NewModuleUntrustedScriptBlock"
$scriptFilePathU = Join-Path $TestDrive ($scriptFileNameU + ".ps1")
$script | Out-File -FilePath $scriptFilePathU
}

It "New-Module throws error when creating module with trusted scriptblock in ConstrainedLanguage" {

$expectedError = $null
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"

# Get scriptblock from trusted script file
$sb = (Get-Command $scriptFilePathT).ScriptBlock

# Create new module from trusted scriptblock while in ConstrainedLanguage
try
{
New-Module -Name TrustedScriptFoo -ScriptBlock $sb
throw "No Exception!"
}
catch
{
$expectedError = $_
}
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}

$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_CannotCreateModuleWithFullLanguageScriptBlock,Microsoft.PowerShell.Commands.NewModuleCommand"
}

It "New-Module succeeds in creating module with untrusted scriptblock in ConstrainedLanguage" {

$result = $null

try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"

# Get scriptblock from untrusted script file
$sb = (Get-Command $scriptFilePathU).ScriptBlock

# Create and import module from scriptblock
$m = New-Module -Name UntrustedScriptFoo -ScriptBlock $sb
Import-Module -ModuleInfo $m -Force

$result = ScriptFn
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}

$result | Should -BeExactly "ConstrainedLanguage"
}
}
}
finally
{
Expand Down

0 comments on commit 57aeca4

Please sign in to comment.