Du manuel de findutils:
Par exemple, des constructions telles que ces deux commandes
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
sont très dangereux. La raison en est que le '{}' est développé en un nom de fichier qui peut contenir un point-virgule ou d'autres caractères spéciaux pour le shell. Si par exemple quelqu'un crée le fichier,
/tmp/foo; rm -rf $HOME
les deux commandes ci-dessus pourraient supprimer le répertoire personnel de quelqu'un.Donc, pour cette raison, n'exécutez aucune commande qui transmettra des données non fiables (telles que les noms de fichiers) à des commandes qui interprètent des arguments comme des commandes à interpréter davantage (par exemple «sh»).
Dans le cas du shell, il existe une solution de contournement intelligente pour ce problème:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
Cette approche n'est pas garantie pour éviter tous les problèmes, mais elle est beaucoup plus sûre que de remplacer les données du choix d'un attaquant dans le texte d'une commande shell.
- La cause du problème est-elle
find -exec sh -c "something {}" \;
{}
est-il dû que le remplacement de est sans guillemets et n'est donc pas traité comme une chaîne unique? Dans la solution
find -exec sh -c 'something "$@"' sh {} \;
,le premier
{}
est remplacé, mais comme il{}
n'est pas cité, n'a-t-il pas"$@"
également le même problème que la commande d'origine? Par exemple,"$@"
sera étendu à"/tmp/foo;"
,"rm"
,"-rf"
et"$HOME"
?pourquoi n'est-il
{}
pas échappé ou cité?
- Pourriez-vous donner d'autres exemples (toujours avec
sh -c
ou sans celui-ci le cas échéant; avec ou sansfind
qui peuvent ne pas être nécessaires) où le même type de problème et de solution s'appliquent, et qui sont des exemples minimaux afin que nous puissions nous concentrer sur le problème et la solution avec peu de distraction possible? Voir Façons de fournir des arguments à une commande exécutée par `bash -c`
Merci.
Réponses:
Ce n'est pas vraiment lié à la citation, mais plutôt au traitement des arguments.
Prenons l'exemple risqué:
Ceci est analysé par la coquille, et divisée en six mots:
find
,-exec
,sh
,-c
,something {}
(sans guillemets plus),;
. Il n'y a rien à développer. Le shell s'exécutefind
avec ces six mots comme arguments.Quand
find
trouve quelque chose à traiter, par exemplefoo; rm -rf $HOME
, il remplace{}
avecfoo; rm -rf $HOME
, et fonctionnesh
avec les argumentssh
,-c
, etsomething foo; rm -rf $HOME
.sh
voit maintenant-c
, et en conséquence analysesomething foo; rm -rf $HOME
( le premier argument sans option ) et exécute le résultat.Considérez maintenant la variante la plus sûre:
Le shell fonctionne
find
avec les argumentsfind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Maintenant , quand
find
trouvefoo; rm -rf $HOME
, il remplace à{}
nouveau, et fonctionnesh
avec les argumentssh
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
voit-c
, et analysesomething "$@"
que la commande à exécuter, etsh
etfoo; rm -rf $HOME
que les paramètres de position ( à partir de$0
), se dilate"$@"
àfoo; rm -rf $HOME
une valeur unique , et fonctionnesomething
avec l'argument uniquefoo; rm -rf $HOME
.Vous pouvez le voir en utilisant
printf
. Créez un nouveau répertoire, entrez-le et exécutezExécution de la première variante comme suit
produit
alors que la deuxième variante,
produit
la source
{}
pas échappé ou cité?;
et{}
) doivent être échappées (avec un '\') ou citées pour les protéger de l'expansion par le shell." Voulez-vous dire que c'est incorrect pour bash?findutils
manuel remonte à au moins 1996 ... Bien sûr, cela ne fait pas de mal de citer{}
, soit comme'{}'
ou"{}"
, mais ce n'est pas nécessaire.find some/path -exec sh -c 'something "$@"' {} \;
il y a en fait trois couches de traitement / passage d'arguments, deux de la variété shell et une de la variété de base du système d'exploitation brut.Partie 1:
find
utilise simplement le remplacement de texte.Oui, si vous l'avez fait sans guillemets, comme ceci:
et un attaquant a réussi à créer un fichier appelé
; echo owned
, puis il exécuteraitce qui entraînerait l'exécution du shell
echo
puisecho owned
.Mais si vous avez ajouté des guillemets, l'attaquant pourrait simplement mettre fin à vos guillemets puis mettre la commande malveillante après lui en créant un fichier appelé
'; echo owned
:ce qui entraînerait l'exécution du shell
echo ''
,echo owned
.(si vous échangiez les guillemets doubles contre des guillemets simples, l'attaquant pourrait aussi utiliser l'autre type de guillemets.)
Partie 2:
Dans
find -exec sh -c 'something "$@"' sh {} \;
, le{}
n'est pas initialement interprété par le shell, il est exécuté directement avecexecve
, donc l'ajout de guillemets au shell n'aiderait pas.n'a aucun effet, car le shell supprime les guillemets doubles avant de s'exécuter
find
.ajoute des guillemets que le shell ne traite pas spécialement, donc dans la plupart des cas, cela signifie simplement que la commande ne fera pas ce que vous voulez.
Après l' avoir à étendre
/tmp/foo;
,rm
,-rf
,$HOME
ne devrait pas être un problème, car ce sont des arguments àsomething
, etsomething
probablement ne traite pas ses arguments comme des commandes à exécuter.Partie 3:
Je suppose que des considérations similaires s'appliquent à tout ce qui prend une entrée non fiable et l'exécute en tant que (partie d'une) commande, par exemple
xargs
etparallel
.la source
xargs
n'est dangereux que si-I
ou-J
est utilisé. En fonctionnement normal, il ajoute uniquement des arguments à la fin de la liste, tout comme le-exec ... {} +
fait.parallel
en revanche, exécute un shell par défaut sans demande explicite de l'utilisateur, augmentant la surface vulnérable; c'est une question qui a fait l'objet de nombreuses discussions; voir unix.stackexchange.com/questions/349483/… pour une explication, et le lien inclus vers lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).parallel
processus de "récepteur" distant à l'autre extrémité de n'importe quel socket, capable de faire un shell direct sans shellexecv
. C'est exactement ce que j'aurais fait si vous étiez à la place de l'outil. Un comportement non interactif basé sur la valeur actuelle deSHELL
n'est guère étonnant.execv
fournit un , c'est plus simple et moins étonnant, et une règle qui ne change pas entre les emplacements / environnements d'exécution.Dans un sens, mais citer ne peut pas aider ici . Le nom de fichier qui est remplacé à la place de
{}
peut contenir tous les caractères, y compris les guillemets . Quelle que soit la forme de citation utilisée, le nom de fichier peut contenir la même chose et "éclater" de la citation.Non se
"$@"
développe en paramètres positionnels, en tant que mots séparés, et ne les divise pas davantage. Ici,{}
est un argument àfind
lui-même etfind
passe le nom de fichier actuel également comme argument distinct àsh
. Il est directement disponible en tant que variable dans le script shell, il n'est pas traité comme une commande shell elle-même.Ce n'est pas nécessaire, dans la plupart des coquilles. Si vous exécutez
fish
, cela doit être:fish -c 'echo {}'
imprime une ligne vide. Mais peu importe si vous le citez, le shell supprimera simplement les guillemets.Chaque fois que vous développez un nom de fichier (ou une autre chaîne non contrôlée) tel quel dans une chaîne qui est considérée comme une sorte de code (*) , il existe une possibilité d'exécution de commande arbitraire.
Par exemple, cela développe
$f
directement le code Perl et causera des problèmes si un nom de fichier contient un guillemet double. La citation dans le nom de fichier mettra fin à la citation dans le code Perl, et le reste du nom de fichier peut contenir n'importe quel code Perl:(Le nom de fichier doit être un peu étrange car Perl analyse tout le code à l'avance, avant d'exécuter tout cela. Nous devons donc éviter une erreur d'analyse.)
Bien que cela passe en toute sécurité à travers un argument:
(Cela n'a pas de sens d'exécuter un autre shell directement à partir d'un shell, mais si vous le faites, c'est similaire au
find -exec sh ...
cas)(* une sorte de code inclut SQL, donc XKCD obligatoire: https://xkcd.com/327/ plus explication: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
la source
Votre préoccupation est précisément la raison pour laquelle GNU Parallel cite une entrée:
Cela ne fonctionnera pas
echo pwned
.Il exécutera un shell, de sorte que si vous étendez votre commande, vous n'aurez pas soudainement une surprise:
Pour plus de détails sur le problème de la génération d'un shell, voir: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell
la source
find . -print0 | xargs -0 printf "Argument: %s\n"
est tout aussi sûr (ou plutôt, moreso, car avec-print0
un gère correctement les noms de fichiers avec des retours à la ligne); la citation parallèle est une solution de contournement pour un problème qui n'existe pas du tout quand aucun shell n'est présent.| wc
à la commande, vous devez sauter à travers des cerceaux pour la rendre sûre. GNU Parallel est sécurisé par défaut.foo {} | wc
ensh -c 'foo "$1" | wc
sh {} `, cela pourrait être différent.