Ich schrieb dies als eine Wiederholung der hervorragenden Antwort von Chris Down im Stil eines Tutorials.
In bash können Sie solche Shell-Variablen haben
$ t="hi there"
$ echo $t
hi there
$
Standardmäßig werden diese Variablen nicht an untergeordnete Prozesse vererbt.
$ bash
$ echo $t
$ exit
Wenn Sie sie jedoch für den Export markieren, setzt bash ein Flag, das bedeutet, dass sie in die Umgebung von Unterprozessen gelangen (obwohl der envp
Parameter nicht sehr häufig angezeigt wird, hat das main
in Ihrem C-Programm drei Parameter: main(int argc, char *argv[], char *envp[])
wobei das letzte Zeigerarray ein Array ist von Shell-Variablen mit ihren Definitionen).
Exportieren wir also t
wie folgt:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Während oben t
in der Subshell undefiniert war, wird es jetzt angezeigt, nachdem wir es exportiert haben (verwenden export -n t
Sie diese Option, wenn Sie den Export stoppen möchten).
Aber Funktionen in der Bash sind ein anderes Tier. Sie erklären sie so:
$ fn() { echo "test"; }
Und jetzt können Sie die Funktion einfach aufrufen, indem Sie sie so aufrufen, als wäre es ein anderer Shell-Befehl:
$ fn
test
$
Noch einmal, wenn Sie eine Subshell erzeugen, wird unsere Funktion nicht exportiert:
$ bash
$ fn
fn: command not found
$ exit
Wir können eine Funktion exportieren mit export -f
:
$ export -f fn
$ bash
$ fn
test
$ exit
Hier ist der knifflige Teil: Eine exportierte Funktion wie fn
wird in eine Umgebungsvariable konvertiert, genau wie unser Export der Shell-Variablen t
oben war. Dies passiert nicht, wenn fn
es sich um eine lokale Variable handelt, aber nach dem Export können wir sie als Shell-Variable sehen. Sie können jedoch auch eine reguläre (dh nicht funktionsfähige) Shell-Variable mit demselben Namen haben. bash unterscheidet anhand des Inhalts der Variablen:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$
Jetzt können wir env
alle für den Export markierten Shell-Variablen anzeigen und sowohl die reguläre fn
als auch die Funktion fn
anzeigen:
$ env
.
.
.
fn=regular
fn=() { echo "test"
}
$
Eine Sub-Shell nimmt beide Definitionen auf: eine als reguläre Variable und eine als Funktion:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Sie können fn
wie oben beschrieben oder direkt als reguläre Variablenzuweisung definieren:
$ fn='() { echo "direct" ; }'
Beachten Sie, dass dies eine ungewöhnliche Sache ist! Normalerweise definieren wir die Funktion fn
wie oben mit der fn() {...}
Syntax. Aber da bash es durch die Umgebung exportiert, können wir direkt zur oben genannten regulären Definition "abkürzen". Beachten Sie, dass dies (möglicherweise entgegen Ihrer Intuition) nicht zu einer neuen Funktion fn
in der aktuellen Shell führt. Aber wenn Sie eine ** sub ** Shell erzeugen, dann wird es.
Brechen wir den Export der Funktion ab fn
und lassen Sie die neue reguläre Funktion fn
(wie oben gezeigt) intakt.
$ export -nf fn
Jetzt wird die Funktion fn
nicht mehr exportiert, sondern die reguläre Variable fn
ist, und sie enthält () { echo "direct" ; }
darin.
Wenn eine Subshell nun eine reguläre Variable sieht, die damit beginnt ()
, interpretiert sie den Rest als Funktionsdefinition. Dies ist jedoch nur dann der Fall, wenn eine neue Shell beginnt. Wie wir oben gesehen haben, ()
verhält sich eine reguläre Shell-Variable, die mit beginnt, nicht wie eine Funktion. Sie müssen eine Unterschale starten.
Und jetzt der "Shellshock" Bug:
Wie wir gerade gesehen haben, ()
interpretiert eine neue Shell die Definition einer regulären Variablen, die damit beginnt , als Funktion. Wenn jedoch nach der schließenden Klammer, die die Funktion definiert, mehr angegeben ist, wird ausgeführt, was auch immer vorhanden ist.
Dies sind noch einmal die Anforderungen:
- Neue Bash wird erzeugt
- Eine Umgebungsvariable wird aufgenommen
- Diese Umgebungsvariable beginnt mit "()" und enthält dann einen Funktionskörper in geschweiften Klammern und hat danach Befehle
In diesem Fall führt eine verwundbare Bash die letzteren Befehle aus.
Beispiel:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
Die reguläre exportierte Variable ex
wurde an die Subshell übergeben, die als Funktion interpretiert wurde, aber die nachfolgenden ex
Befehle wurden ausgeführt ( this is bad
), als die Subshell erzeugt wurde.
Erklären Sie den glatten Einzeilentest
Ein beliebter Einzeiler zum Testen der Shellshock-Sicherheitsanfälligkeit ist der in @ jippies Frage genannte:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Hier ist eine Aufschlüsselung: Zuerst ist die :
In-Bash nur eine Abkürzung für true
. true
und :
beide bewerten zu (Sie haben es erraten) wahr, in bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Zweitens gibt der env
Befehl (der ebenfalls in bash integriert ist) die Umgebungsvariablen aus (wie wir oben gesehen haben), kann aber auch verwendet werden, um einen einzelnen Befehl mit einer exportierten Variablen (oder Variablen) auszuführen, die diesem Befehl zugewiesen wurde, und bash -c
führt dann einen einzelnen Befehl aus Befehlszeile:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Wenn wir also all diese Dinge zusammennähen, können wir bash als Befehl ausführen, ihm eine Dummy-Aufgabe geben (wie bash -c echo this is a test
) und eine Variable exportieren, die mit beginnt, ()
sodass die Subshell sie als Funktion interpretiert. Wenn Shellshock vorhanden ist, werden auch alle nachfolgenden Befehle in der Subshell sofort ausgeführt. Da die übergebene Funktion für uns irrelevant ist (aber geparst werden muss!), Verwenden wir die kürzeste gültige Funktion, die man sich vorstellen kann:
$ f() { :;}
$ f
$
Die Funktion f
hier führt nur den :
Befehl aus, der true zurückgibt und beendet wird. Hänge nun einen "bösen" Befehl an diesen an und exportiere eine reguläre Variable in eine Subshell und du gewinnst. Hier ist wieder der Einzeiler:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Wird x
also als reguläre Variable mit einer einfachen gültigen Funktion exportiert echo vulnerable
, die bis zum Ende angeheftet ist. Dies wird an bash übergeben und bash interpretiert x
als eine Funktion (die uns nicht interessiert) und führt dann möglicherweise das Kommando echo vulnerable
if shellshock aus.
Wir könnten den Einzeiler ein wenig verkürzen, indem wir die this is a test
Nachricht entfernen :
$ env x='() { :;}; echo vulnerable' bash -c :
Dies stört nicht this is a test
, führt aber den stillen :
Befehl erneut aus. (Wenn Sie das -c :
dann weglassen, sitzen Sie in der Unterschale und müssen manuell beenden.) Die vielleicht benutzerfreundlichste Version wäre diese:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"