Aus Linux-Magazin 04/2005

Workshop: Sicheres Programmieren für Administratoren - Folge 2

Fast jedes Skript oder Programm öffnet und bearbeitet Dateien. Dieser Workshop zeigt, wo die Gefahren lauern, und erklärt Admins und Entwicklern, wie sie Fehler schon an der Wurzel neutralisieren.

Zahlreiche Fallen für den programmierenden Admin verbergen sich in ganz unschuldig aussehenden Dateioperationen, etwa ein File erzeugen, öffnen oder schließen. Teil 1 dieser Serie[1] hat bereits offene Dateien bei Programmstart und -ende behandelt; diese Folge gibt Hinweise, wie man weitere Operationen auf dem Dateisystem mit C, C++ und Shellskripten zähmt.

Die Wurzel des Übels

Unix ordnet Dateisysteme, Geräte und andere Ressourcen unterhalb des Wurzelverzeichnisses in einen Verzeichnisbaum ein, auf den viele Prozesse gleichzeitig zugreifen dürfen. Diese Parallel-Zugriffe führen oft zu so genannten Race Conditions, die sich als Sicherheitslücke manifestieren (siehe Kasten “Symlinks, Races und deren Folge”).

Listing 1:
Symlink-Schwäche

01 #!/bin/sh
02 for i in `find . -name "*.txt"`; do
03         tr "A-Z" "a-z" < $i > /tmp/unsicher.tmp
04         mv /tmp/unsicher.tmp $i
05 done

Race Conditions treten ein, wenn ein Programm oder Skript davon ausgeht, dass sich seine Umgebung nicht verändert. Während er mit Dateien hantiert, kann ein Prozess aber allerlei Überraschungen erleben: Zwischen zwei Einzelaktionen erzeugt vielleicht ein anderer Prozess eine neue Datei oder ein Verzeichnis, löscht oder verschiebt es, der Admin hängt ein Dateisystem ein oder aus (»mount()«, »umount()«), ändert den Besitzer einer Datei, ein Benutzer hängt einen symbolischen Link um, erzeugt oder löscht einen Hardlink oder ändert die Rechte einer Datei.

Namensfrage

Verwundbar sind vor allem Programme und Skripte, die sorglos mit Dateinamen werkeln. Kein gängiges Dateisystem garantiert, dass ein Name sich zu zwei verschiedenen Zeitpunkten auf denselben Inode bezieht. Dabei riskiert der Programmierer dreierlei:

  • Er operiert mit einer anderen Datei als beabsichtigt.
  • Eigenschaften der Datei ändern sich unerwartet.
  • Ein Benutzer oder ein Prozess erhält andere Zugriffsrechte
    als gewollt.

Ganz zu verhindern sind solche Pannen auch nicht durch scharfe Sicherheitsmaßnahmen, doch sollte der Entwickler sein Programm darauf vorbereiten. Bei der Abwehr darf er immerhin davon ausgehen, dass Root gefährliche Situationen nicht absichtlich herbeiführt.

Beispiel: Mail-Admin

Auf dem System des Administrators Anton laufen zwei separate Mailserver, je einer für die Teams Rot und Blau. Jedes Teammitglied besitzt eingeschränkte Administratorrechte für die Mailboxen seiner Teamkollegen. Mehrere spezialisierte Programme mit SUID-Root-Bit verleihen ihnen die nötigen Vollmachten. Das Mailverzeichnis »/var/teammail« enthält unter anderem folgende Files:

$ cd /var/teammail; ls -ld * .
drwxrwsrwt 2 root root 4096 ... ./
-rw-rw---- 1 bernd blau   11 ... bernd
-rw-rw---- 1 renate rot    0 ... renate
-rw-rw---- 1 rainer rot  738 ... rainer

