Explication de la commande pour vérifier le shellshock

32

Voici la commande que j'ai utilisée pour vérifier le shell Shellshock dans mon shell bash:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Quelqu'un peut-il s'il vous plaît expliquer la commande en détail?

heemayl
la source
4
Voir aussi: unix.stackexchange.com/q/157329/70524 - La réponse de Fixee pourrait être utile.
muru

Réponses:

45

Cette réponse est un dérivé d'un article original du magazine Fedora par Matthew Miller, sous licence Creative Commons Attribution-Share Alike 4.0 .

Laissez-moi expliquer:

env x='() { :;}; echo OOPS' bash -c :

Ceci imprimera “OOPS” sur un système vulnérable, mais se fermera silencieusement si bash a été corrigé.

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Ceci imprimera “OOPS” sur un système vulnérable, mais imprimera “this is a test”si bash a été corrigé.

Et vous avez probablement entendu dire que cela a quelque chose à voir avec les variables d'environnement. Mais pourquoi le code des variables d’environnement est-il exécuté? Eh bien, ce n'est pas censé l'être - mais, à cause d'une fonctionnalité que je serais tenté d'appeler un peu trop intelligente pour son propre bien, il y a de la place pour un défaut. Bash est ce que vous voyez comme une invite de terminal, mais c’est aussi un langage de script qui permet de définir des fonctions. Vous faites ça comme ça:

$ Ubuntu()  { echo "Ubuntu is awesome."; }

et alors vous avez une nouvelle commande. Gardez à l'esprit que l' echoici n'est pas encore exécuté; c'est enregistré comme ce qui se passera lorsque nous exécuterons notre nouvelle commande. Ce sera important dans une minute!

$ Ubuntu
 Ubuntu is awesome.

Utile! Mais, disons, pour une raison quelconque, nous devons exécuter une nouvelle instance de bash, en tant que sous-processus, et vouloir exécuter ma nouvelle commande impressionnante dans ce contexte. L'instruction bash -c somecommandfait exactement ceci: exécute la commande donnée dans un nouveau shell:

$ bash -c Ubuntu
  bash: Ubuntu: command not found

Ooh. Triste. L'enfant n'a pas hérité de la définition de fonction. Mais cela fait partie de l’environnement - une collection de paires clé-valeur qui ont été exportées à partir du shell. (Ceci est un concept complet. Si vous ne le connaissez pas encore, faites-moi confiance pour le moment.) Et il s'avère que bash peut également exporter des fonctions. Alors:

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

Ce qui est bien beau - sauf que le mécanisme par lequel ceci est accompli est en quelque sorte louche . Fondamentalement, comme il n’existe pas de magie Linux / Unix pour exécuter des fonctions dans des variables d’environnement, la fonction d’exportation crée en réalité une variable d’environnement ordinaire contenant la définition de la fonction. Ensuite, lorsque le second shell lit l'environnement «entrant» et rencontre une variable dont le contenu ressemble à une fonction, il l'évalue.

En théorie, cela est parfaitement sûr car, rappelez-vous, définir une fonction ne l' exécute pas . Sauf que - et c'est pourquoi nous sommes ici - il y avait un bogue dans le code où l'évaluation ne s'est pas arrêtée lorsque la fin de la définition de la fonction a été atteinte. Cela ne fait que commencer.

Cela ne se produirait jamais lorsque la fonction stockée dans une variable d’environnement est légitimement créée, avec export -f. Mais pourquoi être légitime? Un attaquant peut créer n'importe quelle ancienne variable d'environnement, et si cela ressemble à une fonction, les nouveaux shell bash penseront que c'est le cas!

Donc, dans notre premier exemple:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

La envcommande exécute une commande avec un jeu de variables donné. Dans ce cas, nous définissons xquelque chose qui ressemble à une fonction. La fonction est juste une simple :, qui est en fait une simple commande qui est définie comme ne rien faire. Mais ensuite, après le semi-colonsigne de la fin de la définition de la fonction, il y a une echocommande. Ce n'est pas censé être là, mais rien ne nous empêche de le faire.

Ensuite, la commande donnée pour fonctionner avec ce nouvel environnement est un nouveau shell bash, toujours avec une commande “ echo this is a test” ou “ne rien faire :”, après quoi il se fermera de manière totalement inoffensive.

Mais - oups! Lorsque ce nouveau shell démarre et lit l'environnement, il passe à la xvariable et, puisqu'il ressemble à une fonction, il l'évalue. La définition de la fonction est chargée de manière inoffensive - puis notre charge utile malveillante est également déclenchée. Donc, si vous exécutez ce qui précède sur un système vulnérable, vous serez de “OOPS”nouveau imprimé. Ou bien, un attaquant pourrait faire bien pire que simplement imprimer des choses.

