Segurança Implicações do uso de dados não-analizados na avaliação aritmética da Shell

15

Em um comentário para um pergunta recente , Stéphane Chazelas menciona que há implicações de segurança para a aritmética de parênteses duplos, como:

x=$((1-$x))

na maioria das conchas.

Minhas habilidades com o Google parecem estar enferrujadas e não consigo encontrar nada. Quais são as implicações de segurança da aritmética de parênteses duplos?

    
por garethTheRed 08.12.2014 / 13:57

1 resposta

19

O problema está em casos em que o conteúdo de $x não foi higienizado e contém dados que poderiam estar sob o controle de um invasor, em casos em que o código shell pode acabar sendo usado em um contexto de escalonamento de privilégios (por exemplo um script invocado por um aplicativo setuid, um script sudoers ou usado para processar dados fora da rede (CGI, DHCP hook ...) direta ou indiretamente).

Se:

x='PATH=2'

Então:

x=$((1-$x))

tem o efeito colateral de configurar PATH para 2 (um caminho relativo que poderia muito bem estar sob controle do invasor). Você pode substituir PATH por LD_LIBRARY_PATH ou IFS ... O mesmo acontece com x=$((1-x)) em bash, zsh ou ksh (não traços nem yash que aceitam apenas constantes numéricas em variáveis).

Em bash , zsh e ksh (não dash ou yash ), se x for:

x='a[0$(uname>&2)]'

Em seguida, a expansão de $((1-$x)) ou $((1-x)) faz com que o comando uname seja executado (para zsh , a precisa ser uma variável de matriz, mas é possível usar psvar , por exemplo ).

Observe que:

x=$((1-$x))

não funcionará corretamente para valores negativos de $x em alguns shells que implementam o operador (opcional como per capix) -- (decrement) (como em x=-1 , isso significa pedir ao shell para avaliar o 1--1 expressão aritmética). "$((1-x))" não tem o problema, pois x é expandido como parte da avaliação aritmética (não antes).

Em resumo, não se deve usar dados externos não inicializados ou não-higienizados em expressões aritméticas em shells (note que a avaliação aritmética pode ser feita por $((...)) (também conhecido como $[...] em bash ou zsh ) mas também dependendo do shell nos índices let , [ / test , declare/typeset/export... , return , break , continue , exit , printf , print builtins, matriz , ((..)) e [[...]] constrói para citar alguns).

Para verificar se uma variável contém um número inteiro decimal literal, você pode usar POSIXly:

case $var in
  ("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac

Tenha em atenção que [0-9] em alguns locais corresponde a mais de 0123456789. [[:digit:]] deve estar OK, mas não apostaria nisso.

Lembre-se também que os números com zeros à esquerda são tratados como octal em alguns contextos ( 010 é às vezes 10, às vezes 8) e cuidado para que a verificação acima permita números maiores que o número inteiro máximo suportado pelo sistema (ou qualquer que seja a aplicação que você usará aquele inteiro; o bash, por exemplo, trata 18446744073709551616 como 0, isto é, 2 64 ).

Exemplos:

$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux

Mais leitura em:

por 08.12.2014 / 14:29