Aus Linux-Magazin 01/2003

Systrace setzt Regeln für erlaubte Systemaufrufe durch

Ob Webserver, Browser, IRC-Client oder Audioplayer: Sicherheitslücken in Clients und Servern führen dazu, dass die Programme schädliche Aktionen ausführen können. Systrace verhindert diese Folgen – es zwängt die Applikation in ein eng geschnürtes Korsett von erlaubten Sytemaufrufen.

Der Kernel-Pförtner Systrace zwingt Prozesse, sich an eine Policy (ein Regelwerk) für ihre Systemaufrufe zu halten, und schränkt damit den Zugriff auf den Host ein. Das verhindert zwar keine Sicherheitslücken, vermindert aber deren Folgen: Wenn ein Programm keine weiteren Prozesse starten muss, sperrt die Systrace-Policy den dazu nötigen Syscall. Ein Eindringling kann so keine Shell aufrufen, selbst wenn er einen Prozess vollständig kontrolliert.

Um die Policy durchzusetzen, fängt Systrace die Systemaufrufe im Kernel ab und führt nur die vorgesehenen Funktionen tatsächlich aus. Wenn eine Applikation versucht, außerhalb ihrer Grenzen zu arbeiten, warnt ein GUI den Anwender und fordert ihn dazu auf, die Aktion zu erlauben oder zu verbieten. Systrace besteht aus einem Kernel-Patch, einem Kommandozeilen-Programm und einem Gtk-GUI (BSD-lizenziert). Alle drei Komponenten sind auf[1] erhältlich.

Über Systemcalls erhalten Userspace-Applikationen Zugang zum Kernel. Systemaufrufe bieten Dienste in sicherheitskritischen Bereichen an, etwa beim Umgang mit Dateien, Netzwerkverbindungen oder Heap-Speicher. Tabelle 1 zeigt eine Übersicht gängiger Syscalls – die meisten Unix-ähnlichen Betriebssysteme kennen über 200 dieser Aufrufe. Nur über sie sind dauerhafte Änderungen am System möglich. Ohne sie könnte ein Prozess keine sinnvollen Aufgaben erfüllen, ein Angreifer könnte aber auch keinen Schaden anrichten.

Tabelle 1: Gängige Systemcalls

Tabelle 1: Gängige Systemcalls

Kein Angriff ohne Syscall

Ein typischer Angriff gelingt zum Beispiel durch einen Buffer Overflow in einem Webserver, durch den der Eindringling Shell-Zugang erhält. Der Cracker wird Exploit-Code einschleusen, der dann mit den Rechten des Webserver-Prozesses abläuft. Dieser Code muss einige Systemcalls ausführen, beispielsweise »fork()« und »execve()«, um die Shell zu starten. Erst durch Syscalls haben Lücken schädliche Folgen. Da sich Sicherheitslücken nicht vermeiden lassen, bietet sich das Überwachen von Systemaufrufen an, um einen Schutz zu implementieren.

Im Normalfall kann eine Applikation alle Systemcalls aufrufen. Nichts hindert den Webserver daran, einen Shell-Prozess auszuführen und jedem User bereitzustellen, der eine Verbindung zum Webserver aufbaut. Diese Aktion ist aber nicht vorgesehen und der Server ist auch nicht dafür programmiert. Durch Software-Bugs können Angreifer die Applikation jedoch dazu verleiten, Aktionen auszuführen, die von den Autoren nicht vorgesehen wurden.

Jede Applikation benötigt für ihre Aufgaben nur einen Teil der Syscall-Schnittstelle. Ein einfacher Webserver hört auf TCP-Port 80, beantwortet HTTP-Requests und stellt Dateien aus einer Standard-Verzeichnisstruktur bereit. Weitere Dienste muss er nicht anbieten, insbesondere muss er keine interaktive Shell starten oder »/etc/passwd« lesen.

Der erwünschte Funktionsumfang eines Programms lässt sich anhand seiner Systemaufrufe beschreiben. Systrace nutzt dies: Es beobachtet die Syscalls und leitet daraus eine passende Richtlinie ab. Unter der Kontrolle von Systrace kann die Applikation dann nur noch innerhalb der Policy arbeiten.

Richtlinien

Eine Systrace-Policy besteht aus einer Reihe von Regeln. Jede Regel gilt für einen Syscall sowie seine Parameter und legt fest, ob dieser Aufruf erlaubt ist oder nicht. Die folgenden einfachen Richtlinien erlauben die System-Calls »fchdir()« und »fstat()«:

