“executa qualquer comando que passe dados não confiáveis a comandos que interpretam argumentos como comandos”

17

Do manual do findutils:

For example constructs such as these two commands

# risky
find -exec sh -c "something {}" \;
find -execdir sh -c "something {}" \;

are very dangerous. The reason for this is that the ‘{}’ is expanded to a filename which might contain a semicolon or other characters special to the shell. If for example someone creates the file /tmp/foo; rm -rf $HOME then the two commands above could delete someone’s home directory.

So for this reason do not run any command which will pass untrusted data (such as the names of fi les) to commands which interpret arguments as commands to be further interpreted (for example ‘sh’).

In the case of the shell, there is a clever workaround for this problem:

# safer
find -exec sh -c 'something "$@"' sh {} \;
find -execdir sh -c 'something "$@"' sh {} \;

This approach is not guaranteed to avoid every problem, but it is much safer than substituting data of an attacker’s choice into the text of a shell command.

  1. É a causa do problema em find -exec sh -c "something {}" \; que a substituição de {} é sem aspas e, portanto, não tratado como uma única string?
  2. Na solução find -exec sh -c 'something "$@"' sh {} \; ,

    • primeiro {} é substituído, mas como {} não é citado, não "$@" também tem o mesmo problema que o comando original? Por exemplo, "$@" será expandido para "/tmp/foo;" , "rm" , "-rf" e "$HOME" ?

    • por que {} não escapou ou foi citado?

  3. Você poderia dar outros exemplos (ainda com sh -c , ou sem ele se aplicável; com ou sem find , o que pode não ser necessário) onde o mesmo tipo de problema e solução se aplica, e que são exemplos mínimos para que possamos nos concentrar no problema e solução com pouca distração quanto possível? Consulte Formas de fornecer argumentos para um comando executado por 'bash -c'

Obrigado.

    
por Tim 07.06.2018 / 16:28

4 respostas

27

Isso não está relacionado a citar, mas sim ao processamento de argumentos.

Considere o exemplo arriscado:

find -exec sh -c "something {}" \;
  • Isso é analisado pelo shell e dividido em seis palavras: find , -exec , sh , -c , something {} (sem citações mais), ; . Não há nada para expandir. O shell executa find com essas seis palavras como argumentos.

  • Quando find encontra algo para processar, digamos foo; rm -rf $HOME , substitui {} por foo; rm -rf $HOME e executa sh com os argumentos sh , -c e something foo; rm -rf $HOME .

  • sh agora vê -c e, como resultado, analisa something foo; rm -rf $HOME ( o primeiro argumento não opcional ) e executa o resultado.

Agora, considere a variante mais segura:

find -exec sh -c 'something "$@"' sh {} \;
  • O shell executa find com os argumentos find , -exec , sh , -c , something "$@" , sh , {} , ; .

  • Agora, quando find encontra foo; rm -rf $HOME , substitui {} novamente e executa sh com os argumentos sh , -c , something "$@" , sh , foo; rm -rf $HOME .

  • sh-c e analisa something "$@" como o comando a ser executado e sh e foo; rm -rf $HOME como os parâmetros posicionais ( iniciando em $0 ), expande "$@" para foo; rm -rf $HOME como um único valor e executa something com o único argumento foo; rm -rf $HOME .

Você pode ver isso usando printf . Crie um novo diretório, digite-o e execute

touch "hello; echo pwned"

Executando a primeira variante da seguinte forma

find -exec sh -c "printf \"Argument: %s\n\" {}" \;

produz

Argument: .
Argument: ./hello
pwned

enquanto a segunda variante é executada como

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;

produz

Argument: .
Argument: ./hello; echo pwned
    
por 07.06.2018 / 16:58
3

Parte 1:

find apenas usa a substituição de texto.

Sim, se você fez isso sem aspas, assim:

find . -type f -exec sh -c "echo {}" \;

e um atacante conseguiu criar um arquivo chamado ; echo owned , então seria exec

sh -c "echo ; echo owned"

que resultaria no shell executando echo , em seguida, echo owned .

