Förhindra skriptinmatningsattacker
PowerShell-skript, liksom andra programmeringsspråk, kan vara sårbara för inmatningsattacker. En inmatningsattack inträffar när en användare ger indata till en sårbar funktion som innehåller extra kommandon. Den sårbara funktionen kör de extra kommandona, vilket kan vara en allvarlig säkerhetsrisk. En obehörig användare kan till exempel missbruka den sårbara funktionen för att köra godtycklig kod på en fjärrdator, vilket kan äventyra datorn och få åtkomst till andra datorer i nätverket.
När du är medveten om problemet finns det flera sätt att skydda mot inmatningsattacker.
Exempel på sårbar kod
Säkerhetsrisker för PowerShell-kodinmatning omfattar användarindata som innehåller skriptkod. Användarindata läggs till i ett sårbart skript där det parsas och körs av PowerShell.
function Get-ProcessById
{
param ($ProcId)
Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Funktionen Get-ProcessById
söker efter en lokal process med dess ID-värde. Det tar ett $ProcId
parameterargument av vilken typ som helst. $ProcId
Konverteras sedan till en sträng och infogas i ett annat skript som parsas och körs med hjälp av cmdletenInvoke-Expression
. Den här funktionen fungerar bra när ett giltigt process-ID-heltal skickas.
Get-ProcessById $pid
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
97 50.09 132.72 1.20 12528 3 pwsh
Parametern $ProcId
anger dock ingen typ. Den accepterar alla godtyckliga strängvärden som kan innehålla andra kommandon.
Get-ProcessById "$pid; Write-Host 'pwnd!'"
I det här exemplet hämtade funktionen den process som identifierades av $pid
, men körde även det inmatade skriptet 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!
Sätt att skydda mot injektionsattacker
Det finns flera sätt att skydda mot en injektionsattack.
Använda indata som skrivits
Du kan ange en typ för $ProcId
argumentet.
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
Här är indataparametern begränsad till en heltalstyp, så ett fel uppstår när en sträng skickas in som inte kan konverteras till ett heltal.
Använd inte Invoke-Expression
I stället för att använda Invoke-Expression
anropar Get-Process
du direkt och låter PowerShells parameterbindning validera indata.
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."
Som bästa praxis bör du undvika att använda Invoke-Expression
, särskilt när du hanterar användarindata.
Invoke-Expression
är farligt eftersom det parsar och kör det stränginnehåll du tillhandahåller, vilket gör det sårbart för inmatningsattacker. Det är bättre att förlita sig på PowerShell-parameterbindning.
Omsluta strängar med enkla citattecken
Det finns dock tillfällen då det inte går att använda Invoke-Expression
och du måste också hantera indata för användarsträngar. Du kan hantera användarindata på ett säkert sätt med enkla citattecken runt varje stränginmatningsvariabel. Det enkla citattecknet säkerställer att PowerShells parser behandlar användarindata som en strängliteral.
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."
Den här versionen av funktionen är dock ännu inte helt säker från inmatningsattacker. En obehörig användare kan fortfarande använda enkla citattecken i sina indata för att mata in kod.
Get-ProcessById "$pid'; Write-Host 'pwnd!';'"
I det här exemplet används enkla citattecken i användarindata för att tvinga funktionen att köra tre separata instruktioner, varav en är godtycklig kod som matas in av användaren.
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
97 46.08 183.10 1.08 2524 3 pwsh
pwnd!
EscapeSingleQuotedStringContent()
Använda metoden
För att skydda mot att användaren infogar sina egna enkla citattecken för att utnyttja funktionen måste du använda API:et EscapeSingleQuotedStringContent()
. Det här är en statisk offentlig metod för klassen PowerShell System.Management.Automation.Language.CodeGeneration . Den här metoden gör användarens indata säkra genom att undvika alla enkla citattecken som ingår i användarens indata.
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."
Mer information finns i EscapeSingleQuotedStringContent().
Identifiera sårbar kod med Injection Hunter
Injection Hunter är en modul skriven av Lee Holmes som innehåller PowerShell Script Analyzer-regler för att identifiera sårbarheter vid kodinmatning. Använd något av följande kommandon för att installera modulen från PowerShell-galleriet:
# Use PowerShellGet v2.x
Install-Module InjectionHunter
# Use PowerShellGet v3.x
Install-PSResource InjectionHunter
Du kan använda detta för att automatisera säkerhetsanalyser under byggen, kontinuerliga integreringsprocesser, distributioner och andra scenarier.
$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.
Mer information finns i PSScriptAnalyzer.