linux-fchdir: permit
linux-fstat: permit

Die Anweisung »deny« an Stelle von »permit« würde diese Systemaufrufe unterbinden. Die Regeln können sich auch nur auf bestimmte Parameter des Syscalls beziehen:

linux-fsread: filename eq "/tmp/foo" then permit
linux-fsread: filename match "/etc/*" then deny[enoent]

Das Programm darf nach diesen Regeln die Datei »/tmp/foo« lesen. Files, deren Name auf das Muster »/etc/*« passt, führen dagegen zu einem »ENOENT«-Fehler. Statt den Syscall auszuführen, signalisiert Systrace damit der Anwendung, dass die Datei nicht existiert.

Policy-Sprache

Die Policy-Sprache ist für alle Systemaufrufe gleich. Jede Regel beginnt mit dem Namen einer Emulation und dem Syscall, zum Beispiel »linux-fsread«. Darauf folgen eine Liste von Bedingungen sowie die Aktion (»deny« oder »permit«), die Systrace in diesem Fall ausführen soll. Linux kennt keine Syscall-Emulation, daher beginnt jede Regel mit »linux«. Systrace unterstützt auch OpenBSD und NetBSD, die verschiedene Syscall-Varianten emulieren können.

Die Aktionen lassen sich optional um einen Fehlercode ergänzen (per Default »EPERM«, Operation not permitted). Der User kann auch einstellen, dass Systrace bestimmte Aktionen protokolliert, dazu muss er am Ende der Regel das Schlüsselwort »log« einsetzen. Die BNF-Spezifikation (Backus Naur Form) der Policy-Syntax ist in Listing 1 zu sehen.

Um die Gültigkeit von Regeln einzuschränken, gibt es eine begrenzte Auswahl an Prädikaten. Sie knüpfen die jeweilige Aktion an zusätzliche Bedingungen, derzeit können sie User oder Gruppen des Systems betreffen. Prädikate werden mit einem Komma an die Richtlinie angehängt, zum Beispiel:

linux-fsread: filename eq "/etc" then deny[eperm], if group != wheel

Diese Regel gilt nur, wenn der User nicht zur Gruppe »wheel« gehört.

Für die meisten Systemaufrufe sind Argumente definiert, beispielsweise erwartet »open« eine zu öffnende Datei. Systrace übersetzt diese Parameter in ein für Menschen lesbares Format. Es stellt sie als Strings dar und vergleicht diese mit den Regeln. Für den Vergleich kennt Systrace eine Reihe von Operatoren (siehe Tabelle 2).

Listing 1: Syntax
der Systrace-Policy

01 filter = expression "then" action errorcode logcode
02 expression = symbol | "not" expression | "(" expression ")" |
03              expression "and" expression | expression "or" expression
04 symbol = string typeoff "match" cmdstring |
05          string typeoff "eq" cmdstring | string typeoff "neq" cmdstring |
06          string typeoff "sub" cmdstring | string typeoff "nsub" cmdstring |
07          string typeoff "inpath" cmdstring | "true"
08 typeoff = /* empty */ | "[" number "]"
09 action = "permit" | "deny"
10 errorcode = /* empty */ | "[" string "]"
11 logcode = /* empty */ | "log"
Tabelle 2: String-Matching mit Systrace

Tabelle 2: String-Matching mit Systrace

Implementierung: An welcher Stelle eingreifen?

Beim Implementieren der Systrace-Funktion war zunächst eine geeignete Stelle für die Kontrolle zu finden. Der Ablauf eines Syscalls zeigt, dass dafür zunächst mehrere Komponenten in Frage kommen. Applikationen rufen Systemfunktionen auf, indem sie die entsprechenden Register füllen und einen Soft-Interrupt auslösen (»int«-Befehl bei i386-Prozessoren). Das Einrichten und Auslösen der Syscalls übernimmt typischerweise die Standard-C-Bibliothek (Libc). Ein Großteil ihrer Funktionalität beruht auf Systemaufrufen, zum Beispiel »open()«, »read()« und »write()«. Der Ablauf ist in Abbildung 1 dargestellt.

In jeder dieser Schichten können Systemcalls abgefangen und geändert werden. In der Library-Schicht (Libc) wäre das trivial: Mit Hilfe der »LD_PRELOAD«-Umgebungsvariablen kann eine Bibliothek über die Libc geladen werden. Die mit Preload geladene Bibliothek würde die gesamte Syscall-Funktionalität der Libc ersetzen.

