• Keine Ergebnisse gefunden

Kernelland-Angriffen

1.3 Warum funktioniert mein Userland-Exploit nicht mehr?

Diejenigen, die Systeme vor Userland-Exploits zu schützen versuchen, haben sich die im vorherigen Abschnitt aufgeführten Gegenmaßnahmen (und noch viele weitere!) ebenfalls überlegt und festgestellt, das der Kernel der Platz ist, an dem sich diese Ge-genmaßnahmen am wirkungsvollsten umsetzen lassen. Um sich eine Vorstellung davon zu machen, wie sehr die Hürden für die Entwickler von Userland-Exploits angehoben wurden, müssen Sie sich nur die Liste der Leistungsmerkmale von Projekten wie PaX/

grsecurity (www.grsecurity.net), ExecShield (http://people.redhat.com/mingo/exec-shield) oder Openwall (www.openwall.com) für den Linux-Kernel oder die Sicherheitsmerkmale von beispielsweise OpenBSD (W^X, Adress Space Layout Randomization [ASLR]) oder Windows (Datenausführungsverhinderung, ASLR) ansehen.

31 1.3 Warum funktioniert mein Userland-Exploit nicht mehr?

Verteidigen Sie sich!

Verteidigung findet auf mehreren Ebenen statt

Alle Schutzmaßnahmen an einer einzigen Stelle zu konzentrieren, war noch nie eine gute Idee, und dieses Prinzip gilt auch die Verteidigung gegen Exploits. Pat-ches auf Kernelebene gehören zwar zu den wirkungsvollsten Maßnahmen, doch Sicherheitsvorkehrungen lassen sich auch auf anderen Ebenen treffen. Compi-ler sind ebenfalls sehr geeignete Orte für die Anbringung von Patches, denn wie können Sie Ihren Code besser schützen als dadurch, die Verteidigung gleich in ihn selbst aufzunehmen? Beispielsweise enthalten neuere Versionen der GNU Compiler Collection (GCC, http://gcc.gnu.org) bereits Fortify Source2 und Optio-nen für Stack Smashing Protector, auch bekannt als ProPolice (www.trl.ibm.com/

projects/security/sspl/). Allzweck-Bibliotheken können Patches ebenfalls gut ge-brauchen, denn sie gehören zu dynamisch verknüpften Bibliotheken und können sensible Teilsysteme wie Speicherallokatoren enthalten. ExecShield von Red Hat/

Fedora ist ein Beispiel für ein Projekt, dass all diese Arten von Patches einschließt.

Sie können ein System nicht nur dadurch schützen, dass Sie angreifbaren Code gegen Ausnutzung absichern, sondern auch dadurch, dass Sie die Auswirkungen einer Ausnut-zung verringern. In der Einführung zu diesem Kapitel haben wir bereits das klassische Benutzermodell erwähnt, das die meisten in diesem Buch behandelten Betriebssysteme verwenden. Die Stärke dieses Modells, nämlich seine Einfachheit, ist auch seine Schwäche:

Es deckt sich nicht mit dem Nutzungsmodell der Anwendungen, die auf einem System laufen. Um zu verdeutlichen, was das bedeutet, sehen wir uns ein einfaches Beispiel an.

Zu den üblichen privilegierten Operationen gehören das Öffnen eines niedrigen TCP- oder UDP-Ports (1 bis 1023 einschließlich) und das Löschen eines Benutzers vom Sys-tem. Bei dem zuvor beschriebenen naiven Benutzermodell müssen beide Operationen mit Superuser-Rechten ausgeführt werden. Allerdings ist es ziemlich unwahrscheinlich, dass eine Anwendung beide Aktionen ausführen muss. Es gibt keinen Grund dafür, dass ein Webserver Logik für die Verwaltung von Benutzerkonten enthält. Eine Schwachstelle in der Webserveranwendung würde einem Angreifer aber die volle Kontrolle über das Sys-tem geben. Das Prinzip der Rechtetrennung besteht darin, die Menge des mit sämtlichen Rechten ausgeführten Codes so weit wie möglich zu reduzieren. Bei unserem Webserver sind Superuser-Rechte nur notwendig, um das Socket zu öffnen, das an dem traditionel-len HTTP-Port (80) lauscht. Nachdem er diese Operation ausgeführt hat, ist es für ihn nicht mehr erforderlich, den Superuser-Status aufrechtzuerhalten. Um die Auswirkungen der Ausnutzung einer Schwachstelle zu verringern, müssen Anwendungen wie HTTP den Superuser-Status verwerfen, sobald sie ihre privilegierten Operationen ausgeführt haben.

Andere Daemons, beispielsweise sshd, zerlegen die Anwendung auf der Grundlage der ver-schiedenen Arten von erforderlichen Operationen in mehrere Teile. Vollständige Rechte

2 Beispielsweise kennt der Compiler zur Kompilierungszeit die Größe bestimmter Puffer und kann diese Information nutzen, um den Aufruf einer unsicheren Funktion wie strcpy zu einer sicheren Funktion wie strncpy umzuleiten.

werden nur den Teilen zugewiesen, die sie auch benötigen, und diese Teile werden so klein gefasst wie möglich. Die einzelnen Teile kommunizieren während der Lebensdauer der Anwendung daher über eine Form von IPC-Kanal (Interprocess Communication).

Können wir es noch besser machen? Nun, wir können das Prinzip der geringstmöglichen Rechte auf das gesamte System ausweiten. Mandatory Access Control (MAC), Zugriffs-steuerungslisten (Access Control Lists, ACL) und rollengestützte Zugriffssteuerung (Role-Based Access Control, RBAC) wenden dieses System auf die eine oder andere Weise auf das Gesamtsystem an und verwerfen damit das Superuser-Prinzip. Jedem Benutzer wird die geringstmögliche Menge an Rechten zugeteilt, die er zur Erledigung seiner Aufgaben benötigt. Beispiele für solche Systeme sind Solaris Trusted Extensions, Linux grsecuri-ty und Patches für NSA SELinus (www.nsa.gov/research/selinux/index.shtml, enthalten im Linux-Mainstreamkernel seit Version 2.6) sowie Windows Vista Mandatory Integrity Control.

Einen erfolgreichen und zuverlässigen Userland-Exploit zu schreiben, der diese Schutz-vorkehrungen umgeht, stellt eine große Herausforderung dar, und dabei müssen wir außerdem noch voraussetzen, dass der Autor bereits eine Schwachstelle in seinem Ziel gefunden hat. Zum Glück (oder leider, je nachdem, wo Sie stehen) sind die Hürden auch hier heraufgesetzt worden. Während der letzten beiden Jahrzehnte wurden Angriffe mit-hilfe von Exploits immer weiter verbreitet. Als Folge davon wurde jegliche bedeutende Userland-Software mehrfach von verschiedenen Hackern und Sicherheitsforschern in aller Welt überprüft. Software entwickelt sich weiter, und es wäre naiv anzunehmen, dass sich bei dieser Evolution keine neuen Bugs einschleichen würden. Allerdings lassen sich Schwachstellen heute längst nicht mehr so leicht finden wie noch vor zehn Jahren.

Warnung

Wir konzentrieren uns hier auf die Verteidigung gegen Exploits mithilfe von Software, doch auch hardwareseitig ist ein gewisser Schutz möglich. Beispiels-weise gibt es in der 64-Architektur (der 64-Bit-Weiterentwicklung der x86-Architektur) ein NX-Bit3 für physische Seiten. Moderne Kernels können dieses Bit nutzen, um Bereiche im Adressraum als nicht ausführbar zu kennzeichnen, was die Anzahl der Stellen verringert, an denen Angreifer Shellcode unterbrin-gen können. Mehr darüber (und wie diese Schutzvorkehrung umganunterbrin-gen werden kann) erfahren Sie in Kapitel 3.

3 Das NX-Bit (nonexecutable) kann auch auf 32-Bit-x86-Computern aktiviert werden, die die physische Ad-resserweiterung (Physical Address Extension, PAE) unterstützen. Mehr darüber erfahren Sie in Kapitel 3.

33 1.3 Warum funktioniert mein Userland-Exploit nicht mehr?

1.3.1 Kernelland- und Userland-Exploits im Vergleich

Wir haben den Kernel bereits als die Einheit bezeichnet, in der viele Sicherheitsmaßnah-men gegen Exploits impleSicherheitsmaßnah-mentiert sind. Angesichts der zunehSicherheitsmaßnah-menden Verbreitung von Sicherheitspatches und des derzeitigen Rückgangs an Userland-Schwachstellen ist es nicht überraschend, dass Exploit-Autoren ihre Aufmerksamkeit zum Kern des Betriebssystems verlagert haben. Im Vergleich zu Userland-Exploits stellt das Schreiben von Kernelland-Exploits jedoch einige zusätzliche Herausfoderungen an den Autor:

• Der Kernel ist die einzige Software, die für das System unverzichtbar ist. Solange der Kernel ordnungsgemäß läuft, gibt es keine nicht behebbare Situation. Bei Userland-Exploits sind Brute-Force-Methoden durchaus anwendbar, denn die einzige Sorge, die sich ein Angreifer machen muss, wenn die Zielanwendung wiederholt abstürzt, besteht darin, dass dies deutliche Spuren in den Protokollen hinterlässt. Beim Kernel ist das jedoch nicht mehr so: Ein Fehler im Kernel versetzt das System in einen in-konsistenten Zustand, wobei gewöhnlich ein manueller Neustart erforderlich ist, um den Rechner wieder in einen ordnungsgemäß funktionierenden Zustand zurückzu-versetzen. Tritt der Fehler in einem der sensiblen Bereiche des Kernels auf, stürzt das Betriebssystem einfach ab, was als Panik bezeichnet wird. Einige Betriebssysteme, z. B.

Solaris, schreiben auch die Informationen über die Panik zur nachträglichen Analyse in eine Crashdump-Datei.

• Der Kernel ist sowohl durch Software als auch durch Hardware vom Userland ge-trennt. Informationen über den Kernel zu gewinnen, ist sehr viel schwieriger. Auch die Anzahl der Variablen, die nicht mehr der Kontrolle des Angreifers unterliegen, steigt exponentiell. Beispielsweise befindet sich der Speicherallokator bei einem Userland-Exploit innerhalb des Prozesses und ist gewöhnlich über eine gemeinsam genutzte Sys-tembibliothek verknüpft. Das Ziel ist der einzige Verbraucher und das Einzige, was sich darauf auswirkt. Dagegen können sich sämtliche Prozesse auf dem System auf das Verhalten und den Status eines Kernel-Speicherallokators auswirken.

• Der Kernel ist ein ausgedehntes und vielschichtiges System. Sein Umfang ist erheb-lich und liegt in der Größenordnung von Millionen Codezeilen. Er muss sich um die gesamte Hardware auf dem Computer und die meisten maschinennahen Soft-wareabstraktionen kümmern (virtueller Speicher, Dateisysteme, IPC-Einrichtungen usw.). Das führt zu einer Menge hierarchisch geordneter, miteinander verbundener Teilsysteme, die ein Angreifer erst einmal genau verstehen muss, bevor er in der Lage ist, eine bestimmte Schwachstelle auszulösen und erfolgreich auszunutzen. Da ein so kompliziertes System selten fehlerfrei ist, kann sich diese Eigenschaft jedoch auch als vorteilhaft für die Entwickler von Exploits erweisen.

Der Kernel bietet Angreifern jedoch auch einige Vorteile gegenüber dem Userland. Da er der Code mit den höchsten Rechten auf dem System ist (ohne Berücksichtigung von Virtualisierungslösungen; siehe den folgenden Hinweis), lässt er sich auch am schwie-rigsten schützen. Außer der Hardware gibt es keine andere Einheit mehr, auf die dieser Schutz gestützt werden kann.

Hinweis

Zur Zeit der Abfassung dieses Buchs werden Virtualisierungssysteme immer beliebter. Es wird nicht mehr lange dauern, bis virtualisierte Kernelschutzvor-kehrungen auftreten. Allerdings müssen bei dieser Art von Schutz auch Leis-tungseinbußen berücksichtigt werden. Um eine große Verbreitung zu genießen, dürfen die Virtualisierungslösungen den von ihnen geschützten Kernel nicht zu stark beeinträchtigen.

Viele der beschriebenen Schutzvorkehrungen führen allerdings auch zu Leistungseinbu-ßen. Bei einigen Userland-Anwendungen mögen sie zwar vernachlässigbar sein, doch wenn die Maßnahmen auf den Kernel (und damit auf das gesamte System) angewendet werden, haben sie viel stärkere Auswirkungen. Die Leistung ist für Endbenutzer ein entscheidendes Kriterium, und es ist gar nicht einmal so unüblich, auf Sicherheit zu verzichten, wenn sie eine Verschlechterung der Leistung nach sich zieht.

Tabelle 1.1 gibt einen Überblick über die wichtigsten Unterschiede zwischen Userland- und Kernelland-Exploits.

Tabelle 1.1: Unterschiede zwischen Userland- und Kernelland-Exploits

Zweck Userland-Exploit Kernelland-Exploit

Brute-Force-Angriff auf eine Schwachstelle

Führt zu mehreren Abstürzen der Anwendung, die dann neu gestartet werden kann (oder automatisch neu gestartet wird, z. B. über inetd in Linux).

Führt zu einem inkonsistenten Zustand des Computers und im Allgemeinen zu einer Panik oder einem Neustart.

Einfluss auf das Ziel nehmen

Der Angreifer hat großen Einfluss (vor allem lokal) auf die Zielanwendung (beispiels-weise kann er die Umgebung sehen, in der sie läuft). Die den Kernel zu nehmen, mit al-len anderen Anwendungen. Alle Anwendungen sind Verbraucher der Kernel-Teilsysteme.

Shellcode ausführen Der Shellcode kann Kernel-Systemaufrufe über User-land-Zugänge aufrufen, die Sicherheit und Korrektheit garantieren.

Der Shellcode wird mit höheren Rechten ausgeführt und muss die Steuerung korrekt ans Userland zurückgeben, ohne eine Panik des Systems hervor-zurufen.

35