Das Programm »leere-mbox« in Listing 2a leert die Mailbox eines Teamkollegen. Die Zugangskontrolle in den Zeilen 14 bis 23 stellt mit »stat()« sicher, dass die Gruppe der Mailboxdatei mit der Gruppe des Aufrufenden übereinstimmt. Anschließend öffnet »fopen()« die Datei (Zeile 25), löscht eventuell vorhandenen Inhalt oder erzeugt das File nötigenfalls neu. Zum Schluss setzt Antons Programm für den Fall, dass es die Datei neu anlegt, Gruppe und Rechte passend (Zeilen 27 bis 35).

Um die Mailbox ihres Kollegen Rainer (Team Rot) zu leeren, tippt Renate »leere-mbox rainer«. Ohne es zu ahnen hat Anton allerdings fünf kapitale Sicherheitslücken fabriziert, in die Renate nun fällt. Besser arbeitet das Programm in Listing 2b.

Falle 1: Berechtigungen

Öffnet ein SUID-Root-Programm eine Datei im Auftrag des Benutzers, tut es gut daran, die Zugriffsrechte des Users auf das gewünschte File zu überprüfen. Am besten überlässt der Entwickler diese Aufgabe dem Betriebssystem, denn das kennt alle Besonderheiten der diversen Dateisysteme. In »leere-mbox« verlässt sich Anton auf die Gruppe. Wenn Rainer seine Mailbox mit »chmod 600« geschützt hat, wird das Programm dennoch Renate den Zugriff erlauben.

Symlinks, Races und deren
Folge

Die Zutaten für einen erfolgreichen Symlink-Angriff sind ein unvorsichtiges Programm und ein global beschreibbares Verzeichnis, in dem das Programm Dateien schreibt oder liest. Meist eignen sich dazu »/tmp«, »/var/tmp« oder »/var/spool/mail«. Kombiniert mit einer Race Condition tritt die in Abbildung 1 gezeigt Situation ein: Das verwundbare Programm prüft zwar, ob das Ziel in Ordnung ist (linke Seite), aber wenn es unmittelbar danach die Aktion ausführt (rechts), kann sich die Situation schon geändert haben.

Oft ist die Situation noch schlimmer. Das einfache Skript in Listing 1 macht es dem Angreifer durch den konstanten Dateinamen »unsicher.tmp« besonders leicht, er kann fremde Dateien überschreiben, löschen oder den Inhalt manipulieren. Das Skript soll in allen ».txt«-Dateien im aktuellen Verzeichnis Groß- in Kleinbuchstaben umwandeln.

Abbildung 1: Bei Symlink-Attacken erzeugt der Angreifer zunächst einen Symlink auf eine seiner Dateien. Mit »fstat()« findet das Programm heraus, dass der Benutzer die Datei schreiben darf. Danach öffnet es die Datei zum Schreiben - zwischendurch hat der Angreifer aber den Symlink verbogen.

Abbildung 1: Bei Symlink-Attacken erzeugt der Angreifer zunächst einen Symlink auf eine seiner Dateien. Mit »fstat()« findet das Programm heraus, dass der Benutzer die Datei schreiben darf. Danach öffnet es die Datei zum Schreiben – zwischendurch hat der Angreifer aber den Symlink verbogen.

Ach wie gut, dass niemand weiß …

Bevor das Skript läuft legt der Saboteur unter dem Namen »/tmp/unsicher.tmp« einen symbolischen Link auf eine beliebige Datei an, die er unberechtigt löschen möchte, und wartet, bis der Admin das Skript aufruft. Das Skript überschreibt in Zeile 3 den Inhalt der verlinkten Datei und verschiebt dann den Symlink. Damit sind beide Files zerstört – jenes, auf das der Symlink zeigt, und jenes, dessen Inhalt der Benutzer ändern wollte.

In dem unscheinbaren Vierzeiler verbergen sich zwei weitere Angriffspunkte. Wenn ein Benutzer das Skript parallel mehrmals ausführt, um mehrere Verzeichnisse gleichzeitig zu bearbeiten, überschreiben die Prozesse gegenseitig ihre temporäre Datei und zerstören sie dabei. Zudem steht die Variable »$i« nicht in Anführungszeichen. Das kann zu unerwarteten Ergebnissen führen, wenn ein Dateiname Sonderzeichen enthält.

Weit verbreiteter Fehler

Achtung bei Selbstversuchen: Das Skript zerstört sowohl die Eingabe- als auch die Ausgabedatei (Abbildung 2). In das Linux-Magazin-Sonderheft 04/04 (Skripting Edition[2]) hat sich übrigens ein fehlerhaftes Skript aus der Praxis eingeschlichen. Es tappt genau in die beschriebene Falle.

Abbildung 2: Die Folgen eines Symlink-Angriffs. Das Skript aus Listing 1 hat mit der Ausgabe die eigentlich unbeteiligte Datei »/tmp/ziel« überschrieben und die Quelldatei »/tmp/quelle« durch einen Symlink auf »/tmp/ziel« ersetzt. Damit sind beide Files zerstört.

Abbildung 2: Die Folgen eines Symlink-Angriffs. Das Skript aus Listing 1 hat mit der Ausgabe die eigentlich unbeteiligte Datei »/tmp/ziel« überschrieben und die Quelldatei »/tmp/quelle« durch einen Symlink auf »/tmp/ziel« ersetzt. Damit sind beide Files zerstört.

Antons Programm begeht aber noch einen Fehler: Es liest erst die Datei-Informationen mit »stat()« (Zeile 15), prüft sie in Zeile 20 und öffnet dann die Datei (Zeile 25). Zwischen »stat()« und dem »fopen()«-Aufruf wechselt Rainer aber vielleicht ins Team Blau (Abbildung 3), die Daten aus dem Stat-Aufruf sind dann nicht mehr gültig.

Lösung für die Shell: Entweder schreibt der Admin ein kleines C-Programm, das die Rechte prüft, oder vermeidet solche Tests ganz. Noch besser ist es, für SUID-Root-Aufgaben auf Shellskripte zu verzichten – soweit technisch möglich.

Abbildung 3: Renate ruft »leere-mbox rainer« auf. Gleich nach dem »stat()«-Kommando wechselt Rainer ins Team Blau und bekommt eine Mail. Renate hat schon die Zugangsgenehmigung erhalten und löscht unberechtigt Rainers Mailbox mit der neuen Mail.

Abbildung 3: Renate ruft »leere-mbox rainer« auf. Gleich nach dem »stat()«-Kommando wechselt Rainer ins Team Blau und bekommt eine Mail. Renate hat schon die Zugangsgenehmigung erhalten und löscht unberechtigt Rainers Mailbox mit der neuen Mail.

Lösung für C und C++: Das Programm in Listing 2b gibt zuerst die Root-Rechte ab (Zeile 18) und öffnet dann die Datei mit »open()«, ohne sie zu überschreiben (Zeile 20). Nur wenn das klappt, darf der Anwender die Datei anfassen. Ein Wermutstropfen bleibt, denn Rainer kann immer noch nach dem Öffnen der Datei das Team wechseln. Hier helfen nur noch schwere Geschütze wie Posix Mandatory Locks[7].

Generell vermeidet der sicherheitsbewusste Entwickler die drei Funktionen »stat()«, »lstat()« und »access()« und weicht auf »open()« und »fstat()« aus.

Falle 2: Dateien öffnen

Die Funktionen der Open-Familie dienen Angreifern als Haupteinfallstor für Symlink-Attacken. Speziell »fopen()« erlaubt es dem Programmierer nicht, symbolische Links zu erkennen. Gefahr droht aber nicht nur durch den Dateinamen. Darf ein Angreifer irgendwo im Pfad in ein Verzeichnis schreiben, kann er dort Symlinks anlegen oder ganze Dateibäume verschieben oder löschen. Wie der Programmierer das überprüft, steht im Abschnitt “Falle 4”.

