Est-il possible d'utiliser `find -exec sh -c` en toute sécurité?

30

Je suis en train d'utiliser findpour echo 0dans certains fichiers, mais apparemment , cela fonctionne uniquement avec sh -c:

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

Mais utiliser sh -cavec find -execme fait me sentir très mal à l'aise parce que je soupçonne de citer des problèmes. J'en ai un peu tripoté et apparemment mes soupçons étaient justifiés:

  • Ma configuration de test:

    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
  • Utiliser find -execsans sh -csemble fonctionner sans problème - aucune citation n'est nécessaire ici:

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
  • Mais lorsque j'utilise sh -c {}semble nécessiter une sorte de citation:

    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
  • Les guillemets doubles fonctionnent tant qu'aucun nom de fichier ne contient de guillemets doubles:

    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
  • Les guillemets simples fonctionnent tant qu'aucun nom de fichier ne contient de guillemets 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

Je n'ai pas trouvé de solution qui fonctionne dans tous les cas. Y at - il quelque chose que je donne sur, ou utilise sh -cen find -execsoi dangereux?

Martin von Wittich
la source

Réponses:

41

Ne jamais intégrer {}dans le code shell! Cela crée une vulnérabilité d'injection de commande. Notez que pour cat "{}", il est non seulement sur les "personnages, \, `, $sont aussi un problème (considérer par exemple un fichier appelé ./$(reboot)/accept_ra).

(au fait, certaines findimplémentations ne vous permettent pas de le faire, et POSIX laisse le comportement non spécifié lorsqu'il {}n'est pas seul dans un argument à find)

Ici, vous souhaitez transmettre les noms de fichiers en tant qu'arguments séparés à sh(pas dans l' argument de code ), et le shscript en ligne (l' argument de code ) pour y faire référence en utilisant des paramètres de position:

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

Ou, pour éviter d'en exécuter un shpar fichier:

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

De même pour xargs -I{}ou zsh« s zargs -I{}. N'écris pas:

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

Ce qui serait une vulnérabilité d'injection de commande de la même manière que findci-dessus, mais:

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

Ce qui a également l'avantage d'éviter d'en exécuter un shpar fichier et l'erreur quand list.txtne contient aucun fichier.

Avec zsh's zargs, vous voudrez probablement utiliser une fonction plutôt que d'invoquer sh -c:

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

Notez que dans tous les exemples ci-dessus, le second shci-dessus va dans le script en ligne $0. Vous devez utiliser quelque chose de pertinent là (comme shou find-sh), pas des choses comme _, -, --ou la chaîne vide, comme la valeur $0est utilisée pour les messages d'erreur du shell:

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

GNU parallelfonctionne différemment. Avec elle, vous ne pas à utiliser sh -ccomme parallelne lancer un shell déjà et tente de remplacer {}l'argument cité dans la bonne syntaxe pour le shell .

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
Stéphane Chazelas
la source
des thats secondes shsemble être une sorte d'espace réservé, cela fonctionne aussi si elle est remplacée par _par exemple - très utile si vous voulez appeler bash internals: find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Mais quelqu'un sait-il où cela est documenté?
Florian Fida
1
@FlorianFida Le premier argument du shell devient $0(généralement le nom du shell. Vous devez le sauter dans ce scénario afin qu'il ne mange pas l'un de vos arguments positionnels normaux. La documentation pour le -cmentionne.
Etan Reisner
2
@EtanReisner in-ulm.de/~mascheck/various/find/#embedded
Gilles 'SO- arrête d'être méchant'
1
@phk, ce n'est pas argv[0]ici, c'est juste $0le script. La page de Sven est inexacte ici, un rne fera pas entrer le shell dans un mode restreint pour autant que l'on puisse dire et zshne changera pas de mode en fonction de $0. (exec -a rksh ksh -c 'cd /')exécutera une restriction ksh, mais pas ksh -c 'cd /' rksh).
Stéphane Chazelas
1
Stéphane, si cela ne vous dérange pas, y a-t-il une réponse de votre part qui explique "pourquoi ne pas intégrer {} dans le code shell"? Je suis allé bien que toutes vos réponses sous trouver mais je n'ai pas pu en trouver une bien que je puisse jurer que j'ai vu des choses que vous aviez écrites sur ce sujet mais je ne me souviens pas si c'était une réponse, un commentaire dans le chat ou sur un autre site comme compunix ... Si / quand vous avez le temps, cela vous dérangerait d'étendre la partie "Ne jamais intégrer {}" ou pensez-vous que nous devrions avoir un Q dédié, par exemple "Conséquences pour la sécurité de l'utilisation findavec -exec sh -cet de l'intégration {}dans le code shell" ?
don_crissti