Alexander (HH): Warum verlangen manche Software (Forum, Shop) 777'er Rechte

Beitrag lesen

Moin Moin!

Noch ein paar Ergänzungen:

[...]

storage.xml mit Mode 777 ist ein genauso leichtes Opfer. Und um es noch schlimmer zu machen, ist die Datei ausführbar, ein ausreichend schlecht konfigurierter Webserver würde die Datei tatsächlich als CGI ausführen. Ob die Datei-Extension nun .xml, .rhababerquark oder .cgi ist, ist dem Unix-Derivat vollkommen egal, Dateien mit einem x-Bit werden ausgeführt.

Der Apache führt sie aber nur dann aus, wenn sie im CGI-Verzeichnis liegen. Das ist ein normales Verzeichnis, das mit Konfigurationsdirektiven (ScriptAlias) für die CGI-Ausführung freigegeben ist.

Das stimmt leider nur teilweise.

Ein geeignet konfigurierter Apache führt CGIs auch außerhalb des CGI-Verzeichnisses aus, wenn z.B. in der Konfiguration AddHandler cgi-script .cgi steht (auch bei Apache 2.0, 2.2, 2.3). Hier ist dann einzig die Datei-Extension entscheidend.

Die SetHandler-Direktive (ebenfalls mindestens seit Apache 1.3 bis einschließlich 2.3 vorhanden) kann sogar für ALLE Dateien in einem Verzeichnis durch den cgi-script-Handler jagen, damit werden ALLE ausführbaren Dateien zu CGIs.

Allerdings ist der Apache nicht der einzige, der sich für das x-Bit interessiert.

Nö, aber der Apache hat noch eine Stelle, an der das x-Bit benutzt wird: XBitHack. Der Apache sieht eine HTML-Datei, die per x-Bit für die Server Side Include Engine ausgewählt wird. So weit, so harmlos. Nur sieht das Betriebssystem diese Datei als ausführbares Programm an. Und dann ist es gar nicht mehr harmlos.

In diesem Zusammenhang sei angemerkt, dass ein Script nicht zwingend eine Shebang-Zeile ("#!/usr/local/bin/interpreter") braucht. Wenn der Kernel keine bessere Idee hat (sprich: das Dateiformat nicht erkennt), übergibt er das Script stumpf an /bin/sh. Das ist eine Altlast aus grauer Vorzeit, bevor der Shebang-Mechanismus ins Unix eingebaut wurde. Und /bin/sh arbeitet sich durch den Text, auch wenn der Text eigentlich HTML ist. HTML und ein Shell-Script zu mischen sollte durchaus möglich sein, die große Kunst ist eigentlich nur, den HTML-Shell-Hybriden dann auch noch valide zu halten.

Als Tag-Suppe funktioniert z.B. folgendes:

  
<!doctype html # ><html><head><title>HTML oder Shell?</title></head><body><!--  
echo -en "Content-Type: text/plain\r\n\r\n"  
# --><h1>HTML oder Shell?</h1><pre>  
echo "Bin ich nun ein Shell Script oder doch HTML?"  
echo "Vielleicht bin ich ja auch ein CGI?"  
exit 0  
</pre></body></html>  

Raus kommt als Shell-Script:

  
./foo.html: line 1: !doctype: No such file or directory  
Content-Type: text/plain  
  
Bin ich nun ein Shell Script oder doch HTML?  
Vielleicht bin ich ja auch ein CGI?  

Und das ist ein komplett legaler CGI-Header, gefolgt von einem text/plain-Dokument. Nicht zu sehen: die erste Zeile mit der Fehlermeldung wird nach STDERR geschrieben, der Rest nach STDOUT. Beim Aufruf als CGI landet die erste Zeile somit im Webserver-Logfile, der Rest wird vom Webserver als CGI-Antwort verarbeitet.

Der eigentliche Witz ist, das Bourne-Shells I/O-Redirection nicht nur hinter, sondern auch vor(!) dem Programm akzeptieren.

foo < bar

und

< bar foo

... sind gleichwertig, die Leerzeichen rund um "<" sind auch noch optional. Die Shell liest die Doctype-Deklaration also als Aufruf des Programms "html" mit der Datei "!doctype" als STDIN.

Über das Betriebssystem gestartet ist es egal, in welchen Verzeichnis die x-Bit-Datei liegt. Deshalb ist das x-Bit auch außerhalb des ScriptAlias nicht ungefährlich.

Genau.

PHP-Scripte sind aber nicht unbedingt mit CGI-Scripten gleichzusetzen, aber dazu komme ich gleich noch.

Welcher Mode ist also der richtige?
Für Dateien:
[...]
600 (-rw-------) für "geheime" Dateien, z.B. Konfigurationsdatei mit dem Passwort zur Datenbank, oder auch für eine Flat-File-Datenbank.
400 (-rw-------) analog, jedoch mit Schutz gegen versehentliches Überschreiben

Tippfehler, in der 400er Zeile muss das -r-------- heißen.

Richtig!

Ungerade Ziffern (das x-Bit) haben bei Dateien nichts verloren!
Für Programme:
711 (-rwx--x--x) für Programme, deren Innenleben nicht jeder sehen soll. Haken: Das funktioniert nicht für Scripte, denn sobald der Script-Interpreter läuft, muß er das Programm lesen können. Für Scripte also zähneknirschend 755.

Wenn der Script-Interpreter PHP heißt, dann braucht der kein x-Bit.

Doch, wenn PHP vom Betriebssystem via Shebang als Interpreter für ein Script geladen werden soll, muß das x-Bit gesetzt sein, sonst bekommt man errno=EPERM (Permission denied), unabhängig davon, ob es um PHP oder irgendeine andere interpretierte Sprache geht.

