Zapobieganie atakom polegającym na wstrzyknięciu skryptu
Skrypty programu PowerShell, takie jak inne języki programowania, mogą być narażone na ataki iniekcyjne. Atak polegający na wstrzyknięciu występuje, gdy użytkownik dostarcza dane wejściowe do funkcji podatnej na zagrożenia, która zawiera dodatkowe polecenia. Funkcja podatna na zagrożenia uruchamia dodatkowe polecenia, które mogą być poważnymi lukami w zabezpieczeniach. Na przykład złośliwy użytkownik może nadużywać funkcji podatnej na zagrożenia w celu uruchomienia dowolnego kodu na komputerze zdalnym, co może spowodować naruszenie tego komputera i uzyskanie dostępu do innych maszyn w sieci.
Po zdaniu sobie sprawy z tego problemu istnieje kilka sposobów ochrony przed atakami polegającymi na wstrzyknięciu.
Przykład kodu podatnego na zagrożenia
Luki w zabezpieczeniach polegających na wstrzyknięciu kodu programu PowerShell obejmują dane wejściowe użytkownika zawierające kod skryptu. Dane wejściowe użytkownika są dodawane do skryptu podatnego na zagrożenia, w którym są analizowane i uruchamiane przez program PowerShell.
function Get-ProcessById
{
param ($ProcId)
Invoke-Expression -Command "Get-Process -Id $ProcId"
}
Funkcja Get-ProcessById
wyszukuje proces lokalny według jego wartości Identyfikator. $ProcId
Przyjmuje argument parametru dowolnego typu. Element $ProcId
jest następnie konwertowany na ciąg i wstawiany do innego skryptu Invoke-Expression
, który jest analizowany i uruchamiany przy użyciu polecenia cmdlet . Ta funkcja działa prawidłowo, gdy jest przekazywana prawidłowa liczba całkowita identyfikatora procesu.
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
Jednak parametr nie określa typu. Akceptuje dowolną wartość ciągu, która może zawierać inne polecenia.
Get-ProcessById "$pid; Write-Host 'pwnd!'"
W tym przykładzie funkcja poprawnie pobrała proces zidentyfikowany przez $pid
element , ale także uruchomiła wstrzyknięty skrypt 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!
Sposoby ochrony przed atakami polegającymi na wstrzyknięciu
Oto kilka sposobów ochrony przed atakiem iniekcyjnym.
Użyj wpisanych danych wejściowych
Można określić typ argumentu $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
W tym miejscu parametr wejściowy jest ograniczony do typu liczby całkowitej, więc błąd występuje, gdy ciąg zostanie przekazany, który nie może zostać przekonwertowany na liczbę całkowitą.
Nie używaj Invoke-Expression
Zamiast używać Invoke-Expression
metody , bezpośrednio wywołaj Get-Process
metodę i pozwól, aby powiązanie parametrów programu PowerShell weryfikowało dane wejściowe.
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."
Najlepszym rozwiązaniem jest unikanie używania metody Invoke-Expression
, szczególnie w przypadku obsługi danych wejściowych użytkownika.
Invoke-Expression
jest niebezpieczne, ponieważ analizuje i uruchamia dowolną dostarczaną zawartość ciągu, co czyni go podatnym na ataki iniekcyjne. Lepiej jest polegać na powiązaniu parametrów programu PowerShell.
Zawijanie ciągów w pojedynczych cudzysłowach
Jednak czasami użycie Invoke-Expression
jest nieuniknione, a także trzeba obsługiwać dane wejściowe ciągu użytkownika. Dane wejściowe użytkownika można bezpiecznie obsługiwać przy użyciu pojedynczych cudzysłowów wokół każdej zmiennej wejściowej ciągu. Pojedynczy cudzysłów gwarantuje, że analizator programu PowerShell traktuje dane wejściowe użytkownika jako pojedynczy literał ciągu.
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."
Jednak ta wersja funkcji nie jest jeszcze całkowicie bezpieczna przed atakami polegającymi na wstrzyknięciu. Złośliwy użytkownik może nadal używać pojedynczych cudzysłowów w danych wejściowych do wstrzykiwania kodu.
Get-ProcessById "$pid'; Write-Host 'pwnd!';'"
W tym przykładzie użyto pojedynczych cudzysłowów w danych wejściowych użytkownika, aby wymusić uruchomienie trzech oddzielnych instrukcji, z których jeden jest dowolnym kodem wstrzykiwanym przez użytkownika.
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
97 46.08 183.10 1.08 2524 3 pwsh
pwnd!
EscapeSingleQuotedStringContent()
Korzystanie z metody
Aby chronić się przed wstawieniem własnych znaków pojedynczego cudzysłowu w celu wykorzystania funkcji, należy użyć interfejsu EscapeSingleQuotedStringContent()
API. Jest to statyczna publiczna metoda klasy PowerShell System.Management.Automation.Language.CodeGeneration . Ta metoda sprawia, że dane wejściowe użytkownika są bezpieczne, uciekając przed wszelkimi pojedynczymi cudzysłowami zawartymi w danych wejściowych użytkownika.
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."
Aby uzyskać więcej informacji, zobacz EscapeSingleQuotedStringContent().
Wykrywanie kodu podatnego na zagrożenia za pomocą narzędzia Injection Hunter
Injection Hunter to moduł napisany przez Lee Holmes, który zawiera reguły analizatora skryptów programu PowerShell do wykrywania luk w zabezpieczeniach polegających na wstrzyknięciu kodu. Użyj jednego z następujących poleceń, aby zainstalować moduł z Galeria programu PowerShell:
# Use PowerShellGet v2.x
Install-Module InjectionHunter
# Use PowerShellGet v3.x
Install-PSResource InjectionHunter
Służy to do automatyzowania analizy zabezpieczeń podczas kompilacji, procesów ciągłej integracji, wdrożeń i innych scenariuszy.
$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.
Aby uzyskać więcej informacji, zobacz PSScriptAnalyzer.