如何防止在PowerShell中进行变量注入? [英] How can I prevent variable injection in PowerShell?

查看:92
本文介绍了如何防止在PowerShell中进行变量注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在@Ansgar Wiechers对最近的PowerShell问题的评论中再次被触发: 请勿使用Invoke-Expression 来解决我很久以来一直想问的一个安全问题。

I was triggered again on a comment on a recent PowerShell question from @Ansgar Wiechers: DO NOT use Invoke-Expression with regards to a security question I have for a long time somewhere in the back of my mind and need to ask.

强有力的声明(引用调用表达被认为是有害的文章)表明,调用可以覆盖变量的脚本是有害的。

The strong statement (with a reference to the Invoke-Expression considered harmful article) suggests that an invocation of a script that can overwrite variables is considered harmful.

PSScriptAnalyzer 建议不要使用 Invoke-Expression ,请参见

Also the PSScriptAnalyzer advises against using Invoke-Expression, see the AvoidUsingInvokeExpression rule.

但是我曾经使用一种技术自己在递归脚本中更新了一个公共变量,该变量实际上可以覆盖一个

But I once used a technic myself to update a common variable in a recursive script which can actually overwrite a value in any of its parents scopes which is as simple as:

([Ref]$ParentVariable).Value = $NewValue

据我所知,潜在的恶意脚本也可以使用此技术来注入变量,无论如何它会被调用...

As far as I can determine a potential malicious script could use this technic too to inject variables in any case no matter how it is invoked...

请考虑以下恶意 Inject.ps1 脚本:

Consider the following "malicious" Inject.ps1 script:

([Ref]$MyValue).Value = 456
([Ref]$MyString).Value = 'Injected string'
([Ref]$MyObject).Value = [PSCustomObject]@{Name = 'Injected'; Value = 'Object'}

我的 Test.ps1 脚本:

$MyValue = 123
$MyString = "MyString"
$MyObject = [PSCustomObject]@{Name = 'My'; Value = 'Object'}
.\Inject.ps1
Write-Host $MyValue
Write-Host $MyString
Write-Host $MyObject

结果:

456
Injected string
@{Name=Injected; Value=Object}

Test.ps1中看到所有三个变量范围被 Inject.ps1 脚本覆盖。这也可以使用Invoke-Command cmdlet来完成,并且我是否将变量的范围设置为 Private 都没有关系:

As you see all three variables in the Test.ps1 scope are overwritten by the Inject.ps1 script. This can also be done using the Invoke-Command cmdlet and it doesn't even matter whether I set the scope of a variable to Private either:

New-Variable -Name MyValue -Value 123 -Scope Private
$MyString = "MyString"
$MyObject = [PSCustomObject]@{Name = 'My'; Value = 'Object'}
Invoke-Command {
    ([Ref]$MyValue).Value = 456
    ([Ref]$MyString).Value = 'Injected string'
    ([Ref]$MyObject).Value = [PSCustomObject]@{Name = 'Injected'; Value = 'Object'}
}
Write-Host $MyValue
Write-Host $MyString
Write-Host $MyObject

有没有一种方法可以完全隔离已调用的脚本/命令与覆盖当前作用域中的变量?

是否可以被视为以任何方式调用脚本的安全风险?

Is there a way to completely isolate an invoked script/command from overwriting variables in the current scope?
If not, can this be considered as a security risk for invoking scripts in any way?

推荐答案

反对使用 Invoke-表达式的使用主要是为了防止意外执行代码(代码注入)。

The advice against use of Invoke-Expression use is primarily about preventing unintended execution of code (code injection).

如果您调用一段PowerShell代码-无论是直接还是通过 Invoke-Expression -它都可以(可能是恶意地)操纵父作用域,包括全局作用域。

请注意,潜在的操纵不仅限于变量:例如,函数别名也可以进行修改。

If you invoke a piece of PowerShell code - whether directly or via Invoke-Expression - it can indeed (possibly maliciously) manipulate parent scopes, including the global scope.
Note that this potential manipulation isn't limited to variables: for instance, functions and aliases can be modified as well.

