É possível usar o `find -exec sh -c` com segurança?

29

Eu estou tentando usar findpara echo 0em alguns arquivos, mas aparentemente isso só funciona com sh -c:

find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;

Mas usar sh -ccom find -execme deixa muito desconfortável porque suspeito de citar problemas. Eu brinquei um pouco com isso e, aparentemente, minhas suspeitas eram justificadas:

  • Minha configuração de teste:

    martin@dogmeat ~ % cd findtest 
    martin@dogmeat ~/findtest % echo one > file\ with\ spaces
    martin@dogmeat ~/findtest % echo two > file\ with\ \'single\ quotes\'
    martin@dogmeat ~/findtest % echo three > file\ with\ \"double\ quotes\"
    martin@dogmeat ~/findtest % ll
    insgesamt 12K
    -rw-rw-r-- 1 martin martin 6 Sep 17 12:01 file with "double quotes"
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with 'single quotes'
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with spaces
    
  • Usar find -execsem sh -cparece funcionar sem problemas - não é necessário citar aqui:

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
    
  • Mas quando estou usando sh -c {}parece exigir algum tipo de citação:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat {}' \;
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: spaces: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: single quotes: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: double quotes: No such file or directory
    
  • As aspas duplas funcionam desde que nenhum nome de arquivo contenha aspas duplas:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat "{}"' \;
    one
    two
    cat: findtest/file with double: No such file or directory
    cat: quotes: No such file or directory
    
  • As aspas simples funcionam desde que nenhum nome de arquivo contenha aspas simples:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c "cat '{}'" \;
    one
    cat: findtest/file with single: No such file or directory
    cat: quotes: No such file or directory
    three
    

Não encontrei uma solução que funcione em todos os casos. Existe algo que estou ignorando ou que estou usando de sh -cforma find -execinerentemente insegura?

Martin von Wittich
fonte

Respostas:

40

Nunca incorpore {}o código do shell! Isso cria uma vulnerabilidade de injeção de comando. Note-se que para cat "{}", não é apenas sobre os "personagens, \, `, $também são um problema (considere, por exemplo, um arquivo chamado ./$(reboot)/accept_ra).

(a propósito, algumas findimplementações não permitem que você faça isso, e o POSIX deixa o comportamento não especificado quando {}não está por si só em um argumento para find)

Aqui, você deseja passar os nomes dos arquivos como argumentos separados para sh(não no argumento do código ) e o shscript embutido (o argumento do código ) para se referir a eles usando parâmetros posicionais:

find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;

Ou, para evitar a execução de um shpor arquivo:

find . -name accept_ra -exec sh -c 'for file do
  echo 0 > "$file"; done' sh {} +

O mesmo se aplica a xargs -I{}ou zsh's zargs -I{}. Não escreva:

<list.txt xargs -I {} sh -c 'cmd> {}'

Qual seria uma vulnerabilidade de injeção de comando da mesma maneira que findacima, mas:

<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh

O que também tem o benefício de evitar a execução de um shpor arquivo e o erro quando list.txtnão contém nenhum arquivo.

Com zsh's zargs, você provavelmente desejaria usar uma função em vez de invocar sh -c:

do-it() cmd > $1
zargs ./*.txt -- do-it

Observe que em todos os exemplos acima, o segundo shacima entra nos scripts embutidos $0. Você deve usar algo relevante lá (como shou find-sh), não coisas como _, -, --ou a cadeia vazia, como o valor no $0é usado para mensagens de erro do shell:

$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied

O GNU parallelfunciona de maneira diferente. Com ele, você não deseja usá-lo, sh -cpois paralleljá executa um shell e tenta substituir {}pelo argumento citado na sintaxe correta do shell .

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
Stéphane Chazelas
fonte
isso é segunda shparece ser algum tipo de espaço reservado, ele funciona também se substituído por _, por exemplo - muito útil se você quiser chamar internos festança: find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Mas alguém sabe onde isso está documentado?
Florian Fida
1
@FlorianFida O primeiro argumento para o shell se torna $0(geralmente o nome do shell. Você precisa ignorá-lo nesse cenário para que ele não coma um de seus argumentos posicionais normais. A documentação para -cmencionar isso.
Etan Reisner,
2
@EtanReisner in-ulm.de/~mascheck/various/find/#embedded
Gilles 'SO- stop being evil'
1
@phk, não está argv[0]aqui, é apenas $0o script. A página de Sven é imprecisa aqui, rnão fará com que o shell entre em um modo restrito, tanto quanto é possível, e zshnão mudará de modo com base $0. (exec -a rksh ksh -c 'cd /')executará uma restrição ksh, mas não ksh -c 'cd /' rksh).
Stéphane Chazelas 1/11/16
1
Stéphane, se você não se importa, há uma resposta sua que explica "por que não incorporar {} no código do shell"? Analisei todas as suas respostas em encontrar, mas não consegui encontrar uma, mas posso jurar que vi algumas coisas que você escreveu sobre esse assunto, mas não me lembro se era uma resposta, um comentário no chat ou em outro site como o compunix ... Se / quando você tem o tempo, você se importaria de expansão na "} Nunca incorporar {" parte ou você acha que devemos ter um dedicado Q por exemplo "implicações de segurança do uso findcom -exec sh -ce incorporação {}no código shell" ?
23416 don_crissti