Do manual do findutils:
Por exemplo, construções como esses dois comandos
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
são muito perigosos. A razão para isso é que o '{}' é expandido para um nome de arquivo que pode conter um ponto e vírgula ou outros caracteres especiais para o shell. Se, por exemplo, alguém criar o arquivo
/tmp/foo; rm -rf $HOME
, os dois comandos acima poderão excluir o diretório inicial de alguém.Portanto, por esse motivo, não execute nenhum comando que passará dados não confiáveis (como os nomes dos arquivos) para comandos que interpretam argumentos como comandos a serem interpretados posteriormente (por exemplo, 'sh').
No caso do shell, existe uma solução inteligente para esse problema:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
Essa abordagem não garante evitar todos os problemas, mas é muito mais segura do que substituir os dados escolhidos pelo invasor no texto de um comando shell.
- A causa do problema é
find -exec sh -c "something {}" \;
que a substituição de{}
não está entre aspas e, portanto, não é tratada como uma única string? Na solução
find -exec sh -c 'something "$@"' sh {} \;
,first
{}
é substituído, mas como{}
não está entre aspas, também não"$@"
tem o mesmo problema que o comando original? Por exemplo,"$@"
será expandido para"/tmp/foo;"
,"rm"
,"-rf"
e"$HOME"
?por que
{}
não é escapado ou citado?
- Você poderia dar outros exemplos (ainda com
sh -c
, ou sem ele se aplicável; com ou semfind
o que pode não ser necessário) onde o mesmo tipo de problema e solução se aplicam, e que são exemplos mínimos para que possamos focar no problema e na solução com pouca distração possível? Veja Maneiras de fornecer argumentos para um comando executado por `bash -c`
Obrigado.
Isso não está realmente relacionado à citação, mas ao processamento de argumentos.
Considere o exemplo arriscado:
Isso é analisado pelo shell e dividido em seis palavras:
find
,-exec
,sh
,-c
,something {}
(sem mais aspas),;
. Não há nada para expandir. O shell é executadofind
com essas seis palavras como argumentos.Quando
find
encontra algo para processar, digamosfoo; rm -rf $HOME
, ele substitui{}
porfoo; rm -rf $HOME
, e executash
com os argumentossh
,-c
, esomething foo; rm -rf $HOME
.sh
agora vê-c
e, como resultado, analisasomething foo; rm -rf $HOME
( o primeiro argumento não opcional ) e executa o resultado.Agora considere a variante mais segura:
O shell é executado
find
com os argumentosfind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Agora, quando
find
findsfoo; rm -rf $HOME
, ele substitui{}
novamente e é executadosh
com os argumentossh
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
vê-c
e analisasomething "$@"
como o comando a ser executadosh
efoo; rm -rf $HOME
como os parâmetros posicionais ( começando de$0
), expande"$@"
parafoo; rm -rf $HOME
um valor único e é executadosomething
com o argumento únicofoo; rm -rf $HOME
.Você pode ver isso usando
printf
. Crie um novo diretório, insira-o e executeExecutando a primeira variante da seguinte forma
produz
enquanto a segunda variante, executada como
produz
Parte 1:
find
apenas usa a substituição de texto.Sim, se você fez sem aspas, assim:
e um invasor foi capaz de criar um arquivo chamado
; echo owned
, então ele executariao que resultaria na execução do shell
echo
entãoecho owned
.Mas se você adicionasse aspas, o invasor poderia simplesmente encerrar suas aspas e colocar o comando malicioso depois, criando um arquivo chamado
'; echo owned
:o que resultaria na execução do shell
echo ''
,echo owned
.(se você trocou as aspas duplas por aspas simples, o invasor também poderá usar o outro tipo de aspas.)
Parte 2:
Em
find -exec sh -c 'something "$@"' sh {} \;
, o{}
não é interpretado inicialmente pelo shell, ele é executado diretamente comexecve
, portanto, adicionar aspas do shell não ajudaria.não tem efeito, pois o shell retira as aspas duplas antes de executar
find
.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ê deseja.
Expandir para
/tmp/foo;
,rm
,-rf
,$HOME
não deve ser um problema, porque esses são argumentos parasomething
, esomething
provavelmente não trata seus argumentos como comandos a serem executados.Parte 3:
Suponho que considerações semelhantes se aplicam a qualquer coisa que receba entrada não confiável e a execute como (parte de) um comando, por exemplo
xargs
eparallel
.Em certo sentido, mas citar não pode ajudar aqui . O nome do arquivo que é substituído no lugar de
{}
pode conter qualquer caractere, incluindo aspas . Qualquer que seja a forma de citação usada, o nome do arquivo pode conter o mesmo e "quebrar" a citação.No.
"$@"
se expande para os parâmetros posicionais, como palavras separadas, e não os divide mais. Aqui,{}
é um argumento parafind
em si mesmo efind
passa o nome do arquivo atual também como um argumento distinto parash
. Está disponível diretamente como uma variável no script de shell, não é processado como um comando de shell em si.Não precisa ser, na maioria das conchas. Se você executar
fish
, ele precisa ser:fish -c 'echo {}'
imprime uma linha vazia. Mas não importa se você as cita, o shell apenas removerá as aspas.Sempre que você expande um nome de arquivo (ou outra string não controlada) como está dentro de uma string que é considerada algum tipo de código (*) , existe a possibilidade de execução de comandos arbitrários.
Por exemplo, isso expande
$f
diretamente o código Perl e causará problemas se um nome de arquivo contiver aspas duplas. A citação no nome do arquivo encerrará a citação no código Perl, e o restante do nome do arquivo pode conter qualquer código Perl:(O nome do arquivo deve ser um pouco estranho, pois o Perl analisa todo o código antecipadamente, antes de executá-lo. Portanto, teremos que evitar um erro de análise.)
Enquanto isso passa com segurança por meio de um argumento:
(Não faz sentido executar outro shell diretamente de um shell, mas se você fizer isso, é semelhante ao
find -exec sh ...
caso)(* algum tipo de código inclui SQL, então XKCD obrigatório: https://xkcd.com/327/ mais explicação: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
Sua preocupação é precisamente a razão pela qual o GNU Parallel cita a entrada:
Isso não será executado
echo pwned
.Ele executará um shell, para que, se você estender seu comando, não tenha uma surpresa repentina:
Para mais detalhes sobre o problema de spawning-a-shell, consulte: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell