J'ai écrit ceci comme une refonte de l'excellente réponse de Chris Down ci-dessus, dans le style d'un tutoriel.
En bash, vous pouvez avoir des variables shell comme ceci
$ t="hi there"
$ echo $t
hi there
$
Par défaut, ces variables ne sont pas héritées par les processus enfants.
$ bash
$ echo $t
$ exit
Mais si vous les marquez pour exportation, bash définira un indicateur qui signifiera qu’ils iront dans l’environnement des sous-processus (bien que le envpparamètre ne soit pas très visible, le programme mainde votre programme C comporte trois paramètres: main(int argc, char *argv[], char *envp[])où ce dernier tableau de pointeurs est un tableau des variables de shell avec leurs définitions).
Exportons donc tcomme suit:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Considérant que ce qui précède tétait indéfini dans le sous-shell, il apparaît maintenant après son exportation (utilisez-le export -n tsi vous souhaitez cesser de l'exporter).
Mais les fonctions de bash sont un animal différent. Vous les déclarez comme ceci:
$ fn() { echo "test"; }
Et maintenant, vous pouvez simplement appeler la fonction en l'appelant comme s'il s'agissait d'une autre commande shell:
$ fn
test
$
Encore une fois, si vous créez un sous-shell, notre fonction n'est pas exportée:
$ bash
$ fn
fn: command not found
$ exit
Nous pouvons exporter une fonction avec export -f:
$ export -f fn
$ bash
$ fn
test
$ exit
Voici la partie la plus délicate: une fonction exportée comme fnest convertie en une variable d’environnement, tout comme notre exportation de la variable shell tétait ci-dessus. Cela n'arrive pas quand fnétait une variable locale, mais après l'exportation, nous pouvons le voir comme une variable shell. Cependant, vous pouvez également avoir une variable shell régulière (c'est-à-dire non fonction) portant le même nom. bash distingue sur la base du contenu de la variable:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$ 
Nous pouvons maintenant utiliser envpour afficher toutes les variables de shell marquées pour l'exportation, ainsi que la fonction normale fnet la fonction fn:
$ env
.
.
.
fn=regular
fn=() {  echo "test"
}
$
Un sous-shell va ingérer les deux définitions: une en tant que variable normale et l'autre en tant que fonction:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Vous pouvez définir fncomme nous l’avons fait ci-dessus, ou directement comme une affectation de variable régulière:
$ fn='() { echo "direct" ; }'
Notez que c'est une chose très inhabituelle à faire! Normalement, nous définirions la fonction fncomme nous l’avons fait ci-dessus avec la fn() {...}syntaxe. Mais comme bash l'exporte via l'environnement, nous pouvons «raccourcir» directement vers la définition régulière ci-dessus. Notez que (contrairement à votre intuition, peut-être) cela n'entraîne pas une nouvelle fonction fndisponible dans le shell actuel. Mais si vous créez un shell ** sous **, alors ce sera le cas.
Annulons l'exportation de la fonction fnet laissons la nouvelle régulière fn(comme indiqué ci-dessus) intacte.
$ export -nf fn
Maintenant, la fonction fnn'est plus exportée, mais la variable régulière l' fnest et contient () { echo "direct" ; }-la.
Maintenant, quand un sous-shell voit une variable régulière qui commence par ()elle interprète le reste comme une définition de fonction. Mais c'est seulement quand un nouveau shell commence. Comme nous l’avons vu plus haut, le simple fait de définir une variable de shell régulière en commençant par ()ne le fait pas se comporter comme une fonction. Vous devez démarrer un sous-shell.
Et maintenant le bug "shellshock":
Comme nous venons de le voir, lorsqu'un nouveau shell ingère la définition d'une variable régulière en commençant par ()il l'interprète comme une fonction. Cependant, s'il y a plus de données après l'accolade fermante qui définit la fonction, elle exécute aussi ce qui est là.
Ce sont les exigences, une fois de plus: 
- La nouvelle bash est engendrée
- Une variable d'environnement est ingérée
- Cette variable d'environnement commence par "()" puis contient un corps de fonction à l'intérieur d'accolades, puis contient des commandes
Dans ce cas, un bash vulnérable exécutera les dernières commandes.
Exemple:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
La variable exportée régulière a exété transmise au sous-shell, ce qui a été interprété comme une fonction, exmais les commandes de fin ont été exécutées ( this is bad) lors de l'apparition du sous-shell.
Expliquer le test d'une ligne lisse
Un one-liner populaire pour tester la vulnérabilité de Shellshock est celui cité dans la question de @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Voici une ventilation: d'abord, le :in bash n'est qu'un raccourci pour true.  trueet les :deux évaluent à (vous l'avez deviné) vrai, en bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Deuxièmement, la envcommande (également intégrée à bash) affiche les variables d’environnement (comme nous l’avons vu ci-dessus) mais peut également être utilisée pour exécuter une seule commande avec une variable exportée (ou des variables) affectée à cette commande, et bash -cexécute une seule commande à partir de sa ligne de commande:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Donc, en cousant tout cela ensemble, nous pouvons exécuter bash en tant que commande, lui donner une tâche factice (comme bash -c echo this is a test) et exporter une variable qui commence par ()pour que le sous-shell l’interprète comme une fonction. Si shellshock est présent, il exécutera immédiatement toutes les commandes de fin du sous-shell. Comme la fonction que nous passons ne nous concerne pas (mais nous devons analyser!), Nous utilisons la fonction valide la plus courte imaginable:
$ f() { :;}
$ f
$ 
La fonction fici exécute simplement la :commande, qui renvoie true et quitte. Ajoutez maintenant à cela une commande "diabolique" et exportez une variable normale dans un sous-shell et vous gagnerez. Voici à nouveau le one-liner:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Donc, xest exporté sous forme de variable régulière avec une simple fonction valide avec echo vulnerableclouée à la fin. Ceci est passé à bash, et bash interprète xcomme une fonction (ce qui nous importe peu) puis exécute peut-être le echo vulnerablesi shellshock est présent.
Nous pourrions raccourcir un peu le one-liner en supprimant le this is a testmessage:
$ env x='() { :;}; echo vulnerable' bash -c :
Cela ne le dérange pas, this is a testmais lance à nouveau la :commande silencieuse . (Si vous quittez le, -c :vous êtes assis dans le sous-shell et devez quitter manuellement.) La version la plus conviviale serait peut-être celle-ci:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"