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 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 transmita dados não confiáveis (como nomes de arquivos) para comandos que interpretam argumentos como comandos a serem interpretados posteriormente (por exemplo, 'sh').
No caso do shell, há uma solução inteligente para esse problema:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
Não é garantido que essa abordagem evite todos os problemas, mas é muito mais segura do que substituir dados da escolha de um invasor pelo texto de um comando do shell.
- A causa do problema é
find -exec sh -c "something {}" \;
que a substituição de{}
é sem aspas e, portanto, não é tratada como uma única sequência? Na solução
find -exec sh -c 'something "$@"' sh {} \;
,primeiro
{}
é 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, se aplicável; com ou sem ofind
que pode não ser necessário) onde o mesmo tipo de problema e solução se aplica e quais 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.
Respostas:
Isso não está realmente relacionado à citação, mas ao processamento de argumentos.
Considere o exemplo arriscado:
Esta é analisado pelo shell, e dividida em seis palavras:
find
,-exec
,sh
,-c
,something {}
(sem aspas mais),;
. Não há nada para expandir. O shell rodafind
com essas seis palavras como argumentos.Quando
find
encontra algo de processo, por exemplofoo; rm -rf $HOME
, substitui{}
comfoo; rm -rf $HOME
, e corresh
com os argumentossh
,-c
, esomething foo; rm -rf $HOME
.sh
agora vê-c
e, como resultado, analisasomething foo; rm -rf $HOME
( o primeiro argumento sem opção ) 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
descobertasfoo; rm -rf $HOME
, ele substitui{}
outra vez, e corresh
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 parâmetros posicionais (a partir de$0
), se 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 maneira
produz
enquanto a segunda variante, é executada como
produz
fonte
{}
não é escapado ou citado?;
e{}
) precisam ser escapadas (com um '\') ou citadas para protegê-las da expansão pelo shell." Você quer dizer que está incorreto para o bash?findutils
manual remonta a pelo menos 1996 ... É claro que não faz mal citar{}
, como'{}'
ou"{}"
, mas não é necessário.find some/path -exec sh -c 'something "$@"' {} \;
existem três camadas de processamento / aprovação de argumentos, duas da variedade shell e uma da variedade básica do sistema operacional bruto.Parte 1:
find
apenas usa substituição de texto.Sim, se você fez sem aspas, assim:
e um invasor conseguiu criar um arquivo chamado
; echo owned
, então ele executariao que resultaria no shell sendo executado
echo
entãoecho owned
.Mas se você adicionar aspas, o invasor poderá encerrá-las e, depois, colocar o comando malicioso criando um arquivo chamado
'; echo owned
:o que resultaria na concha de corrida
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 {} \;
,{}
não é inicialmente interpretado pelo shell, é 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, portanto, na maioria dos casos, significa que o comando não fará o que você deseja.
Tê-lo expandir-se para
/tmp/foo;
,rm
,-rf
,$HOME
não deve ser um problema, porque esses são argumentos parasomething
esomething
provavelmente não trata os seus argumentos como comandos para executar.Parte 3:
Suponho que considerações semelhantes se apliquem a qualquer coisa que aceite entrada não confiável e a execute como (parte de) um comando, por exemplo,
xargs
eparallel
.fonte
xargs
só é perigoso se-I
ou-J
for usado. Em operação normal, apenas anexamos argumentos ao final da lista, assim como-exec ... {} +
faz.parallel
por outro lado, executa um shell por padrão sem solicitação explícita do usuário, aumentando a superfície vulnerável; esse é um assunto que tem sido objeto de muita discussão; consulte unix.stackexchange.com/questions/349483/… para obter uma explicação, e o link incluído para lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).parallel
processo de "receptor" remoto na outra extremidade de qualquer soquete, capaz de executar um shell-less diretoexecv
. O que é exatamente o que eu teria feito, se você estivesse implementando a ferramenta. Ter um programa não interativo se comporta de maneira diferente com base no valor atual deSHELL
dificilmente é menos surpreendente.execv
fornece, isso é mais simples e menos surpreendente, e uma regra que não muda nos locais / ambientes de tempo de execução.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 "interromper" a citação.Não.
"$@"
Expande para os parâmetros posicionais, como palavras separadas, e não os divide mais. Aqui,{}
existe um argumento parafind
ele mesmo efind
passa o nome do arquivo atual também como um argumento distinto parash
. Está diretamente disponível como uma variável no script do shell, não é processado como um próprio comando do shell.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ê citar, 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 é usada como algum tipo de código (*) , existe a possibilidade de execução arbitrária de comandos.
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 terminará a citação no código Perl, e o restante do nome do arquivo poderá 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 um argumento:
(Não faz sentido executar outro shell diretamente a partir de um shell, mas se você o fizer, é semelhante ao
find -exec sh ...
caso)(* algum tipo de código inclui SQL, XKCD obrigatório: https://xkcd.com/327/ mais explicação: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
fonte
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, de modo que, se você estender seu comando, de repente não terá uma surpresa:
Para obter mais detalhes sobre a questão de gerar um shell, consulte: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell
fonte
find . -print0 | xargs -0 printf "Argument: %s\n"
é tão seguro (ou melhor, mais, porque com-print0
um lida com nomes de arquivos com novas linhas corretamente); As citações de paralelo são uma solução alternativa para um problema que não existe quando nenhum shell está presente.| wc
ao comando, precisará pular os aros para torná-lo seguro. O GNU Parallel é seguro por padrão.foo {} | wc
emsh -c 'foo "$1" | wc
sh {} `, isso poderia ser diferente.