Как я могу предотвратить внедрение переменных в PowerShell? - PullRequest
3 голосов
/ 11 июля 2019

Меня снова вызвали комментарий на недавний вопрос PowerShell от @Ansgar Wiechers: DO NOT use Invoke-Expression в отношении секретного вопроса, который у меня давно есть где-то в глубине души спросить.

Сильное утверждение (со ссылкой на статью Invoke-Expression, считающуюся вредной ) предполагает, что вызов сценария, который может перезаписывать переменные, считается вредным.

Также PSScriptAnalyzer рекомендует не использовать Invoke-Expression, см. Правило AvoidUsingInvokeExpression .

Но однажды я сам использовал технику для обновления общей переменной в рекурсивном скрипте, который может фактически перезаписать значение в любой из родительских областей, что так просто:

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

Насколько я могу определить, что потенциальный злонамеренный скрипт мог бы использовать эту технику для внедрения переменных в любом случае, независимо от того, как он вызывается ...

Рассмотрим следующий «вредоносный» скрипт Inject.ps1:

([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, и даже не имеет значения, задаю ли я область видимости переменной Private:

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

Есть ли способ полностью изолировать вызываемый скрипт / команду от перезаписи переменных в текущей области?
Если нет, то может ли это рассматриваться как угроза безопасности для запуска сценариев каким-либо образом?

1 Ответ

2 голосов
/ 11 июля 2019

Рекомендации против использования Invoke-Expression используются главным образом для предотвращения непреднамеренного выполнения кода (внедрение кода).

Если вы сознательно вызываете фрагмент кода PowerShell, он действительно (возможно, злонамеренно) может манипулировать родительскими областями, включая глобальную область.
Обратите внимание, что эта потенциальная манипуляция не ограничивается переменными : например, функции и псевдонимы также могут быть изменены.

Чтобы обеспечить желаемую изоляцию, у вас есть два основных варианта:

  • Запустите код в дочернем процессе:

    • Запуск другого экземпляра PowerShell; например (используйте powershell вместо pwsh в Windows PowerShell):

      • pwsh { ./someUntrustedScript.ps1 }
    • Запуская фоновое задание ; e.g.:

      • Start-Job { ./someUntrustedScript.ps1 } | Receive-Job -Wait -AutoRemove
  • Запустите код в отдельном потоке :

    • В качестве потокового задания через модуль ThreadJob (поставляется с PowerShell Core и в Windows PowerShell может быть установлен из PowerShell Gallery с чем-то вроде
      Install-Module -Scope CurrentUser ThreadJob); e.g.:

      • Start-ThreadJob { ./someUntrustedScript.ps1 } | Receive-Job -Wait -AutoRemove
    • Путем создания нового пространства выполнения с помощью PowerShell SDK; e.g.:

      • [powershell]::Create().AddScript({ ./someUntrustedScript.ps1 }).Invoke()
      • Обратите внимание, что вам придется проделать дополнительную работу, чтобы получить выходные потоки , отличные от успешных, особенно вывод потока ошибок; также .Dispose() должен вызываться для экземпляра PowerShell по завершении команды.

A дочерний процесс решение будет медленным и ограниченным с точки зрения типов данных, которые вы можете возвращать (из-за задействованной сериализации / десериализации), но оно обеспечивает изоляцию от вызываемого кода, приводящего к сбою процесса.

Работа на основе потоков выполняется намного быстрее, может возвращать данные любого типа, но может привести к сбою всего процесса.

Во всех случаях вам нужно будет передать любые значения от вызывающей стороны, к которой вызванному коду необходим доступ, как arguments .


js2010 упоминает другие, менее желательные альтернативы:

  • Start-Process (на основе дочерних процессов, с текстовыми аргументами и выходными данными)

  • Рабочие процессы PowerShell , которые устарели (они не были перенесены в PowerShell Core и не будут).

  • Использование Invoke-Command с «удаленным вызовом по шлейфу» (-ComputerName localhost) гипотетически также является опцией, но тогда вы получаете двойную нагрузку на дочерний процесс и связь на основе HTTP; Кроме того, ваш компьютер должен быть настроен для удаленного взаимодействия.

...