Ein Widersacher kann diese Lösung jedoch leicht umgehen: Statt der entsprechenden Libc-Funktion muss seine Applikation den Systemaufruf nur selbst auslösen. Bei statisch gelinkten Programmen würde diese Methode ebenfalls nicht funktionieren. Außerdem gibt es Systrace-Funktionen, die im Userspace nicht ausführbar sind.

Abbildung 1: Um einen Syscall auszulösen, benutzen Userspace-Prozesse die Libc. »/bin/cp« ruft die Bibliotheksfunktion »write()« auf, die über das Register »eax« den passenden Syscall auswählt.

Abbildung 1: Um einen Syscall auszulösen, benutzen Userspace-Prozesse die Libc. »/bin/cp« ruft die Bibliotheksfunktion »write()« auf, die über das Register »eax« den passenden Syscall auswählt.

Syscall-Gateway als Kontrollposten

Der geeignete Ort um Systemaufrufe abzufangen ist daher die Kernelebene. Nur hier ist sicher jeder Systemcall betroffen, unabhängig davon, wo und wie er ausgelöst wurde. Alle Systemaufrufe treten über das Syscall-Gateway in den Kernel ein: Es handelt sich um den Interrupt-Handler für den Soft-Interrupt, den die Syscalls nutzen.

Das Gateway liest die Syscall-Nummer aus einem Register (»eax« bei i386-Prozessoren). Diese Nummer ist ein Index für die Syscall-Tabelle, die Funktionszeiger auf die jeweiligen Kernelfunktionen enthält. Das Gateway überprüft den Wertebereich der Syscall-Nummer und verzweigt dann zur jeweiligen Funktion, sie erledigt dann die Aufgabe des Systemaufrufs. Um einen Syscall ablehnen zu können, muss Systrace ihn abfangen, bevor er ausgeführt wird. Es klinkt sich dazu in das Call-Gateway ein.

Ein Großteil der Systrace-Funktionalität ist als Userspace-Programm implementiert. Die Verbindung zum Kernel wird von einem Device bereitgestellt: »/dev/systrace«. Der Userspace-Abschnitt von Systrace liest Nachrichten vom Kernel über dieses Device und führt »ioctl«-Aufrufe für das Gerät durch, um Nachrichten zurückzusenden.

Systrace greift ein

Um Systrace zu initialisieren, muss eine Applikation unter der Kontrolle des Userspace-Utility »systrace« laufen. Dieses Kommando baut eine Session mit dem Kernelabschnitt von Systrace auf, indem es »/dev/systrace« öffnet. Es startet einen neuen Prozess (»fork«), markiert ihn mit Hilfe eines »ioctl«-Befehls und führt mit »execve()« die zu überwachende Applikation aus.

Bei jedem Syscall prüft das modifizierte Call-Gateway, ob der Prozess markiert ist. Wenn ja, kontrolliert Systrace den weiteren Ablauf. Es sucht die Syscall-Nummer im Policy-Cache um festzustellen, ob für diesen Aufruf eine einfache Regel existiert (»permit« respektive »deny«, ohne Überprüfung der Argumente).

Wird Systrace fündig, führt es die von der Richtlinie vorgeschriebenen Aktionen durch. Ist keine Aktion im Cache zu finden, muss Systrace seine Userspace-Hälfte um eine Entscheidung bitten.

Die Kernelkomponente stellt dazu eine Nachricht in eine Warteschlange, die über »/dev/systrace« an das Userspace-Systrace geleitet wird. Die Nachricht enthält die Nummer sowie alle Parameter des Systemaufrufs. Die Userspace-Komponente sucht in der Policy der laufenden Applikation nach einem Treffer für diese Kombination aus Syscall und Parameter. Wird sie fündig, informiert sie den Kernel über die gewünschte Aktion. Findet sie keine passende Regel, bittet Systrace im interaktiven Modus den User um eine Entscheidung. Im Enforcement-Modus verbietet es alle Aktionen, die nicht in der Policy aufgeführt sind, und protokolliert dies.

Der Anwender leistet Entscheidungshilfe