Lösung für die Shell: Der Admin sorgt für sichere Verzeichnisse. Setzt der Programmierer gleich am Anfang eines Skripts die Option »set -C«, überschreibt die Shell mit der Ausgabeumleitung »>« keine vorhandenen Dateien. Ein hilfreiches Bonbon: Die Zsh (und nur sie)[6] weigert sich mit »cd -s Verzeichnis« Symlinks im Pfad zu folgen.

Lösung für C und C++: Wichtig ist es, den Zugriffsmodus sorgsam zu wählen. Listing 2a (Zeile 25) macht das mit »w+« richtig, »fopen()« gewährt aber keinen Schutz vor Symlink-Angriffen. Das gelingt mit der Funktion »open()« und der Option »O_NOFOLLOW« (Listing 2b, Zeile 28). Leider gibt es »O_NOFOLLOW« nur auf GNU-Systemen, wenn »_GNU_SOURCE« definiert ist (Zeile 1). Wer einen File Pointer benötigt, behilft sich mit »fdopen()«:

int fd = open("Datei", O_RDWR);
FILE *f = fdopen(fd, "r+");

Die meisten Funktionen, die mit Pfadnamen oder mit File Pointern operieren, gibt es auch in Versionen, die Dateideskriptoren verwenden, etwa »stat()« und »fstat()«, »read()« und »fread()« sowie »truncate()« und »ftruncate()« (Listing 2b, Zeile 23). Verwirrend: Mal arbeitet eine Funktion, deren Name mit f beginnt, mit Namen oder File-Zeigern und ihre Schwester ohne f mit Deskriptoren, mal ist es genau umgekehrt.

Listing 2a: Mailbox unsicher
leeren

01 #include <errno.h>
02 #include <limits.h>
03 #include <stdio.h>
04 #include <sys/types.h>
05 #include <sys/stat.h>
06 #include <unistd.h>
07 
08 int main(int argc, char **argv) {
09   char fn[PATH_MAX];
10   struct stat buf;
11   int rc;
12 
13   snprintf(fn, PATH_MAX, "/var/teammail/%s", argv[1]);
14   /* Zugriffsrecht prüfen */
15   rc = stat(fn, &buf);
16   if (rc != 0 && rc != ENOENT) {
17     fprintf(stderr, "Fehlern");
18     return 1;
19   }
20   if (rc == 0 && buf.st_gid != getgid()) {
21     fprintf(stderr, "Zugriff verweigertn");
22     return 1;
23   }
24   /* Datei erzeugen oder abschneiden */
25   if (fopen(fn, "w+") == NULL)
26     return 1;
27   /* Eigentümer und Rechte setzen */
28   if (chown(fn, buf.st_uid, getgid()) != 0) {
29     remove(fn);
30     return 1;
31   }
32   if (chmod(fn, 0066) != 0) {
33     remove(fn);
34     return 1;
35   }
36 
37   return 0;
38 }

Listing 2b: Mailbox sicher
leeren

01 #define _GNU_SOURCE
02 #include <fcntl.h>
03 #include <limits.h>
04 #include <stdio.h>
05 #include <sys/types.h>
06 #include <sys/stat.h>
07 #include <unistd.h>
08 
09 int main(int argc, char **argv) {
10   char fn[PATH_MAX];
11   uid_t alte_euid;
12   int rc;
13   int fd;
14 
15   snprintf(fn, PATH_MAX, "/var/teammail/%s", argv[1]);
16   /* Zum User werden */
17   alte_euid = geteuid();
18   seteuid(getuid());
19   /* Bestehende Datei öffnen */
20   fd = open(fn, O_NOFOLLOW);
21   if (fd >= 0) {
22     /* existiert, Zugriff erlaubt, abschneiden */
23     rc = ftruncate(fd, 0);
24     close(fd);
25     return (rc == 0);
26   }
27   /* Neue Datei erzeugen */
28   fd = open(fn, O_NOFOLLOW | O_CREAT | O_EXCL, 0660);
29   if (fd < 0)
30     return 1;
31   close(fd);
32   /* Als Root die Gruppe setzen */
33   seteuid(alte_euid);
34   if (fchown(fd, getuid(), getgid()) != 0) {
35     unlink(fn);
36     return 1;
37   }
38 
39   return 0;
40 }

