How can I prevent variable injection in PowerShell?

前端 未结 1 1280
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-06 21:08

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

相关标签:
1条回答
  • 2021-01-06 21:40

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

    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:

    • Run the code in a child process:

      • By starting another PowerShell instance; e.g. (use powershell instead of pwsh in Windows PowerShell):

        • pwsh { ./someUntrustedScript.ps1 }
      • By starting a background job; e.g.:

        • 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 } | Receive-Job -Wait -AutoRemove
      • By creating a new runspace via the PowerShell SDK; e.g.:

        • [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.

    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 mentions other, less desirable alternatives:

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

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

    • 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).

    0 讨论(0)
提交回复
热议问题