Systrace bittet den User entweder über die Konsole oder in einem GUI-Dialog um seine Entscheidung. In beiden Fällen zeigt es den Syscall sowie alle Parameter. Der User kann bestimmen, ob Systrace diesen Aufruf zulassen oder ablehnen soll oder ob es einen neuen Eintrag in das Regelwerk einfügen soll. Ist die Entscheidung gefallen, muss Systrace die Aktion ausführen. Im Fall von »deny« gibt es den Fehlercode zurück, der in der »deny«-Anforderung definiert ist (per Default »EPERM«). Nur bei »permit« lässt Systrace zu, dass der Systemcall ausgelöst wird. Als Sicherheitsmaßnahme beendet der Kernel alle von Systrace überwachten Prozesse, wenn der Überwachungsprozess (»systrace«) unerwartet endet.

In einigen Fällen will die Userspace-Komponente von Systrace den Rückgabewert des Systemaufrufs erfahren, die Kernelkomponente meldet ihn dann im Anschluss an den Aufruf. Diese Möglichkeit kommt insbesondere bei »execve()« zum Einsatz: War der Systemaufruf erfolgreich, dann verwendet Systrace für diesen Prozess ab sofort jene Policy, die für das neue Programm zuständig ist.

Erst kommt das Training, dann der Einsatz

Um Systrace auf einen Prozess anzuwenden, muss dieser unter der Kontrolle des »systrace«-Utility laufen. Für Netscape lautet der Aufruf:

% systrace netscape

Das Tool startet dann einen Netscape-Prozess, den es für die Überwachung markiert. Wenn für diese Anwendung bereits eine Policy besteht, kommt sie zum Einsatz, existiert keine, legt Systrace ein neues Regelwerk an. Systrace meldet dem User jeden Systemaufruf, für den es keinen passenden Eintrag in der Policy gibt. Über die Option »-A« bietet Systrace auch einen Trainingsmodus an: In dieser Betriebsart gilt das jeweilige Verhalten der Applikation als normal. Systrace beobachtet, welche Syscalls die Applikation aufruft, und leitet daraus eine entsprechende Policy ab. Am Beispiel von XMMS sieht das folgendermaßen aus:

% systrace -A xmms

Im Trainingsmodus sollte der Anwender alle gewünschten Funktionen des Programms nutzen, bei XMMS vor allem Lieder abspielen.

XMMS bändigen

Nach dem Beenden der XMMS-Applikation schreibt Systrace die neuen Regeln in »$HOME/.systrace/usr_bin_xmms«. Ausschnitte aus diesem Regelwerk sind in Listing 2 aufgeführt. Die Policy enthält etwa 100 Einträge, die größtenteils Dateisystemzugriffe auf Bibliotheken und Plugins betreffen, außerdem wird das Sound-Device geöffnet und genutzt. Es ist ratsam, die automatisch generierte Policy auf ungewöhnliche Inhalte zu durchsuchen – es könnte ja sein, dass ausgerechnet während der Trainingsphase ein Angriff stattgefunden hat. Systrace würde die Aktionen des Widersachers dann als normal einstufen und in Zukunft erlauben.

Mit Hilfe der Policy kann Systrace die Applikation überwachen: »systrace xmms«. Damit sollte XMMS problemlos laufen – außer der Anwender versucht etwas, das in der Richtlinie nicht vorgesehen ist. Zum Beispiel könnte er im Dateiauswahl-Dialog auf das Root-Verzeichnis »/« zugreifen. Das wird mit einer Systrace-Meldung quittiert, wie sie in Abbildung 2 zu sehen ist. Folgender Policy-Eintrag verbietet XMMS diesen Zugriff dauerhaft:

filename eq "/" then deny[eacces]

Der Eintrag legt auch fest, dass der Syscall mit der Fehlermeldung »EACCES« scheitert. Das zeigt XMMS, dass ihm die Rechte für diesen Zugriff fehlen. XMMS meldet dann, dass es das Verzeichnis nicht lesen kann (siehe Abbildung 3). Sollte XMMS einen Bug enthalten, durch den ein Angreifer auf die privaten Dateien des Benutzers zugreift, würde Systrace das abnorme Verhalten feststellen und den Anwender warnen. XMMS muss auf diese Files normalerweise nicht zugreifen, daher sind sie auch nicht in der Policy erfasst.

Sobald die Richtlinien definiert sind, kann man Systrace im Enforcement-Modus ausführen. Es fragt den Benutzer dann nicht mehr, wenn es abnormes Verhalten feststellt, sondern lehnt den Syscall ab und schreibt einen Eintrag in Syslog.

Listing 2:
Ausschnitte aus der XMMS-Richtlinie