Falle 3: Dateien erzeugen

Neben dem bisher Gesagten sollte der Entwickler eine neue Datei auch gleich mit den korrekten Berechtigungen erzeugen. Setzt er diese zu spät, hat ein Angreifer die Datei vielleicht schon geöffnet[1]. Das »leere-mbox«-Programm in Listing 2a begeht diesen Fehler (Zeilen 25, 28, 32). Lösung für die Shell:

umask 077
set -C
: > "Datei"
set +C
chmod Modus Datei

Lösung für C und C++: Wieder drängt sich »open()« auf. Die Optionen »O_ CREAT« und »O_EXCL« sorgen dafür, dass die Open-Operation die Datei nur erzeugt, wenn es sie noch nicht gibt. Das dritte Argument gibt die initialen Berechtigungen an (Listing 2b, Zeile 28). Wichtig: Wählt der Programmierer die Open-Variante mit nur zwei Argumenten, erhält die Datei zufällige Berechtigungen! Die Funktion »fchown()« ersetzt »chown()« (Zeile 34).

Falle 4: Sicheres Verzeichnis

Dateien in einem Verzeichnis sind sicher vor fremden Zugriffen, wenn das Verzeichnis und alle darüber liegenden nur Root oder dem Besitzer der Datei Schreibrechte einräumen. Die Dateirechte selbst schützen nur unvollkommen, da ein Angreifer mit Schreibrecht im Verzeichnis die Datei umbenennen und unter dem alten Namen ein neues File anlegen kann.

Schutz mit Gate Guardian

Seit dem ersten Teil dieser Serie [1] ist die Bibliothek Gate Guardian [4] weiter gewachsen und enthält ab Version 0.9.3 auch eine Shared Library sowie Funktionen zum sicheren Umgang mit Dateien. Mit ihrer Hilfe ist es leicht, für sichere Verzeichnisse zu sorgen. Eine andere Lösung steht in [3].

Lösung für die Shell: Das Programm »cwdsafe« in Listing 3 überprüft das Arbeitsverzeichnis und verwendet dazu Libgateguardian. Diese Bibliothek installiert der Admin wie üblich mit »./configure && make && make install« und übersetzt anschließend »cwdsafe.c« mit dem Aufruf »gcc -o cwdsafe cwdsafe.c -lgateguardian«. Wenn er das Programm noch an eine Stelle im Pfad kopiert (etwa »/usr/local/bin«), kann jeder User »cwdsafe« in seinen Skripten nutzen:

umask 077
cd Verzeichnis
cwdsafe || exit 1
echo hallo > Datei

Lösung für C und C++: Analog zur Shell. Wie bei Gate Guardian üblich kann der Programmierer den kompletten Code einfach per Include-Direktive einbinden und aufrufen, ohne eine Bibliothek zu verwenden:

#include "fileguardian.c"
[...]
if (fileg_check_cwd_safety() != 0)
  exit(1);

Außerdem bietet Gate Guardian noch Funktionen an, die beliebige Verzeichnisse akzeptieren: »fileg_check_dir_safety_with_mode()« erlaubt es, die verbotenen Zugriffsrechte mit anzugeben, »fileg_safe_opendir()« öffnet das geprüfte Verzeichnis und liefert einen Dir-Pointer, »fileg_safe_opendir_with_ mode()« kombiniert beides.

Falle 5: Temporäre Dateien

