Material zum Praktikumsversuch Stand: Juli 2011, zusammengestellt von: Michael Psarros (Lehrstuhl NDS) Version 2.4 Grundpraktikum zur IT-Sicherheit Sicheres CGI-Scripting Lehrstuhl für Netz- und Datensicherheit ruhr-universität bochum Bitte beachten Sie, dass dieser im Raum ID 2/168 stattfindet. Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 1/8
Was ist CGI? Die CGI-Schnittstelle (Common Gateway Interface in etwa Allgemeine Vermittlungsrechner-Schnittstelle) stellt eine Möglichkeit dar, Programme oder Scripte im Web bereitzustellen, die von HTML-Dateien aus aufgerufen werden können, und die selbst HTML- Code erzeugen und an einen Web-Browser senden können. CGI ist also die älteste Möglichkeit Webseiten dynamisch bzw. interaktiv zu machen. Um die CGI-Schnittstelle verwenden zu können, muss diese von der Web-Server-Software unterstützt werden. Dabei ist wichtig, dass diese Software dem Programm/Script immer drei Schnittstellen zur Verfügung stellt: Umgebungsvariablen (z.b. SERVER_NAME), deren Inhalte dem Programm helfen, sich vor Ort zu orientieren und über aktuelle Einstellungen zu informieren. Weiterleitung von Ausgaben, meistens als dynamisch erzeugte HTML-Seite (oder Seitenteile), aber auch als Einträge in Fehlerprotokolldateien. Einholen von Formulareingaben oder Aufrufparametern z.b. aus HTML-Seiten, damit das CGI-Programm/-Script auf diese reagieren kann. Dabei können solche Daten als Umgebungsvariable (GET-Methode) oder über einen Eingabe-Kanal (POST-Methode) Eingang ins Programm/Script finden. Wie diese Daten strukturiert sind, ist der eigentliche Inhalt des CGI-Standards. CGI Programme können also in jeder möglichen Programmiersprache geschrieben werden, welche die Standardeingabe (STDIN), die Standardausgabe (STDOUT), sowie das Lesen von Umgebungsvariablen (Environment variables) unterstützt. Die beliebtesten unter ihnen sind Perl und PHP. Was ist Perl? Die Skriptsprache PERL (practical extraction and report language) wurde 1987 von Larry Wall entwickelt. Die Bezeichnung der Sprache deutet bereits auf den ursprünglichen Zweck, nämlich dem Extrahieren von Informationen aus Dateien und der Aufbereitung dieser in einem "Report", hin. Bis dahin vorhandene Programmiersprachen wie UNIX konnten aufgrund mangelnder Flexibilität diesen Anforderungen nur in Ansätzen gerecht werden. PERL erfreute sich sofort nach seiner Veröffentlichung großer Beliebtheit und wurde aufgrund wiederum rasant steigender Anforderungen an die Programmiersprache auch von anderen Programmierern ständig weiterentwickelt. Dieser Prozess hält bis heute an. Warum Perl? Perl legt keine Restriktionen am Programmierer. Es gibt mehrere Möglichkeiten in Perl etwas zu implementieren. Auch wegen der gewöhnungsbedürftigen Syntax hat Perl den Ruf, kompliziert und nicht für Anfänger geeignet zu sein. Keith Bostic meint dazu treffend: Perl - The only language that looks the same before and after RSA encryption. Durch seine Flexibilität und die Anzahl der verfügbaren Module hat sich Perl zu einem ungeschriebenen Standard für CGI-Programme entwickelt. Was ist PHP? 1995 hat Rasmus Lerdorf eine Sammlung von Perl-Skripten unter dem Namen Personal Home Page Tools veröffentlicht. Inzwischen steht PHP für PHP: Hypertext Preprocessor und wird von der Firma Zend Technologies weiterentwickelt. PHP ist eine serverseitig interpretierte Skriptsprache, wie z.b. Microsofts Active Server Pages, und ist deswegen meistens in ein HTML-Dokument eingebetet. Inzwischen gibt es sehr viele Erweiterungen zu PHP und sie ist somit zu einer der mächtigsten Sprache für dynamische Webseiten geworden. Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 2/8
Warum PHP? Die Syntax von PHP ist leichter zu erlernen als diejenige von Perl, deswegen erfreut sich die Sprache auch großer Beliebtheit. Von der anderen Seite ist PHP zeitweise etwas ungesteuert gewachsen und deswegen in einigen Bereichen inkonsistent. Ob PHP oder Perl besser ist, ist eher einer der üblichen Glaubenskriegen, es wird hier deswegen keine Stellung dazu genommen. Sichere CGI Programmierung Folgende Punkte sind allgemein und unabhängig von der gewählten Programmiersprache bei der Programmierung von CGI-Skripten zu beachten: Überlegungen vor dem Programmieren: Was soll das Programm machen? Wie kritisch ist der Einsatzbereich? Welche Daten werden benötigt? Wie soll das Programm reagieren. Zuerst ein Konzept und eine Spezifikation erarbeiten und erst nachher programmieren! Alle Eingabewerte testen Die meisten Probleme treten auf, weil das Programm nicht richtig auf unerwartete Eingabewerte reagiert. Alle Eingabewerte müssen sehr genau überprüft werden und die Ausnahmen müssen explizit behandelt werden. Alle Argumente an Systemaufrufe überprüfen Noch wichtiger sind Eingabewerte die als Parameter bei einem Systemaufruf agieren. Sie müssen ebenfalls sehr genau überprüft werden. Rückgabewerte von Systemaufrufen überprüfen Ein Systemaufruf kann jederzeit mit einen Fehler abbrechen. In diesem Fall müssen temporäre Dateien gelöscht werden und das Programm sauber beendet werden. Immer volle Pfadangaben benutzen Statt zu vertrauen, dass das Skript im richtigen Verzeichnis ausgeführt wird, lieber den vollen Pfad für den Systemaufruf explizit angeben. umask setzen Standardmäßig ist sie bei den meisten http-dämons 000 oder 022, d.h. alle Dateien, die das CGI-Script generiert, können von jedem gelesen oder sogar überschrieben werden. Bei vertraulichen Daten sollte sie explizit auf 077 gesetzt werden. Der kritische Abschnitt eines Programms sollte so klein und einfach wie möglich sein. Das ist umso wichtiger, wenn verschiedene Prozesse miteinander konkurrieren und eine race-condition entstehen kann. Von einer anderen Person das Programm überprüfen lassen. Gemeinsam den Code besprechen und den Ablauf der prüfenden Person erklären. So können leicht logische Fehler gefunden werden. Nicht das Rad neu erfinden Nach Möglichkeit eine bereits existierende Bibliothek, die schon mehrmals überprüft wurde, benutzen, statt die Funktion selber zu schreiben. Natürlich muss man dann vorsichtig gegenüber Trojanern sein. Da lohnt es sich, vom großen Wissensschatz der Opensource-Welt zu profitieren und seine eigene Arbeit dann der Gemeinde wieder zur Verfügung zu stellen. Folgende Punkte sind Perl-spezifisch: Taint-Modus benutzen In diesem Modus wird die Benutzung von nicht geprüften Argumenten bei sicherheitskritischen Befehlen unterbunden. In diesem Modus ist garantiert, dass der Programmierer alle Eingaben richtig überprüft hat. w Parameter benutzen Alle Warnungen sollten ausgegeben werden. Mit use strict; Variablen explizit deklarieren So können Fehler vermieden werden, falls eine Variable falsch getippt wurde. Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 3/8
Folgende Punkte sind PHP-spezifisch: RegisterGlobals abstellen Da sonst beliebige Variablen eingeschleust werden können allow_url_fopen abschalten Ist auch ein beliebtes Tor für Angreifer Versuch Versuchsaufbau Nach den einleitenden Informationen erhalten Sie nun eine konkrete Versuchsbeschreibung. Auf den Rechnern ist Gentoo Linux installiert. Für diesen Versuch arbeiten Sie unter root. Das Passwort wird für den Versuch jeweils auf 12345 gesetzt. Auf Ihrem Rechner ist Apache installiert. Bitte starten Sie ihn mit: /etc/init.d/apache2 start Unter der URL http://localhost/cgiversuch.html befindet sich eine Maske zu den Skripten. Die Dateien und CGI-Skripte befinden sich in den Verzeichnisen: /var/www/localhost/cgi-restricted und /var/www/localhost/htdocs. Es gibt je ein Skript, geschrieben in Perl, PHP und ANSI-C. Sie müssen für zwei der drei Skripten folgende Schritte durchführen, welche drei es sind, ist Ihnen überlassen. Falls Sie etwas nicht verstehen, fragen Sie bitte zu Beginn den Mitarbeiter, der das Praktikum betreut. Ziel ist es, dass Sie die Vorgabe jedes Schrittes erfüllen und das Ergebnis für sich dokumentieren. Schritt 1: Was macht das jeweilige Skript? Schritt 2: Welche Sicherheitslücken weist dieses Skript auf? Welche Attacken können ausgeführt werden? Schritt 3: Korrigieren Sie die Fehler des Skriptes. Sind die Attacken weiterhin möglich? Gibt es Attacken die Sie nicht berücksichtigt haben? Ende des Versuches Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 4/8
Schriftliche Versuchsauswertung Jedes Team fertigt eine schriftliche Auswertung an. Diese sollte insbesondere die bei jedem Schritt verwendeten Befehle enthalten (also unbedingt dokumentieren, was Sie bei der Versuchsdurchführung getan haben) und die Ausgabe der Befehle erläutern. Bitte geben Sie auch Feedback, ob Sie den Praktikumsversuch als interessant empfunden haben und ob dieses Dokument für Sie bei der Versuchsdurchführung hilfreich war. Verbesserungsvorschläge sind willkommen! Die Versuchsauswertung ist schriftlich beim nächsten Versuch abzugeben. Voraussetzungen für die Teilnahme an diesem Versuch Grundkenntnisse in Perl, PHP und ANSI-C Grundkenntnisse in Arbeiten mit Linux Sie müssen mit Kommandozeilen umgehen können Dieses Dokument muss vorher gelesen und verstanden werden. Sie sollten alle Kontrollfragen beantworten können. Kontrollfragen Was ist CGI? Was ist Perl? Was ist PHP? Wie wird ein CGI-Skript aufgerufen? Was ist der Unterschied zwischen HTTP GET und POST? Für was steht das Attribut enctype in einer form -Anweisung? Welche Werte kann das Attribut enctype annehmen? Was ist der sicherheitskritischste Fehler, den man machen kann? Was ist die einfachste Methode, um in Perl solche Fehler zu umgehen? Was ist die einfachste Methode, um in PHP solche Fehler zu umgehen? Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 5/8
Weiterführende Informationen - Links CGI SelfHTML: http://de.selfhtml.org/ Wikipedia: http://de.wikipedia.org/wiki/common_gateway_interface http://de.wikipedia.org/wiki/perl http://de.wikipedia.org/wiki/php Einführung in CGI: http://www.stephan-muller.com/cgi/ http://www.html-world.de/program/cgi_ov.php Sicheres CGI-Scripting http://www.codito.de/text/perlcgi.html http://www.pi.infn.it/html/cgisecdef.html http://www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-security.html http://www.cert.org/tech_tips/cgi_metacharacters.html http://www.eekim.com/pubs/cgibook/index.html Perl Das CPAN-Archiv: http://www.cpan.org/ Dokumentation zu CGI.pm: http://stein.cshl.org/www/software/cgi/ Dokumentation zum Taint-Modus: http://gunther.web66.com/faqs/taintmode.html PHP Das PHP Extension and Application Repository: http://pear.php.net/ Hardened php project: http://www.hardened-php.net/ Impressum, Geschichte März 2005 Michael Psarros: erste Fassung Mai 2006 Michael Psarros: komplett überarbeitet. PHP-Skript von Tobias Füchtler, grammatikalische Korrekturen von Olga Paustjan. Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 6/8
Anhang CGIVersuch.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><head><title>sicheres CGI-Scripting</title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> </head><body> <h1>sicheres CGI-Scripting</h1> Bitte wählen Sie eine Programmiersprache aus und geben Sie die anzuzeigende Datei an.<br> Gültige Werte sind <tt>datei1.txt</tt> und <tt>datei2.txt</tt>.<br> (Firefox ist gegenüber Konqueror vorzuziehen, da es Textdateien direkt anzeigen kann). <br><br> <fieldset><legend> Perl </legend> <form action="/cgi-restricted/perl.cgi" method="get"> <input type="text" name="file" size="20" value="datei1.txt"/> <input type="submit" value=" OK "> <input type="reset" value="reset"> </form></fieldset> <br> <fieldset><legend> PHP </legend> <form action="/cgiversuch.php" method="get"> <input type="text" name="file" size="20" value="datei2.txt"/> <input type="submit" value=" OK "> <input type="reset" value="reset"> </form></fieldset> <br> <fieldset><legend> ANSI-C </legend> <form action="/cgi-restricted/ansic.cgi" method="get"> <input type="text" name="file" size="20" value="datei1.txt"/> <input type="submit" value=" OK "> <input type="reset" value="reset"> </form></fieldset> </body></html> perl.cgi #!/usr/bin/perl use CGI qw/:standard :html/; use CGI::Carp qw(fatalstobrowser); my $targetdir = "/var/www/localhost/htdocs/"; my $q = new CGI; my $file = $q->param("file"); printf "Content-type: text/plain\n\n"; system "cat $targetdir$file"; exit 0; cgiversuch.php <?php $targetdir = "/var/www/localhost/htdocs/"; header("content-type: text/plain"); readfile($targetdir.$_get['file']);?> Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 7/8
ansic.c #include <stdio.h> #include <stdlib.h> int main(void) { char *querydata; char fullfilename[35]; char buffer[100]; char targetdir[] = "/var/www/localhost/htdocs/"; FILE *fp; int chars; querydata = getenv("query_string"); sprintf(fullfilename, "%s%s", targetdir, querydata+5); printf("content-type: text/plain\n\n"); fflush(stdout); fp = fopen(fullfilename, "r"); if (fp == NULL) { printf("datei konnte nicht geoeffnet werden...\n"); } else { chars = fread(buffer, 1, 200, fp); fwrite(buffer, 1, chars, stdout); fclose(fp); } } return 0; Kompilieren mit: gcc -Wall -pedantic ansic.c -o ansic.cgi datei1.txt, datei2.txt Kleine Beispieltexte mit uninteressantem Inhalt. Praktikumsversuch Sicheres CGI-Scripting Lehrstuhl Netz- und Datensicherheit 8/8