PowerShell スクリプトは、他のプログラミング言語と同様に、インジェクション攻撃に対して脆弱になる可能性があります。 インジェクション攻撃は、ユーザーが追加のコマンドを含む脆弱な関数に入力を提供したときに発生します。 脆弱な関数は追加のコマンドを実行します。これは重大なセキュリティの脆弱性になる可能性があります。 たとえば、悪意のあるユーザーが脆弱な機能を悪用してリモート コンピューター上で任意のコードを実行し、そのコンピューターを侵害し、ネットワーク上の他のマシンにアクセスする可能性があります。
問題を認識したら、インジェクション攻撃から保護するいくつかの方法があります。
脆弱なコードの例
PowerShell コードインジェクションの脆弱性には、スクリプト コードを含むユーザー入力が含まれます。 ユーザー入力は、PowerShell によって解析および実行される脆弱なスクリプトに追加されます。
function Get-ProcessById
{
param ($ProcId)
Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Get-ProcessById
関数は、ID 値によってローカル プロセスを検索します。 任意の型の $ProcId
パラメーター引数を受け取ります。 その後、 $ProcId
は文字列に変換され、 Invoke-Expression
コマンドレットを使用して解析されて実行される別のスクリプトに挿入されます。 この関数は、有効なプロセス ID 整数が渡されると正常に動作します。
Get-ProcessById $PID
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
97 50.09 132.72 1.20 12528 3 pwsh
ただし、 $ProcId
パラメーターは型を指定しません。 他のコマンドを含めることができる任意の文字列値を受け入れます。
Get-ProcessById "$PID; Write-Host 'pwnd!'"
この例では、関数は $PID
によって識別されたプロセスを正しく取得しましたが、挿入されたスクリプト Write-Host 'pwnd!'
も実行しました。
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
92 45.66 122.52 1.06 21736 3 pwsh
pwnd!
インジェクション攻撃から保護する方法
インジェクション攻撃から保護するには、いくつかの方法があります。
入力はタイプ済みのものを使用する
$ProcId
引数の型を指定できます。
function Get-ProcessById
{
param ([int] $ProcId)
Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-ProcessById:
Line |
7 | Get-ProcessById "$PID; Write-Host 'pwnd!'"
| ~~~~~~~~~~~~~~~~~~~~~~~~~
| Cannot process argument transformation on parameter 'ProcId'. Cannot convert value
"8064; Write-Host 'pwnd!'" to type "System.Int32". Error: "The input string '8064; Write-Host 'pwnd!'
was not in a correct format."
ここでは、 $ProcId
入力パラメーターは整数型に制限されているため、整数に変換できない文字列が渡されるとエラーが発生します。
Invoke-Expression
は使用しません
Invoke-Expression
を使用する代わりに、Get-Process
を直接呼び出し、PowerShell のパラメーター バインダーで入力を検証します。
function Get-ProcessById
{
param ($ProcId)
Get-Process -Id $ProcId
}
Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-Process:
Line |
5 | Get-Process -Id $ProcId
| ~~~~~~~
| Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host 'pwnd!'" to type
"System.Int32". Error: "The input string '8064; Write-Host 'pwnd!' was not in a correct
format."
ベスト プラクティスとして、特にユーザー入力を処理する場合は、 Invoke-Expression
の使用を避ける必要があります。
Invoke-Expression
は、指定した文字列コンテンツを解析して実行し、インジェクション攻撃に対して脆弱になるため、危険です。 PowerShell パラメーター バインドに依存することをお勧めします。
文字列を一重引用符で囲む
ただし、 Invoke-Expression
を使用することは避けられない場合があり、ユーザー文字列の入力も処理する必要があります。 各文字列入力変数を囲む単一引用符を使用して、ユーザー入力を安全に処理できます。 一重引用符を使用すると、PowerShell のパーサーがユーザー入力を単一の文字列リテラルとして扱うことができます。
function Get-ProcessById
{
param ($ProcId)
Invoke-Expression -Command "Get-Process -Id '$ProcId'"
}
Get-ProcessById "$PID; Write-Host 'pwnd!'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064; Write-Host " to type
"System.Int32". Error: "The input string '8064; Write-Host' was not in a correct format."
ただし、このバージョンの関数はまだインジェクション攻撃から完全に安全ではありません。 悪意のあるユーザーは、入力で引き続き単一引用符を使用してコードを挿入できます。
Get-ProcessById "$PID'; Write-Host 'pwnd!';'"
この例では、ユーザー入力で単一引用符を使用して、関数で 3 つの個別のステートメントを強制的に実行します。そのうちの 1 つは、ユーザーによって挿入された任意のコードです。
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
97 46.08 183.10 1.08 2524 3 pwsh
pwnd!
EscapeSingleQuotedStringContent()
メソッドを使用する
関数を利用するために独自の単一引用符文字を挿入するユーザーから保護するには、 EscapeSingleQuotedStringContent()
API を使用する必要があります。 これは、PowerShell System.Management.Automation.Language.CodeGeneration クラスの静的パブリック メソッドです。 このメソッドは、ユーザー入力に含まれる単一引用符をエスケープすることで、ユーザー入力を安全にします。
function Get-ProcessById
{
param ($ProcId)
$ProcIdClean = [System.Management.Automation.Language.CodeGeneration]::
EscapeSingleQuotedStringContent("$ProcId")
Invoke-Expression -Command "Get-Process -Id '$ProcIdClean'"
}
Get-ProcessById "$PID'; Write-Host 'pwnd!';'"
Get-Process: Cannot bind parameter 'Id'. Cannot convert value "8064'; Write-Host 'pwnd!';'" to type
"System.Int32". Error: "The input string '8064'; Write-Host 'pwnd!';'' was not in a correct format."
詳細については、「 EscapeSingleQuotedStringContent()」を参照してください。
インジェクション ハンターによる脆弱なコードの検出
インジェクション ハンター は Lee Holmes によって記述されたモジュールで、コードインジェクションの脆弱性を検出するための PowerShell スクリプト アナライザールールが含まれています。 PowerShell ギャラリーからモジュールをインストールするには、次のいずれかのコマンドを使用します。
# Use PowerShellGet v2.x
Install-Module InjectionHunter
# Use PowerShellGet v3.x
Install-PSResource InjectionHunter
これを使用して、ビルド中のセキュリティ分析、継続的インテグレーション プロセス、デプロイ、およびその他のシナリオを自動化できます。
$RulePath = (Get-Module -List InjectionHunter).Path
Invoke-ScriptAnalyzer -CustomRulePath $RulePath -Path .\Invoke-Dangerous.ps1
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
InjectionRisk.InvokeExpression Warning Invoke-Dan 3 Possible script injection risk via the
gerous.ps1 Invoke-Expression cmdlet. Untrusted input can cause
arbitrary PowerShell expressions to be run.
Variables may be used directly for dynamic parameter
arguments, splatting can be used for dynamic
parameter names, and the invocation operator can be
used for dynamic command names. If content escaping
is truly needed, PowerShell has several valid quote
characters, so [System.Management.Automation.Languag
e.CodeGeneration]::Escape* should be used.
詳細については、「 PSScriptAnalyzer」を参照してください。
関連リンク
PowerShell