Die beschriebenen Probleme übersehen Programmierer besonders leicht, wenn sie temporäre Daten speichern. Als Gegenmaßnahme hat das »/tmp«-Verzeichnis in der Regel das Sticky-Bit gesetzt. Auch wenn es Schreibrechte für alle bietet, darf damit nur der Eigentümer eine Datei löschen – neue Files anlegen darf aber jeder. Nur wer beim Öffnen (zum Lesen oder Schreiben) aufpasst, hat wenig zu befürchten. E

Listing 3:
»cwdsafe.c«

01 #include <fileguardian_headers.h>
02 
03 int main(void) {
04   return !!fileg_check_cwd_safety();
05 }
Abbildung 4: Ein Hacker (rechts) nutzt einen Bug in den Linux-Quellen für einen Symlink-Angriff. Wenn ein Benutzer den Kernel übersetzt, überschreibt er unbebabsichtigt die verlinkte Datei. Glücklicherweise funktioniert dieser Angriff mit »gcc« nicht, wenn Root das File übersetzt.

Abbildung 4: Ein Hacker (rechts) nutzt einen Bug in den Linux-Quellen für einen Symlink-Angriff. Wenn ein Benutzer den Kernel übersetzt, überschreibt er unbebabsichtigt die verlinkte Datei. Glücklicherweise funktioniert dieser Angriff mit »gcc« nicht, wenn Root das File übersetzt.

Abbildung 5: Oben: Ein argloses »find ... | xargs ...« erwischt die falschen Dateien. Unten: Mit den GNU-spezifischen Optionen »-print0« und »-0« überleben Leerzeichen in Dateinamen die Pipeline.

Abbildung 5: Oben: Ein argloses »find … | xargs …« erwischt die falschen Dateien. Unten: Mit den GNU-spezifischen Optionen »-print0« und »-0« überleben Leerzeichen in Dateinamen die Pipeline.

Der Linux-Kernel 2.6.10 enthält für die IA64-Architektur einige Skripte, die unvorsichtig schreiben. Listing 4 zeigt den Anfang von »linux-2.6.10/arch/ia64/scripts/check-gas«: Zeilen 5 und 6 belegen die Variable »out« mit dem Namen »/tmp/out PID.o« für eine temporäre Datei, die der Compiler-Aufruf in Zeile 7 schreibt. Wie leicht ein Angreifer damit per Symlink Dateien überschreiben kann, belegt Abbildung 4. Den gleichen Fehler wie in den Linux-Quellen machen Programmierer ungewöhnlich häufig. Auf dem Debian-Woody-System (3.0r4) des Autors findet der Befehl

find / -type f -perm +111 -print0 | 
xargs -0 grep '/tmp.*$$'

stolze 85 Skripte in etwa 550 Paketen, die auf einer Zeile Konstrukte wie »/tmp/out$$« enthalten. Nach persönlichen Erfahrungen ist ungefähr jedes dritte solche Skript anfällig für Symlink-Angriffe.

Lösung für die Shell: Der »mktemp«-Befehl erzeugt eine neue Datei, deren Name zum Teil von Mktemp berechnet wird, ohne auf Race Conditions hereinzufallen (Listing 5a). Im Notfall genügt auch ein beliebiger, vom Programmierer bestimmter Dateiname, wenn das Skript mit »set -C« das Überschreiben bestehender Dateien abschaltet. Listing 5a sorgt außerdem per Signal-Trap dafür, dass das Skript die temporäre Datei bei einem Programmabbruch löscht.

Lösung für C und C++: Die Funktion »tmpnam()« erzeugt einen Dateinamen, der noch frei ist. Wie man damit eine Datei sicher erzeugt, steht unter “Falle 3”. Viele Ratgeber empfehlen stattdessen zwar »mkstemp()«, doch liefert diese Routine keinen Pfad zurück. Daher kann der Programmierer das Verzeichnis nicht mehr auf Sicherheit abklopfen.