01 linux-fsread: filename eq "/etc/ld.so.preload" then permit
02 linux-fsread: filename eq "/etc/ld.so.cache" then permit
03 linux-fsread: filename eq "/lib/libpthread.so.0" then permit
04 linux-fsread: filename eq "/usr/X11R6/lib/libSM.so.6" then permit
05 linux-fsread: filename eq "/usr/X11R6/lib/libICE.so.6" then permit
06 linux-fsread: filename eq "/usr/lib/libxmms.so.1" then permit
07 [...]
08 linux-fswrite: filename eq "/dev/dsp" then permit
09 linux-fsread: filename eq "/home/marius/.xmms/menurc" then permit
10 linux-fsread: filename eq "/dev/mixer" then permit
11 linux-fsread: filename eq "/home/marius/.xmms/xmms.m3u" then permit
12 linux-fsread: filename eq "/home/marius" then permit
13 [...]
14 linux-pipe: permit
15 linux-clone: permit
16 linux-rt_sigsuspend: permit
17 linux-poll: permit
18 linux-getppid: permit
19 linux-kill: pidname eq "/usr/bin/xmms" and signame eq "<unknown>: 32" then permit
Abbildung 2: Im interaktiven Modus meldet Systrace dem Benutzer, wenn ein Programm außerhalb der Policy arbeitet. Im abgebildeten Beispiel hat XMMS versucht das Root-Verzeichnis »/« zu lesen.

Abbildung 2: Im interaktiven Modus meldet Systrace dem Benutzer, wenn ein Programm außerhalb der Policy arbeitet. Im abgebildeten Beispiel hat XMMS versucht das Root-Verzeichnis »/« zu lesen.

Abbildung 3: Eine Systrace-Policy sperrt den Zugriff auf »/« mit der Fehlermeldung »EACCESS«. Der File-Dialog von XMMS quittiert dies mit dem Hinweis »Directory unreadable: Permission denied«.

Abbildung 3: Eine Systrace-Policy sperrt den Zugriff auf »/« mit der Fehlermeldung »EACCESS«. Der File-Dialog von XMMS quittiert dies mit dem Hinweis »Directory unreadable: Permission denied«.

Fazit

Systrace zwängt Applikationen in ein Policy-Korsett und begrenzt damit den Schaden, den Sicherheitslücken anrichten können (siehe Abbildung 4). Das Regelwerk beschreibt im Wesentlichen die vorgesehenen Systemaufrufe. Wenn Systrace aktiv ist, informiert es den User über Syscall-Aktivitäten, die von der Richtlinie nicht erfasst sind. Er kann dann entscheiden, ob Systrace diese Aufrufe erlaubt oder nicht. (fjl)

Abbildung 4: Systrace erwischt ein Trojanisches Pferd im Configure-Skript von Fragroute: Das Source-Paket wurde von Crackern modifiziert. Es versucht eine TCP-Verbindung zu Port 6667 auf IP-Adresse 216.80.99.202 zu öffnen.

Abbildung 4: Systrace erwischt ein Trojanisches Pferd im Configure-Skript von Fragroute: Das Source-Paket wurde von Crackern modifiziert. Es versucht eine TCP-Verbindung zu Port 6667 auf IP-Adresse 216.80.99.202 zu öffnen.

Systrace bei
Monkey.org

Systrace kommt beispielsweise bei Monkey.org zum Einsatz. Dieser private Unix-Shell-Provider führt alle Prozesse seiner etwa 200 User unter Systrace aus. Die Admins haben dazu für jedes installierte Programm ein Regelwerk definiert.

Als Login-Shell ist für jeden User »stsh« (Systrace-Shell) gesetzt. Sie ruft die echte Shell unter der Kontrolle von Systrace auf, dadurch wird auch jeder User-Prozess überwacht. Systrace läuft hier im Enforcement-Modus, es lehnt also jeden Syscall ab, der nicht in der Policy vorgesehen ist, und protokolliert diese Vorfälle. Die Administratoren können die Protokolle auswerten und bei Bedarf ihre Richtlinien ergänzen.

Infos

[1] Systrace-Homepage: [http://www.citi.umich.edu/u/provos/systrace/]

Die
Autoren

Marius Aamodt Eriksen ist Open-Source-Developer und studiert Informatik an der University of Michigan in Ann Arbor, Michigan, USA. Er hat Systrace auf Linux portiert.

Niels Provos entwickelte viele Open-Source-Programme, unter anderen auch Systrace. Er promoviert derzeit an der University of Michigan in Ann Arbor, Michigan, USA. Seine Forschungsschwerpunkte sind Computer- und Netzwerksicherheit, außerdem befasst er sich mit Steganographie.

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