注意事项:运行未知代码不仅有问题,还有问题不仅恶意修改了呼叫者的状态(变量,...),而且还涉及到直接执行 有害/破坏性行为的行为;

Caveat: Running unknown code is problematic not just with respect to maliciously modifying the caller's state (variables, ...), but also with respect to performing unwanted / destructive actions directly; the solutions below only guard against the former.

要提供所需的隔离,您有两个基本选择:

To provide the desired isolation, you have two basic choices:


  • child 进程中运行代码

  • Run the code in a child process:


  • 通过启动另一个PowerShell实例;例如(在Windows PowerShell中使用 powershell 代替 pwsh ):


  • pwsh {./someUntrustedScript.ps1}

  • pwsh { ./someUntrustedScript.ps1 }

通过开始后台作业;例如:


  • 开始作业{./someUntrustedScript.ps1} |接收作业-等待-自动删除

  • Start-Job { ./someUntrustedScript.ps1 } | Receive-Job -Wait -AutoRemove

在同一进程的单独线程 中运行代码:

Run the code in a separate thread in the same process:

  • As a thread job, via the Start-ThreadJob cmdlet (ships with PowerShell [Core] 6+; in Windows PowerShell, it can be installed from the PowerShell Gallery with something like
    Install-Module -Scope CurrentUser ThreadJob); e.g.:


  • Start-ThreadJob {./someUntrustedScript.ps1} |接收作业-等待-自动删除

  • Start-ThreadJob { ./someUntrustedScript.ps1 } | Receive-Job -Wait -AutoRemove

通过创建新运行空间通过PowerShell SDK;例如:

By creating a new runspace via the PowerShell SDK; e.g.:


  • [powershell] :: Create()。AddScript({./someUntrustedScript.ps1})。 Invoke()

  • 请注意,您必须付出额外的努力才能使输出流 other 而不是成功流。错误流的输出;同样,在命令完成后,应该在PowerShell实例上调用 .Dispose()

  • [powershell]::Create().AddScript({ ./someUntrustedScript.ps1 }).Invoke()
  • Note that you'll have to do extra work to get the output streams other than the success one, notably the error stream's output; also, .Dispose() should be called on the PowerShell instance on completion of the command.

基于子进程的解决方案将很慢并且在以下方面受到限制您可以返回的数据类型(由于涉及序列化/反序列化),但是它提供了隔离机制,以防止所调用的代码使进程崩溃。

A child process-based solution will be slow and limited in terms of data types you can return (due to serialization / deserialization being involved), but it provides isolation against the invoked code crashing the process.

基于线程的作业速度更快,可以返回任何数据类型,但可能会使整个过程崩溃。

A thread-based job is much faster, can return any data type, but can crash the entire process.

在所有情况下,您都必须以 arguments 的形式传递调用者需要访问的任何值,或者使用后台作业和线程作业,或者通过 $使用:范围说明符。

In all cases you will have to pass any values from the caller that the invoked code needs access to as arguments or, with background jobs and thread jobs, alternatively via the $using: scope specifier.

js2010 提到了其他不太理想的替代方法:

js2010 mentions other, less desirable alternatives:


  • Start-Process (基于子进程,具有纯文本参数和输出)

  • Start-Process (child process-based, with text-only arguments and output)

PowerShell工作流,它们已经过时了(不会移植到PowerShell Core ,并且不会)。

PowerShell Workflows, which are obsolescent (they weren't ported to PowerShell Core and won't be).

使用 Invoke-Command 与环回远程处理( -ComputerName localhost )假设也是一种选择,但是这会导致子进程和基于HTTP的通信的双重开销;另外,您必须为远程计算机设置计算机,并且必须以高程运行(以管理员身份)。

Using Invoke-Command with "loopback remoting" (-ComputerName localhost) is hypothetically also an option, but then you incur the double overhead of a child process and HTTP-based communication; also, your computer must be set up for remoting, and you must run with elevation (as administrator).

这篇关于如何防止在PowerShell中进行变量注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