Erläuterung des Befehls zum Überprüfen von Shellshock

32

Hier ist der Befehl, mit dem ich meine Bash-Shell auf den Shellshock-Fehler überprüft habe:

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

Kann jemand bitte den Befehl im Detail erklären?

heemayl
quelle
4
Siehe auch: unix.stackexchange.com/q/157329/70524 - Die Antwort des Fixee kann hilfreich sein.
Muru

Antworten:

45

Diese Antwort ist eine Ableitung eines Originalartikels im Fedora Magazine von Matthew Miller, lizenziert unter der Creative Commons Namensnennung-Weitergabe unter gleichen Bedingungen 4.0 .

Lassen Sie mich erklären:

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

Dies gibt "OOPS" auf einem anfälligen System aus, beendet sich jedoch unbemerkt, wenn bash gepatcht wurde.

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

Dadurch wird "OOPS" auf einem anfälligen System gedruckt, es wird jedoch gedruckt, “this is a test”wenn bash gepatcht wurde.

Und Sie haben wahrscheinlich gehört, dass es etwas mit Umgebungsvariablen zu tun hat. Aber warum wird Code in Umgebungsvariablen ausgeführt? Nun, das soll es nicht sein - aber aufgrund eines Merkmals, das ich in der Versuchung bin, es ein bisschen zu schlau zu nennen, gibt es etwas Raum für einen Fehler. Bash ist das, was Sie als Terminal-Eingabeaufforderung sehen, aber es ist auch eine Skriptsprache und kann Funktionen definieren. Das machst du so:

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

und dann hast du einen neuen befehl. Denken Sie daran, dass das echohier noch nicht ausgeführt wird. Es wird nur gespeichert, was passieren wird, wenn wir unseren neuen Befehl ausführen. Dies wird in einer Minute wichtig sein!

$ Ubuntu
 Ubuntu is awesome.

Sinnvoll! Aber lassen Sie uns aus irgendeinem Grund sagen, wir müssen eine neue Instanz von bash als Unterprozess ausführen und möchten meinen fantastischen neuen Befehl unter diesem ausführen. Die Anweisung bash -c somecommandmacht genau das: Führt den angegebenen Befehl in einer neuen Shell aus:

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

Oh. Traurig. Das Kind hat die Funktionsdefinition nicht geerbt. Die Umgebung ist jedoch inhärent - eine Sammlung von Schlüssel-Wert-Paaren, die aus der Shell exportiert wurden. (Dies ist ein komplettes Konzept. Wenn Sie damit nicht vertraut sind, vertrauen Sie mir jetzt.) Und wie sich herausstellt, kann bash auch Funktionen exportieren. So:

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

Was alles gut und schön ist - außer dass der Mechanismus, durch den dies erreicht wird, ein bisschen zweifelhaft ist . Da es für die Ausführung von Funktionen in Umgebungsvariablen keine Linux / Unix-Magie gibt, erstellt die Exportfunktion im Grunde genommen nur eine reguläre Umgebungsvariable, die die Funktionsdefinition enthält. Wenn dann die zweite Shell die "eingehende" Umgebung liest und auf eine Variable mit Inhalten stößt, die wie eine Funktion aussehen, wertet sie diese aus.

Theoretisch ist dies absolut sicher , da das Definieren einer Funktion sie nicht tatsächlich ausführt . Außer - und aus diesem Grund sind wir hier - gab es einen Fehler im Code, bei dem die Auswertung nicht gestoppt wurde, als das Ende der Funktionsdefinition erreicht wurde. Es läuft einfach.

Das würde niemals passieren, wenn die in einer Umgebungsvariablen gespeicherte Funktion mit legitim gemacht wird export -f. Aber warum echt sein? Ein Angreifer kann einfach eine alte Umgebungsvariable erfinden, und wenn es wie eine Funktion aussieht, werden neue Bash-Shells denken, dass dies der Fall ist!

Also, in unserem ersten Beispiel:

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

Der envBefehl führt einen Befehl mit einem bestimmten Variablensatz aus. In diesem Fall stellen wir xetwas ein, das wie eine Funktion aussieht. Die Funktion ist nur eine einzige :, was eigentlich ein einfacher Befehl ist, der definiert ist, nichts zu tun. Aber dann, nach dem, semi-colonwas das Ende der Funktionsdefinition signalisiert, gibt es einen echoBefehl. Das sollte nicht da sein, aber es hindert uns nichts daran, es zu tun.

Dann ist der Befehl, der zum Ausführen mit dieser neuen Umgebung gegeben wird, eine neue Bash-Shell, wiederum mit einem " echo this is a test" oder "nichts tun": " enthält. sie völlig harmlos beendet.

Aber - hoppla! Wenn diese neue Shell gestartet wird und die Umgebung liest, gelangt sie zur xVariablen, und da sie wie eine Funktion aussieht, wertet sie sie aus. Die Funktionsdefinition ist harmlos geladen - und dann wird auch unsere schädliche Nutzlast ausgelöst. Wenn Sie die oben genannten Schritte auf einem anfälligen System ausführen, werden Sie darauf hingewiesen “OOPS”. Oder ein Angreifer kann viel Schlimmeres tun, als nur Dinge zu drucken.