αғsнιη
la source
1
Muchas gracias pour une excellente explication de la raison pour laquelle cela fonctionne.
Doug R.
2
Notez que ce envn'est pas nécessaire. Vous pouvez obtenir le même résultat (réussite / échec selon que Bash a été mis à jour) en utilisant la commande sans elle: x='() { :;}; echo OOPS' bash -c "echo this is a test". En effet, le fait de faire précéder une commande avec une affectation de variable transmet cette variable et sa valeur dans l' bash -c "..."environnement de la commande ( dans ce cas).
pause jusqu'à nouvel ordre.
1
... mais cela peut être nécessaire dans certains des correctifs les plus récents. Les choses évoluent.
pause jusqu'à nouvel ordre.
4
@DennisWilliamson Que ce soit envnécessaire ou non est déterminé par le shell à partir duquel on effectue le test, pas par le shell en cours de test. (C’est peut-être la même chose. Même dans ce cas, nous testons la façon dont bash traite son propre environnement.) Les shells de type Bourne acceptent la NAME=value commandsyntaxe; Coquilles de type C (par exemple csh, tcsh) ne le font pas. Le test est donc un peu plus portable env(au prix de créer parfois une confusion sur son fonctionnement).
Eliah Kagan
2

Dans sa version non corrigée,bash il stocke les définitions de fonctions exportées sous forme de variables d’environnement.

Enregistrer une fonction en xtant que,

$ x() { bar; }
$ export -f x

Et vérifiez sa définition comme,

$ env | grep -A1 x
x=() {  bar
}

Donc, on pourrait exploiter cela en définissant ses propres variables d'environnement et les interpréter comme des définitions de fonctions. Par exemple, env x='() { :;}'serait traité comme

x() { :;
}

Que fait la commande pour vérifier shellshock,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

de man env,

  1. env - exécuter un programme dans un environnement modifié.

  2. :ne rien faire mais quitte avec le statut de sortie 0. voir plus

  3. Lorsqu'une nouvelle instance de bash non corrigée est lancée en tant que bash -c "echo this is a test", la variable d'environnement construite est traitée comme une fonction et chargée. En conséquence on obtient la sortie

    vulnérable
    c'est un test

Remarque: l'écho en dehors de la définition de la fonction a été exécuté de manière inattendue lors du démarrage de bash. La définition de la fonction est juste une étape pour obtenir l'évaluation et l'exploitation, la définition de la fonction elle-même et la variable d'environnement utilisée sont arbitraires. Le shell examine les variables d'environnement, voit x, ce qui donne l'impression qu'il respecte les contraintes sur la définition d'une fonction, et évalue la ligne en exécutant involontairement aussi l'écho (qui peut être n'importe quelle commande, malveillante ou non). . Voir aussi cette

souravc
la source
J'ai toujours trouvé que toute fonction bash définie, si elle était exportée, était évaluée dans le shell enfant dans la version corrigée de bash. Voir ceci: chayan @ chayan: ~ / testr $ test () {echo "n'importe quoi"; }; export -f test; test bash -c Ouput: n'importe quoi Votre réponse est donc mal dirigée. Je pense que l'explication de kasiyA sur le bogue comme élargir la variable au-delà de sa définition est correcte.
Heemayl
@heemayl ce comportement est naturel. Mais si vous essayez, env test='() { echo "anything"; }' bash -c "echo otherthing"vous verrez à la sortie otherthing. Cela est corrigé dans le patch. Sentez-vous libre si je ne suis toujours pas clair.
Souravc
S'il vous plaît faites-moi clairement une fois de plus. Dans votre dernier commentaire, nous définissons essentiellement la fonction, puis nous demandons à bash d’exécuter echo. Dans cet exemple, nous n’avons pas appelé la fonction in bash. Cela n'aurait-il pas la même sortie en bash patché et non patché? J'ai l'impression que le bogue était fondamentalement lié au fait que bash exécutait des commandes placées après la définition de la fonction, alors que la fonction n'était jamais appelée nulle part plus tard, par exemple si nous faisons cela env test = '() {echo "rien"; }; echo "foo" 'bash -c "echo otherthing". S'il vous plaît clarifiez-moi dans ce contexte.
Heemayl
@heemayl J'ai modifié ma réponse, j'espère que c'est clair. Vous avez raison dans l'exemple de mon dernier commentaire, nous n'avons pas appelé la fonction. Mais la différence est que dans un, unpatched bashvous pouvez appeler la fonction telle qu'elle est définie, mais dans un correctif, bashla définition elle-même n'y figure pas.
Souravc
@heemayl: Non, c'est inexact. Un Bash corrigé passera quand même la définition de la fonction dans l'environnement de l'enfant. La différence que fait le patch est que le code qui suit la fonction definition ( echo vulnerable) n'est pas exécuté. Notez que dans les derniers patchs, la fonction transmise doit avoir un préfixe spécifique ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"). Certains correctifs plus récents peuvent utiliser %%au lieu du premier ().
pause jusqu'à nouvel ordre.