Mas se você adicionou aspas, o atacante pode simplesmente terminar suas citações e depois colocar o comando mal-intencionado após criar um arquivo chamado '; echo owned :

find . -type f -exec sh -c "echo '{}'" \;

que resultaria no shell executando echo '' , echo owned .

(se você trocou as aspas duplas por aspas simples, o invasor também pode usar o outro tipo de aspas).

Parte 2:

Em find -exec sh -c 'something "$@"' sh {} \; , o {} não é inicialmente interpretado pelo shell, ele é executado diretamente com execve , portanto, a inclusão de citações do shell não ajudaria.

find -exec sh -c 'something "$@"' sh "{}" \;

não tem efeito, pois o shell retira as aspas duplas antes de executar find .

find -exec sh -c 'something "$@"' sh "'{}'" \;

adiciona aspas que o shell não trata especialmente, então, na maioria dos casos, significa apenas que o comando não fará o que você quer.

A expansão para /tmp/foo; , rm , -rf , $HOME não deve ser um problema, porque esses são argumentos para something e something provavelmente não trata seus argumentos como comandos para executar.

Parte 3:

Assumo que considerações semelhantes se aplicam a qualquer coisa que receba entradas não confiáveis e as execute como (parte de) um comando, por exemplo, xargs e parallel .

    
por 07.06.2018 / 17:39
3

1. Is the cause of the problem in find -exec sh -c "something {}" \; that the replacement for {} is unquoted and therefore not treated as a single string?

De certa forma, mas as citações não podem ajudar aqui . O nome do arquivo que é substituído no lugar de {} pode conter qualquer caracteres, incluindo aspas . Seja qual for a forma de citar usada, o nome do arquivo pode conter o mesmo e "sair" da citação.

2. ... but since {} is unquoted, doesn't "$@" also have the same problem as the original command? For example, "$@" will be expanded to "/tmp/foo;", "rm", "-rf", and "$HOME"?

Não. "$@" se expande para os parâmetros posicionais, como palavras separadas, e não os divide mais. Aqui, {} é um argumento para find , e find passa o nome de arquivo atual também como um argumento distinto para sh . Ele está diretamente disponível como uma variável no script de shell, não é processado como um comando shell em si.

... why is {} not escaped or quoted?

Não precisa ser, na maioria dos shells. Se você executar fish , será necessário: fish -c 'echo {}' imprime uma linha vazia. Mas não importa se você citar, o shell apenas removerá as aspas.

3. Could you give other examples...

Toda vez que você expande um nome de arquivo (ou outra string não controlada) como está dentro de uma string que é tomada como algum tipo de código (*) , existe a possibilidade de execução arbitrária de comandos.

Por exemplo, isso expande $f diretamente do código Perl e causará problemas se um nome de arquivo contiver uma aspa dupla. A citação no nome do arquivo terminará a citação no código Perl, e o resto do nome do arquivo pode conter qualquer código Perl:

touch '"; print "HELLO";"'
for f in ./*; do
    perl -le "print \"size: \" . -s \"$f\""
done

(O nome do arquivo precisa ser um pouco estranho, pois o Perl analisa todo o código antes de executar qualquer um deles. Portanto, teremos que evitar um erro de análise).

Enquanto isso passa com segurança por meio de um argumento:

for f in ./*; do
    perl -le 'print "size: " . -s $ARGV[0]' "$f"
done

(Não faz sentido executar outro shell diretamente de um shell, mas se você fizer isso, é semelhante ao caso find -exec sh ... )

(* algum tipo de código inclui SQL, então XKCD obrigatório: link mais explicações: link )

    
por 07.06.2018 / 21:35
1

Sua preocupação é justamente a razão pela qual as citações paralelas do GNU são inseridas:

touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\n\" {}

Isso não será executado em echo pwned .

Ele executará um shell, de modo que, se você estender seu comando, não obterá uma surpresa de repente:

# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo

Para mais detalhes sobre o problema da desova, veja: link

    
por 08.06.2018 / 09:28

Tags