Wenn der Webserver (egal welcher) aufgrund seiner Konfiguration beschließt, dass eine angeforderte Datei ein PHP-Script ist, das durch einen PHP-Interpreter laufen soll, ist das x-Bit nicht zwingend notwendig (es sei denn, der Webserver besteht darauf). Auch das ist unabhängig von PHP für alle möglichen Sprachen gültig.

Der feine Unterschied ist, dass bei vielen Hostern und vielen Linux-Distributionen PHP gleich automatisch per mod_php in den Apachen eingebunden ist, während andere Sprachen via CGI oder seltener FastCGI angebunden werden. Deswegen scheint auf den ersten Blick PHP ein Sonderfall zu sein. Ist es aber nicht.

Ausnahme sind Scripts, die mit einer Shebang-Zeile beginnen und direkt über ihren Dateinamen aufgerufen werden. In der Shebang-Zeile steht dann der Pfad zum zu verwendenden Script-Interpreter.

Nö, keine Ausnahme, Regelfall. Siehe oben.

Der dahinterstehende Mechanismus steckt seit Urzeiten in jedem Unix, irgendwo tief in der Implementierung vom exec()-Systemcall sieht sich der Kernel die ersten zwei Bytes der auszuführenden Datei an. Stehen dort die "magischen" Zeichen "#" und "!", so wird evtl. folgender Whitespace übersprungen, alles bis zum nächsten Whitespace als Interpreter-Programm angenommen, und alles nach dem Whitespace bis zum Zeilenende (LF-Byte) als Optionsstring für den Interpreter. Das jeweilige Unix-Derivat bastelt sich dann die Kommandozeilen-Argumente um, schiebt den Interpreter und den Optionsstring vor die eigentlichen Argumente, und unternimmt einen neuen Anlauf, exec() mit den veränderten Argumenten auszuführen. Siehe z.B. exec.c und insbesondere binfmt_script.c in Linux.

Auch andere nicht direkt aufgerufenen Scripts benötigen kein x-Bit, also alles was à la

php script.php

aufgerufen wird, muss vom Script-Interpreter (hier die CLI-Version von php) nur gelesen werden können.

Richtig, und für jeden Interpreter richtig.

sh script.sh
perl script.pl
lua script.lua

oder

php script.sh
sh script.pl
perl script.lua
lua script.php

oder auch

php script.exe
sh script.cgi
perl script.dll
lua script.html
python script.bat

usw. funktionieren, so lange das Script für den aktuellen Benutzer lesbar ist. Wenn der Interpreter setuid root installiert ist, müßte es auch ohne Leserechte für den aktuellen Benutzer funktionieren.

Letztlich ist das genau das, was der Kernel intern macht, wenn ein Script mit einer Shebang-Zeile anfängt. Ob der Interpreter das Script mit einer "ungewohnten" Dateiendung ausführen möchte, steht auf einem anderen Blatt, in einer Unix-artigen Umgebung stören sich allerdings die wenigsten Interpreter an ungewöhnlichen Dateiendungen.

Bei binären Dateiformaten (ELF, a.out, ...) muß ein ausführbares Programm für den aktuellen Benutzer übrigens NICHT zwingend lesbar sein, es reicht, wenn der Kernel die Datei lesen (bei Netzwerk-Dateisystemen kann auch der Kernel nicht jede Datei lesen) und verstehen kann.

Gerade Ziffern (fehlendes x-Bit) haben bei Programmen nichts verloren.
Gerade Ziffern (fehlendes x-Bit) haben auch bei Verzeichnissen nichts verloren.

Verloren würde ich das nicht unbedingt bezeichnen. Es schadet nichts, nur die Funktionalität ist dann nicht da.

Klar, es schadet nicht, wenn ein Programm nicht ausführbar ist. chmod a-x /sbin/init && reboot, danach sprechen wir uns wieder ... ;-)

Die 0 als Ausnahme von der Regel habe ich vergessen, Mode 700 und 750 kann für einige Programme bzw. Verzeichnisse durchaus sinnvoll sein.

Diese Modi funktionieren nicht wie erwartet, wenn der Hoster nicht fein säuberlich die Programme der Benutzer unter deren Account laufen läßt, sondern mit einem Sammelaccount arbeitet. [...]

Dagegen hatte man bei PHP den Safe Mode erfunden, der das Problem der nicht erfolgten Trennung seitens des Systembetreibers lösen sollte. Doch das war aus Sicherheitssicht nicht der richtige Ansatz und machte mehr Probleme als er löste. [...]

Alles Gefrickel innerhalb von PHP, die Unix-Rechteverwaltung nachzubauen, funktioniert nicht.

Genau, damit meintest du den Safe Mode.

Exakt.

Es ist schon schwierig genug, ein Programm außerhalb des Webservers in einem eigenen Prozess sicher unter einem anderen User-Account laufen zu lassen, siehe suEXEC.

Innerhalb des Webserver-Prozesses (bzw. innerhalb eines Interpreters) ist das fast unmöglich, wenn man nicht für wirklich jede Funktion einen Wrapper zwischen Prozess und Kernel bastelt und damit größere Teile des Betriebssystems nachbaut. Und damit das innerhalb des Webserver-Prozesses funktioniert, muß der Webserver-Prozess mit root-Rechten laufen. Das will man in der Regel nicht, weil Angriffe auf den eigentlichen Webserver dann viel größeren Schaden anrichten können als wenn der Webserver brav unter einem unpriviligierten Account in einer abgeschotteten Umgebung (chroot, jails) läuft.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".