αғsнιη
quelle
1
Muchas Gracias für eine hervorragende Erklärung, warum dies funktioniert.
Doug R.
2
Beachten Sie, dass dies envnicht erforderlich ist. Sie können das gleiche Ergebnis (bestanden / nicht bestanden , je nachdem ob Bash aktualisiert wurde) , indem Sie den Befehl ohne es: x='() { :;}; echo OOPS' bash -c "echo this is a test". Dies liegt daran, dass vor einem Befehl mit einer Variablenzuweisung diese Variable und ihr Wert an die bash -c "..."Umgebung des Befehls ( in diesem Fall) übergeben werden.
Bis auf weiteres angehalten.
1
... aber in einigen neueren Patches ist dies möglicherweise erforderlich. Die Dinge sind in Bewegung.
Bis auf weiteres angehalten.
4
@DennisWilliamson Ob dies enverforderlich ist oder nicht , hängt von der Shell ab, von der aus der Test ausgeführt wird, nicht von der zu testenden Shell. (Diese können identisch sein. Selbst dann testen wir, wie bash seine eigene Umgebung verarbeitet.) Shells im Borowski-Stil akzeptieren NAME=value commandSyntax. C-Stil Schalen (zB csh, tcsh) nicht. Daher ist der Test etwas portabler env(auf Kosten von Verwirrung, wie er manchmal funktioniert).
Eliah Kagan
2

In der ungepatchten Versionbash werden exportierte Funktionsdefinitionen als Umgebungsvariablen gespeichert.

Speichern Sie eine Funktion xals,

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

Und überprüfen Sie seine Definition als,

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

Man könnte dies also ausnutzen, indem man seine eigenen Umgebungsvariablen definiert und sie als Funktionsdefinitionen interpretiert. Zum Beispiel env x='() { :;}'würde als behandelt werden

x() { :;
}

Was bewirkt der Befehl zum Überprüfen von Shellshock?

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

Von man env,

  1. env - Führen Sie ein Programm in einer geänderten Umgebung aus.

  2. :Mach nichts anderes als Exits mit Exit-Status 0. sehen mehr

  3. Wenn eine neue Instanz von Bash ohne Patch gestartet wird bash -c "echo this is a test", wird die gestaltete Umgebungsvariable als Funktion behandelt und geladen. Dementsprechend bekommt man die Ausgabe

    verletzlich
    das ist ein Test

Hinweis: Das Echo außerhalb der Funktionsdefinition wurde beim Bash-Start unerwartet ausgeführt. Die Funktionsdefinition ist nur ein Schritt, um die Evaluierung und den Exploit durchzuführen. Die Funktionsdefinition selbst und die verwendete Umgebungsvariable sind beliebig. Die Shell betrachtet die Umgebungsvariablen, sieht x, das den Einschränkungen entspricht, die für das Aussehen einer Funktionsdefinition bekannt sind, und wertet die Zeile aus. Dabei wird unbeabsichtigt auch das Echo ausgeführt (bei dem es sich um einen beliebigen Befehl handeln kann, der böswillig ist oder nicht). . Siehe auch dies

souravc
quelle
Ich fand immer noch, dass jede definierte Bash-Funktion, wenn sie exportiert wird, in der Child-Shell in einer gepatchten Version von Bash ausgewertet wird. Siehe dies: chayan @ chayan: ~ / testr $ test () {echo "irgendetwas"; }; Export -f Test; bash -c test Ausgabe: alles Also deine Antwort ist etwas nicht richtig gerichtet. Ich denke, KasiyAs Erklärung des Fehlers, die Variable über ihre Definition hinaus zu erweitern, ist korrekt.
Heemayl
@heemayl dieses Verhalten ist natürlich. Aber wenn Sie es versuchen env test='() { echo "anything"; }' bash -c "echo otherthing", werden Sie am Ausgang sehen otherthing. Das ist im Patch korrigiert. Fühlen Sie sich frei, wenn ich noch nicht klar bin.
Souravc
Bitte machen Sie mich noch einmal klar. In Ihrem letzten Kommentar definieren wir im Wesentlichen die Funktion und weisen bash an, das Echo auszuführen. In diesem Beispiel haben wir die Funktion nicht in bash aufgerufen. Wäre dies nicht die gleiche Ausgabe in gepatchter und nicht gepatchter Bash? Ich habe eine Ahnung, dass der Fehler im Grunde war, als Bash Befehle ausführte, die nach der Funktionsdefinition platziert wurden, während die Funktion später nirgends aufgerufen wurde, zum Beispiel wenn wir diesen env-Test durchführen = '() {echo "anything"; }; echo "foo" 'bash -c "echo otherthing" Bitte klären Sie mich in diesem Zusammenhang.
Heemayl
@heemayl Ich habe meine Antwort bearbeitet, hoffe jetzt ist es klar. Sie haben im Beispiel in meinem letzten Kommentar Recht, wir haben die Funktion nicht aufgerufen. Der Unterschied ist jedoch, dass Sie in einem unpatched bashdie Funktion aufrufen können, wie sie definiert ist, aber in einem gepatchten bashdie Definition selbst nicht vorhanden ist.
Souravc
@heemayl: Nein, das ist falsch. Ein gepatchter Bash übergibt die Funktionsdefinition weiterhin an die Umgebung des Kindes. Der Unterschied, den der Patch macht, besteht darin, dass der Code, der der Funktionsdefinition ( echo vulnerable) folgt, nicht ausgeführt wird. Beachten Sie, dass in den neuesten Patches die übergebene Funktion ein bestimmtes Präfix ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test") haben muss. Einige neuere Patches können %%anstelle des ersten verwendet werden ().
Bis auf weiteres angehalten.