Local File Inclusion: Einige Beispiele
Ich habe schon länger keine sicherheitsrelevanten Probleme mehr gepostet, heute schauen wir uns ein paar häufig gemachte und exisierende Fehler an. Schaut euch diesen Code-Schnipsel an:
<? $url = $_POST['imageUrl']; $content = file_get_contents($url); // Put the content of that URL into a database for later use/display
Der Benutzer der Webseite kann also in ein Formular eine URL eintippen, beispielsweise die URL eines Bildes, einer XML-Datei oder einer Webseite. Das Script ruft diese Resource (z.B. ein Avatar Bildchen) ab und speichert es auf der Festplatte oder in einer Datenbank.
Auf einer anderen Seite kann der Benutzer (und vielleicht alle anderen Besucher der Webseite) dieses Bild dann abrufen.
Gibt es hier ein Sicherheitsloch? Ja, gleich mehrere! Was passiert wenn der Benutzer zum Beispiel folgende URLs eingibt:
http://192.168.0.1/monitoring.php file://admin/config.php ssh2.exec://root:password@paymentserver:22/sbin/shutdown
Je nachdem welche Wrapper zur Verfügung stehen kann man da ziemlich böse Dinge tun. Man sollte also auf jeden Fall prüfen ob die gegebene URL eine HTTP(S) URL ist die man auch erwartet und die ins Internet führt und nicht auf interne Systeme zugreift. Es sollte auch geprüft werden ob der Inhalt ein gültiges Bild (oder XML…) ist. Eventuell sollte auch geprüft werden ob curl eine bessere Lösung ist denn da stehen die PHP-Wrapper nicht zur Verfügung, wobei die Zugriffe auf interne Server via HTTP oder FTP auch damit möglich sind.
Ein ähnlicher Fall ist der folgende Skriptausschnitt:
<? $page = $_POST['p']; require_once $page.'.php';
Normalerweise wird mittels des Parameters p gesteuert welche Seite angezeigt werden soll, beispielsweise das „menu“ (menu.php), „home“ (home.php) etc. Man darf sich nicht einreden dass ein angehängtes „.php“ die Sicherheit erhöht, denn dieses kann durch ein einfaches Null-Byte oder geschickt gewählte Parameter auch umgangen werden.
Ein Angreifer könnte damit jetzt auch seinen Spass haben indem er beispielsweise folgende Werte für p übergibt:
config http://192.168.1.1/admininterface ../../../../etc/passwd\0 /kunden/web001/htdocs/useruploads/avatars/justuploaded /tmp/sess_324g234u2z34g /var/log/apache/access.log
Zweiteres geht natürlich nur wenn allow_url_include aktiviert ist.
Die dritte Zeile zeigt die Null-Byte-Variante (je nachdem ob man via Telnet arbeitet oder via Browser muss man eventuell %00 nehmen statt \0). Das „.php“ wird dadurch abgeschnitten.
Die 4. Zeile zeigt den Zugriff auf ein PHP-Script das der Angreifer kurz vorher normal hochgeladen hat, darin könnte beispielsweise ein system() Aufruf sein oder eine Reverse Shell.
Die 5. und 6. Variante laden auch Dateien die der Angreifer vorher mit entsprechenden Referrern, User-Agent-Werten oder Session-Werten gefüllt hat mit PHP-Code: <? system(‚….‘); ?>
Die erste Zeile ist relativ unspannend da die Seite weiß bleiben wird, denn in der config.php steht ja PHP Code. Der wird zwar ausgeführt, aber nicht ausgegeben und demnach wird nichts angezeigt. Aber man kommt trotzdem an den Quelltext, mit diesem kleinen Trick:
php://filter/convert.base64-encode/resource=config
Die Ausgabe könnte dann beispielsweise so aussehen:
PD9waHANCiRwd2QgPSAnYWJjMTIzJzs=
und wenn man das wiederum Base64-decodiert hat man den Quelltext der config.php Datei.
Alles hier beschriebene gehört in den großen Bereich Remote File Inclusion und Local File Inclusion, d.h. der Benutzer hat Zugriff und Einfluss auf eingebundene Dateien. An allen Stellen wo ein Benutzer das Laden oder Abrufen von Dateien beeinflussen kann muss höchste Vorsicht geboten sein und alles geprüft werden, am besten gegen Whitelists (letztes Beispiel) oder reguläre Ausdrücke.
Das Aktivieren von open_basedir hilft natürlich auch dabei dass der Angreifer nicht aus dem Projektverzeichnis herauskommt, /etc/passwd wird damit beispielsweise geschützt, aber im Projektverzeichnis oder auf anderen Servern kann der Angreifer nach wie vor tun was er will.
Wie ladet ihr Dateien von fremden Servern herunter die der User bestimmen kann?
Wie wäre es denn wenn man sich die Domain extrahiert, sich zur IP auflöst und dann prüft das sie nicht lokal ist?
Wäre das möglich bzw. wirklich sicherer? Und wenn wie? Müsste man ja auch IPv6 mit einbeziehen?
Florian Heinze
16 Mrz 12 at 10:30
gibt es wirklich noch seiten, wo solche technik eingesetzt wird? gruselig…
wolxXx
16 Mrz 12 at 10:55
Spontan würde ich sagen:
!is_file(realpath($url = trim($_REQUEST[‚p‘]))) ? $url : “;
php
16 Mrz 12 at 11:37
parse_url() und auseinander pflücken 🙂 Formal wie jede andere Validierung auch, oder wer akzeptiert noch „alles“ als Email? 😀
KingCrunch
16 Mrz 12 at 15:10
Von fremden Servern? Avatarchen? Hmmm…Kurz nachdenken…
cURL. Am besten zuvor kurz ein HEAD absetzen, Content-type aus der Antwort prüfen und dann nach dem Download per GET noch einmal auf die ersten paar Bytes prüfen. Wenn’s kein „#!“ oder „ELF“ ist, muss man nur noch hoffen, dass ein vorsorglich gestarteter Convert mit GD nicht scheitert (z. B. weil es ein JS für XSS war)…
Mehr tue ich eigentlich nicht.
UmbertoKo
18 Mrz 12 at 22:59
Ich kann nur dringlich empfehlen die SUHOSIN PHP Extension zu installieren, besonders wenn man Fremdsoftware im Einsatz hat, denn damit wird das Nullbyte Problem schon mal erschlagen.
Sven Jansen
19 Mrz 12 at 17:40
Hi,
danke für den Artikel, wieder mal sehr interessant zu lesen, was alles so möglich ist.
Inspiriert durch diesen Artikel habe ich mal weiter gesucht und bin dabei auf diesen Artikel gestossen
_http://www.exploit-db.com/papers/15108/
Dazu mal eine Frage, wenn das in Ordnung ist 🙂
Beim Punkt „2. Uploadfunktion:“ gibt es folgenden Hinweis:
„Mittels edjpgcom z.B. kann man Comments in Bilder einfügen.
So fügen wir unseren PHP Shellcode ein. Nun wird es hochgeladen und man gibt
den absoluten Pfad des Bildes/der Datei an und schwups, siehe da, der PHP-Code wird ausgeführt. “
Was ich hier nicht verstehe, wieso sollte das Bild als PHPCode interpretiert werden ?
Vielen Dank schon mal
Sven
Sven
23 Mrz 12 at 10:16
@Sven In dem Beispiel wird davon ausgegangen dass in dem anfälligen Script sowas hier steht:
include($_GET[‚file‘]);
Wenn man diesen Parameter manipuliert kann man jede Datei auf dem Server ausführen. Da eigener Code ausgeführt werden soll muss man diesen auf den Server bekommen. Das geht entweder indem PHP Code in die access.log etc. geschrieben wird, oder indem ein Bild hochgeladen wird mit einem Kommentar, der PHP Code ist.
Nehmen wir also an es wird ein Avatar Bild hochgeladen das jede Menge kryptischen Code enthält, aber auch den PHP Code:
GIF89ax………..<?php BÖSER CODE…. ?>
Diese Datei liegt dann also auf dem Server, und wenn man die dann aufruft:
http://www.seite.de/index.php?file=../../../../../../../home/fred/domains/fred777.tk/pic.gif
landet das ja im include() Aufruf und wird wie PHP Code aufgerufen, sprich der Anfang wird einfach ausgegeben, und alles zwischen den PHP Tags wird ausgeführt.
Michael Kliewe
24 Mrz 12 at 15:51
[…] Script ist, meist passiert das durch schlampige Programmierung. Hier findest du ein paar Beispiele: https://www.phpgangsta.de/local-file-…nige-beispiele wie z.B. unsaubere Programmierung aussehen kann, das ist aber nur ein Bruchteil. […]
Server schreibt in andere Dateien - Seite 2
26 Mrz 12 at 22:19
@Michael,
vielen Dank für die Erklärung. Bin ja echt baff, das so PHP Code ausgeführt werden kann…
sven
28 Mrz 12 at 11:29