Partilhar via


Prevenção de ataques de injeção de script

Os scripts do PowerShell, como outras linguagens de programação, podem ser vulneráveis a ataques de injeção. Um ataque de injeção ocorre quando um usuário fornece entrada para uma função vulnerável que inclui comandos extras. A função vulnerável executa os comandos extras, o que pode ser uma grave vulnerabilidade de segurança. Por exemplo, um usuário mal-intencionado pode abusar da função vulnerável para executar código arbitrário em um computador remoto, possivelmente comprometendo esse computador e obtendo acesso a outras máquinas na rede.

Uma vez que você esteja ciente do problema, há várias maneiras de se proteger contra ataques de injeção.

Exemplo de código vulnerável

As vulnerabilidades de injeção de código do PowerShell envolvem a entrada do usuário que contém código de script. A entrada do usuário é adicionada ao script vulnerável onde é analisada e executada pelo PowerShell.

function Get-ProcessById
{
    param ($ProcId)

    Invoke-Expression -Command "Get-Process -Id $ProcId"
}

A Get-ProcessById função procura um processo local pelo seu valor Id. Ele usa um $ProcId argumento de parâmetro de qualquer tipo. O $ProcId é então convertido em uma cadeia de caracteres e inserido em outro script que é analisado e executado usando o Invoke-Expression cmdlet. Esta função funciona bem quando um inteiro de ID de processo válido é passado.

Get-ProcessById $pid

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    50.09     132.72       1.20   12528   3 pwsh

No entanto, o $ProcId parâmetro não especifica um tipo. Ele aceita qualquer valor de cadeia de caracteres arbitrário que possa incluir outros comandos.

Get-ProcessById "$pid; Write-Host 'pwnd!'"

Neste exemplo, a função recuperou corretamente o processo identificado pelo $pid, mas também executou o script Write-Host 'pwnd!'injetado.

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     92    45.66     122.52       1.06   21736   3 pwsh
pwnd!

Formas de proteção contra ataques de injeção

Existem várias maneiras de se proteger contra um ataque de injeção.

Usar entrada digitada

Você pode especificar um tipo para o $ProcId argumento.

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."

Aqui, o $ProcId parâmetro de entrada é restrito a um tipo inteiro, portanto, ocorre um erro quando uma cadeia de caracteres é passada que não pode ser convertida em um inteiro.

Não utilize Invoke-Expression

Em vez de usar Invoke-Expression, chame Get-Processdiretamente e deixe o fichário de parâmetros do PowerShell validar a entrada.

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."

Como prática recomendada, você deve evitar o uso do Invoke-Expression, especialmente ao lidar com a entrada do usuário. Invoke-Expression é perigoso porque analisa e executa qualquer conteúdo de cadeia de caracteres fornecido, tornando-o vulnerável a ataques de injeção. É melhor confiar na vinculação de parâmetros do PowerShell.

Envolver cadeias de caracteres entre aspas simples

No entanto, há momentos em que o uso Invoke-Expression é inevitável e você também precisa lidar com a entrada de cadeia de caracteres do usuário. Você pode lidar com segurança com a entrada do usuário usando aspas simples em torno de cada variável de entrada de cadeia de caracteres. A aspa única garante que o analisador do PowerShell trate a entrada do usuário como um literal de cadeia de caracteres única.

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."

No entanto, esta versão da função ainda não está completamente segura contra ataques de injeção. Um usuário mal-intencionado ainda pode usar aspas simples em sua entrada para injetar código.

Get-ProcessById "$pid'; Write-Host 'pwnd!';'"

Este exemplo usa aspas simples na entrada do usuário para forçar a função a executar três instruções separadas, uma das quais é código arbitrário injetado pelo usuário.

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
     97    46.08     183.10       1.08    2524   3 pwsh
pwnd!

Use o EscapeSingleQuotedStringContent() método

Para proteger contra o usuário inserir seus próprios caracteres de aspas simples para explorar a função, você deve usar a EscapeSingleQuotedStringContent() API. Este é um método público estático da classe System.Management.Automation.Language.CodeGeneration do PowerShell. Esse método torna a entrada do usuário segura, escapando de qualquer aspa única incluída na entrada do usuário.

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."

Para obter mais informações, consulte EscapeSingleQuotedStringContent().

Deteção de código vulnerável com o Injection Hunter

O Injection Hunter é um módulo escrito por Lee Holmes que contém regras do PowerShell Script Analyzer para detetar vulnerabilidades de injeção de código. Use um dos seguintes comandos para instalar o módulo da Galeria do PowerShell:

# Use PowerShellGet v2.x
Install-Module InjectionHunter

# Use PowerShellGet v3.x
Install-PSResource InjectionHunter

Você pode usar isso para automatizar a análise de segurança durante compilações, processos de integração contínua, implantações e outros cenários.

$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.

Para obter mais informações, consulte PSScriptAnalyzer.