Riesiges PHP-Scheunentor
Ich habe eben zufällig mal ausprobiert, einen URL in der Browser-Adresszeile dahingehend zu ändern, dass die .htaccess Datei des root-Verzeichnisses von qxm.de angezeigt wird, und – was für ein Schock am Morgen! – dies bekam ich zu sehen:

Die komplette .htaccess wurde angezeigt, zwar mit falschen Zeilenumbrüchen, aber egal, schlimmer geht’s ja wohl nicht. Dieser Exploit funktionierte bei mir mit beliebigen Dateien, auch in anderen Verzeichnissen. Ich konnte mir sämtliche PHP Dateien, die auf meinem Server lagen, im Quelltext anschauen, der Zugang zu sämtlichen Daten (auch zu den .htpasswd Dateien!) war offen wie ein Scheunentor.
Achtung!
Wer PHP einsetzt, um dynamische Seiten zu erzeugen, und URLs mit einem Querystring mit Dateinamen versieht, wie z.B. in diesem URL: www.meinserver.de/?id=eintrag75.html ist höchstwahrscheinlich auch davon betroffen.
Es gibt mehrere Wege, das zu umgehen, die effektivste, die mir auf die schnelle einfiel, war, vor der Weiterverarbeitung bestimmte Zeichenfolgen im Querystring zu filtern bzw. zu ersetzen, wie hier exemplarisch gezeigt:
if (array_key_exists("id", $_GET)) {
$idFilter = $_GET["id"];
$idFilter = ereg_replace (".php", "boeserString", $idFilter);
$idFilter = ereg_replace ("htaccess", "boeserString", $idFilter);
$idFilter = ereg_replace ("htpasswd", "boeserString", $idFilter);
$idFilter = ereg_replace ("../../", "boeserString", $idFilter);
}
Ich empfehle jedem, der mit Querystrings und angehängten Dateinamen oder URLs arbeitet, schnellstens so einen Filter oder etwas ähnliches einzubauen.
Update: Ich hatte wohl etwas zu viel des Guten getan – die Kommentare gehen jetzt wieder…
Ähnliche Beiträge:
Wendetechniken und Minzeblätter Deutlich kleinere PDFs mit Mac OS X
Kommentare:
Bei den Kommentaren handelt es sich um fremde Inhalte, die sich „esse est percipi“ nicht zueigen macht. Verantwortlich für den Inhalt eines Kommentars ist der jeweilige Verfasser.
Dein Title ist ein wenig irreführend ;)
Ich sehe das nämlich nicht als Scheunentor von PHP; den schliesslich hat PHP nur das gemacht was Du wolltest: Eine per GET-Parameter angegebene Datein includiert und angezeigt.
IMHO ist das kein Scheunentor in PHP sondern ein Scheunentor in der Programmierung … Das „Problem“ ist bekannt, weshalb man eigentlich von Anfang an darauf achtet und entweder ein Array hat erlaubte Files oder Filetypes enthält o.ä. – also genau das, was Du jetzt hier als Bugfix eingesetzt hast …
Auweih … es ist echt zu früh für mich; man verzeihe meinen fürchterlichen Ausdruck; ich hoff es ist trotzdem verständlich ;-)
Am effektivsten ist die Verwendung der Funktion basename(). Was du empfiehlst, ist die Verwendung einer schwarzen Liste: Alles, was nicht explizit verboten ist, ist erlaubt. Eine weiße Liste ist deutlich sicherer: Alles, was nicht explizit erlaubt ist, ist verboten. Hier ein Beispiel.
Ehrlich gesagt würde ich das schon als Scheunentor in PHP bezeichnen. Wenn ich eine Datei per Querystring include, will ich nicht, dass jeder User ganz einfach in der Browser-Adresszeile eigene bzw. irgendwelche anderen Dateien includen kann.
Mittlerweile steht register_globals schließlich auch überall standardmäßig auf Off, und allow_url_fopen wird allmählich von den meisten Hostern auch auf Off gesetzt – die dazugehörigen Exploits hätte man durch entsprechende Programmierung ja auch umgehen können…
Das kommt ganz auf den Standpunkt an … Wie sollte PHP den zwischen ‘guten’ und ‘schlechten’ Dateien unterscheiden, wenn Du es PHP nicht sagst?
Du kannst auch mit _POST und _GET noch nette Lücken reinreißen, trotz register globals off; nimm einfach _REQUEST und schon hast Du wieder ein Loch; aber soll es deshalb _REQUEST nicht mehr geben?
Egal …
Das dürfte nicht helfen, ich könnte auch so was eingeben: ..//..//..// ...
$idFilter = ereg_replace ("../../", "boeserString", $idFilter);
wenn ich so etwas mache, dann filtere einfach die beiden Punkte, und dem / am Anfang …
Das dürfte nicht helfen, ich könnte auch so was eingeben: ..//..//..// ...
$idFilter = ereg_replace ("../../", "boeserString", $idFilter);
wenn ich so etwas mache, dann filtere einfach die beiden Punkte, und dem / am Anfang …
Das funktioniert in praktisch allen serverseitigen Skript-Sprachen so, auch in ASP oder JAVA. Man muss einfach den Code entsprechend richtig aufbauen – grundsätzlich darf man niemals über GET oder POST einen Dateinamen übergeben. Grundsätzlich muss man auch immer alle GET, POST oder COOKIE Variablen auf den Inhalt testen, bevor man sie weiter verarbeitet. Das ist kein Problem von PHP, sondern liegt in der Natur des Internets – man kann eben sehr einfach alle POST, GET oder COOKIE-Variablen fälschen.
Näheres hier: http://www.dclp-faq.de/ch/ch-security.html
„Wer Eingaben in Programmen (auch geskripteten) UNVALIDIERT verarbeitet, wird mit Speicherentzug nicht unter 512 MB bestraft!“
Nix für ungut, aber es ist DEIN Fehler; die Überschrift des Beitrages ist – gelinde gesagt – eine Unterstellung.
Warum includierst Du überhaupt? Nimm einen Datenbank-Lookup und alles ist gut. Tip: Rasmus Lehrdorf beschreibt in „Programming PHP“ ausführlich die Fallen, die einem bei leichtsinniger Verwendung von Upload, Include etc. auflauern können.
Gottseidank habe ich hier genug RAM… ;-)
Nein, ich bleibe dabei: das ist ein Riesen-Scheunentor, was PHP da potentiellen Angreifern aufmacht. Hätte ich gesagt, es sei eine Riesen-Sicherheitslücke, könnte ich den Unmut eventuell verstehen, aber ich spreche von SCHEUNENTOR, und da wird mir ja wohl keiner ernsthaft widersprechen wollen.
Denn erstens muss man das nicht extra programmieren, das ist automatisch so, wenn man diese Funktion einfach nur so benutzt, wie sie gedacht ist, und zweitens will ich das überhaupt nicht, was potentielle Angreifer da machen können: Pfade rauf- und runterwandern, mal eben schnell eine .htpasswd aufmachen etc.
Und so bekannt, wie das hier einige erzählen, scheint das im übrigen überhaupt nicht zu sein – Heise spricht zufälligerweise genau heute davon, dass das, was ich oben beschreibe, im März 2004 öffentlich gemacht wurde.
Scheunentor!
Naja wollen wir mal nich uebertreiben. Woher soll php denn wissen dass du nich wirklich /etc/passwd verarbeiten willst? Selbst wenn php innerhalb einer chroot() laeuft kann es dort auch ueberall hin. Einfach ganz normal jede potentiell unsichere eingabe (alles was irgendwann ueber $_GET $_POST oder aehnliches kommt strengstens pruefen)
Im falle von dateiinkludierung den realpath() des zielobjekts mit zulaessigem vergleichen. Ja und das offensichtliche (basename() pruefen etc..)
Aber wem sag ich das? Wird ja seit Jahr und Tag gebetsmuehlenartig wiederholt.
Aus Fehlern wird man bekanntlich klug (wenns nicht die eignen sind ist es allerdings weitaus angenehmer)
Okay, so ganz überzeugt bin ich noch nicht – man könnte es z.B. so einrichten, dass relative Pfade per Parameter explizit freigegeben werden müssen –, aber zukünftig halte ich mich wohl an folgende Devise: Traue niemandem. Validiere allen Input oder stirb. ;-)
Kommentar schreiben:
FAQ (Häufig gestellte Fragen)
Kommentare:
- Michael Preidel zu Harbor – Sicherer Hafen [Testflight]
- Henning zu Harbor – Sicherer Hafen [Testflight]
- Michael Preidel zu Harbor – Sicherer Hafen [Testflight]
- Michael Preidel zu Harbor – Sicherer Hafen [Testflight]
- Mesrop zu Harbor – Sicherer Hafen [Testflight]
- Michael Preidel zu Hopper 1.0b7 (Update: 1.0b9)
Schlagwörter