Mit Gate Guardian entstehen sichere Temp-Files mit einer der beiden Funktionen »fileg_safe_tmpfile()« oder »fileg_safe_tmpfile_with_name()« (siehe Listing 5b), sie legen entweder eine sichere temporäre Datei an oder geben »-1« zurück, wenn das nicht möglich ist.

Listing 4: Bug in den
Linux-Quellen

01 #!/bin/sh
02 dir=$(dirname $0)
03 CC=$1
04 OBJDUMP=$2
05 tmp=${TMPDIR:-/tmp}
06 out=$tmp/out$$.o
07 $CC -c $dir/check-gas-asm.S -o $out

Listing 5a: Temp-Datei in der
Shell

01 TMPF=""
02 TMPDIR=${TMPDIR-/tmp}
03 
04 clean_up () {
05   test ! x"$TMPF" = x && rm -f "$TMPF"
06 }
07 
08 trap clean_up 0 1 2 13 15
09 umask 077
10 TMPF=`mktemp -q "$TMPDIR/foo.XXXXXXXX"` || exit 1

Listing 5b: Temporäre Datei
mit Gate Guardian

01 #include <fileguardian_headers.h>
02 
03 int main(void) {
04   char name[L_tmpnam];
05   FILE *f = fileg_safe_tmpfile_with_name(name);
06   return 0;
07

Listing 6: Verbessertes Listing
1

01 #!/bin/sh
02 IFS=" tn"
03 PATH="/bin:/usr/bin"
04 umask 077
05 TDIR=${TDIR-/tmp}
06 TFILE=`mktemp -q "$TDIR/sicherer.XXXXXXXX"` || exit 1
07 for i in **/*.txt; do
08   tr "A-Z" "a-z" < "$i" > "$TFILE"
09   mv "$TFILE" "$i"
10 done

Falle 6: Dateien löschen

Für das Entfernen einer Datei bieten sich die Funktionen »unlink()« und »remove()« an. Beide nehmen den Dateinamen als Argument. Wenn jedoch ein anderer Prozess die Datei bereits gelöscht und eine neue mit demselben Namen erzeugt hat, entfernen beide Funktionen die falsche Datei. Dagegen gibt es keinerlei Schutz.

Manchmal möchte der Programmierer ganz sicher sein, dass er die gespeicherten Daten auch wirklich löscht. Ein simples »unlink()« reicht nicht aus, weil ein Angreifer einen Hardlink auf die Datei erzeugen kann. Das darf jeder Benutzer mit Leserecht für das Verzeichnis, in dem die Datei liegt. Damit überwindet der Angreifer zwar nicht die Dateirechte, aber er konserviert die Daten, bis er das System geknackt hat.

Lösung: Das Programm Wipe[5] überschreibt die Daten mehrmals mit bestimmten und zufälligen Mustern und entfernt anschließend die Datei.

Falle 7: Space in Dateinamen

Mit Leerzeichen in Dateinamen rechnet offenbar kaum ein Skriptautor. Eine kurze Suche nach Shellskripten (»grep “#!/bin/sh” *«) in den Verzeichnissen »/bin« und »/usr/bin« fördert gleich mehrere Kandidaten zu Tage: Bei dem Dateinamen »leer zeichen« müssen unter anderem »colormake«, »ps2ps«, »ps2ascii« und »xdvi« passen. Denn statt die gewünschte Datei zu bearbeiten, fummeln sie fröhlich an den beiden Files »leer« und »zeichen« herum. Besonders schlau gibt sich »gzexe«: Es verpackt die Datei zwar erfolgreich in ein selbstextrahierendes Skript, das dann beim Auspacken allerdings versagt.

Lösung: Der vorsichtige Admin setzt Shellvariablen unter allen Umständen in einfache oder doppelte Anführungszeichen (Listing 6). Haarig ist allerdings die Kommando-Substitution mit umgekehrten Anführungszeichen (Listing 1, Zeile 2) oder per »$( Befehl)«.

Am besten ist es, bei solchen Arbeiten auf Befehle auszuweichen, die auch Sonderzeichen verdauen. In Listing 6, Zeile 7 ersetzt »**/*.txt« das Kommando »find«. Den Doppelstern kennt die Zsh – neuerdings auch die Bash – seit Urzeiten. Er steht als Platzhalter für alle Dateien in allen Unterverzeichnissen.

Befehlskette

Die Shellvariable »$*« (Liste aller Optionen) wird ebenfalls oft falsch verwendet. Das folgende Skript »bad.sh« funktioniert zum Beispiel nicht zuverlässig:

#!/bin/sh
grep $*

Der Befehl »bad.sh hallo “leer zeichen”« durchsucht die Dateien »leer« und »zeichen«. Auch mit Anführungszeichen versagt das Skript: »grep “$*”«. Jetzt sucht Grep nach der Zeichenkette »hallo leer zeichen« auf der Standardeingabe. Lösung: Richtig geht es mit »$@« in doppelten Anführungsstrichen, also »grep “$@”«. Die Shell merkt sich in dieser Variablen die Wortgrenzen unabhängig von enthaltenen Leerzeichen.

Teuflisches Duo

Geradezu gemeingefährlich werden die beliebten Befehle »find« und »xargs« in Kommandos wie »find / -name “*~” | xargs rm«. In der Pipe geht die Information über eingebettete Leerzeichen, Zeilenumbrüche und Tabulatoren verloren. Dumm, wenn Find auf eine Datei namens »/etc/passwd bak~« stößt: »rm« löscht gnadenlos sowohl »/etc/passwd« als auch »/bak~«. Abbildung 5 führt das vor – aus nahe liegenden Gründen mit anderen Dateinamen.

Lösung: Die GNU-Variante von Find kennt die Option »-print0«. Damit trennt Find die Ausgabe durch Nullzeichen wie eine Zeichenkette in C. Auf der anderen Seite der Pipe sorgt die Xargs-Option »-0« dafür, dass sich beide Programme richtig verstehen. Aber Achtung: Das klappt ausschließlich mit Xargs. So ist etwa »find /tmp -name “*~” -print0 | Befehl« fast immer eine Todsünde – all die schönen Unix-Tools fangen leider mit »-print0« nichts an.

Detail-Arbeit

Bei der Arbeit mit Dateien steckt der Teufel im Detail. Dabei kratzt dieser Workshop zum Teil nur an der Oberfläche. Eine spätere Folge geht auf wichtige Themen wie Pufferüberläufe durch zu lange Pfadnamen ein. Zunächst wird sich Folge 3 mit überraschenden und böswilligen Programmeingaben befassen. (fjl)

Infos

[1] Dominik Vogt, “Umweltverschmutzung – Sicheres Programmieren für Administratoren, Folge 1”: Linux-Magazin 02/05, S. 54

[2] Peer Heinlein, “Alltagstauglich – Bash- Einsatz im echten Administrator-Leben”: Linux-Magazin-Sonderheft 04/04, S. 18, Errata dazu: [https://www.linux-magazin.de/Produkte/Bestellen/lms_2004_4.html]

[3] John Viega und Matt Messier, “Secure Programming Cookbook”: O\’Reilly, [http://www.secureprogramming.com]

[4] Dominik Vogt, Gate-Guardian-Projekt: [http://www.sourceforge.net/projects/gateguardian/]

[5] Wipe: [http://abaababa.ouvaton.org/wipe/]

[6] Zsh: [http://zsh.sunsite.dk]

[7] Marc André Selig, “Admin-Workshop zu Locks”: Linux-Magazin 12/04, S. 72

Der Autor

Dipl.-Math. Dominik Vogt ist langjähriger Software-Entwickler und Systemadministrator. Zurzeit arbeitet er als freiberuflicher EDV-Berater mit Schwerpunkt Softwaresicherheit. In seiner Freizeit werkelt er am Windowmanager Fvwm.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben