Implicações de segurança do uso de dados não autorizados na avaliação aritmética do shell

17

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

x=$((1-$x))

na maioria das conchas.

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

garethTheRed
fonte

Respostas:

22

O problema ocorre nos casos em que o conteúdo de $xnão foi higienizado e contém dados que podem estar sob o controle de um invasor nos casos em que o código do shell pode acabar sendo usado em um contexto de escalação de privilégios (por exemplo, um script invocado por um setuid aplicação, um script sudoers ou usado para processar dados fora da rede (CGI, gancho DHCP ...) direta ou indiretamente).

E se:

x='(PATH=2)'

Então:

x=$((1-$x)))

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

Observe que:

x=$((1-$x))

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

Em bash, zshe ksh(não dashou yash), se xé:

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

Em seguida, a expansão de $((1-$x))ou $((1-x))faz com que esse unamecomando seja executado (pois zsh, aprecisa ser uma variável de matriz, mas pode-se usar, psvarpor exemplo, para isso).

Em resumo, não se deve usar uninitialised ou não higienizado dados externos em expressões aritméticas em conchas (nota que a avaliação aritmética pode ser feito por $((...))(aka $[...]em bashou zsh), mas também em função do shell no let, [/ test, declare/typeset/export..., return, break, continue, exit, printf, printbuiltins, índices de matriz ((..))e [[...]]construções 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

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

Lembre-se também de que números com zeros à esquerda são tratados como octais em alguns contextos ( 010às vezes 10, às vezes 8) e tenha cuidado para que a verificação acima permita números que são potencialmente maiores que o número máximo máximo suportado pelo seu sistema (ou qualquer aplicativo que você desejar) use esse número inteiro em; bash, por exemplo, trata 18446744073709551616 como 0, pois é 2 64 ). Portanto, você pode querer adicionar verificações extras nessa declaração de caso acima, como:

(0?* | -0?*)
  echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
  echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;

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
$ bash -c '[ -v "$x" ]'
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:

Stéphane Chazelas
fonte
x='P=3'; : $(($x + 5))irá definir Pa 8, mas x='P=3'; : $((x + 5))irá definir Pa 3(em zsh, kshou bash). "O mesmo acontece com $((x + 1))..." não está correto agora; ele irá definir PATHpara 2, como antigamente.
mosvy 9/01
@mosvy, certo obrigado (e pela edição anterior). Corrigido agora.
Stéphane Chazelas