Linux 5.13 mit Angriffsschutz und Sandbox

Linux 5.13 erhöht mit Control Flow Integrity und Sandboxen die Sicherheit. Programme in der Laufzeitumgebung eBPF dürfen auf Kernel-Funktionen zugreifen.

In Pocket speichern vorlesen Druckansicht 14 Kommentare lesen

(Bild: J0hnTV/Shutterstock.com)

Lesezeit: 15 Min.
Von
  • Oliver Müller
Inhaltsverzeichnis

Nach sieben Release-Candidates entschied sich Linus Torvalds den Linux-Kernel 5.13 freizugeben. Die Woche nach rc7 sei recht ruhig gewesen, was ihm dazu bewog, Linux 5.13 nicht mehr zurückzuhalten. Mit immerhin über 17.000 Commits inklusive Merges ist es eines der größten Releases der 5.x-Reihe. Über 2000 Entwickler trugen zu dem Release bei. Dennoch beschwichtigt Torvalds in seiner Release-Note, dass trotz dieser Größe nichts wirklich "herausragend ungewöhnliches" dabei sei. Er vermutet den Größenzuwachs in der Extrawoche des rc8 von Linux 5.12, welches das Merge-Window von 5.13 hinauszögerte. Zwischen den Zeilen bedeutet das, es haben sich durch diese Extrawoche zusätzliche Patches und Merges angestaut. Diese flossen nun noch zusätzlich in Linux 5.13 ein.

Das Ergebnis kann sich sehen lassen. Aus dem Android-Kernel hält die Control Flow Integrity (CFI) zum Absichern gegen Angriffe Einzug. Landlock gestattet Sandboxen für Anwendungen und Systemdienste anzulegen. Mit der Möglichkeit, Kernel-Funktionen aus eBPF-Programmen heraus zu nutzen, kommt jedoch auch ein womöglich riskantes Feature in den Linux-Kernel. Der einst als Firewall-Technik gestartete Extended Berkeley Packet Filter (eBPF) stellt heute einen Interpreter für Programme dar, analog einer kleinen virtuellen Maschine.

Linux 5.12 führte bereits Link Time Optimization (LTO) für den Compiler Clang ein. Damit waren die Voraussetzung für die Control Flow Integrity (CFI) gelegt. In Linux 5.13 konnte somit das ursprünglich aus dem Android-Kernel stammende CFI in den Mainline-Stream aufgenommen werden.

CFI soll den Kernel vor dem Umlenken des Programmflusses bei indirekten Funktionsaufrufen und manipulierten Rücksprungadressen schützen. Was sperrig klingt, ist ein neues Puzzleteil für den Schutz vor einem nicht so komplizierten Angriffsvektor.

Wollen Angreifer ein System unter ihre Kontrolle bringen, müssen sie dem System in irgendeiner Form Schadcode unterschieben und diesen ausführen lassen. In der Vergangenheit konzentrierten sich Schutzmechanismen darauf, Speicherbereiche aufgabenbasiert zu trennen und Rechte einzuschränken. Das No-Execute-Feature (NX) machte Datenspeichersegmente im Kernel nicht ausführbar. Später verhinderten Intels Supervisor Mode Execution Prevention (SMEP) und ARMs Privileged Execute Never (PXN) zusätzlich, dass der Kernel (versehentlich) Code im Userspace ausführt. Letzteres nur, wenn er sich im hoch privilegierten Kernel-Mode befindet. Es kann so kein Code aus dem Userspace untergeschoben werden.

Trotz aller Bemühungen blieben wunde Punkte: indirekte Funktionsaufrufe und manipulierte Rücksprungadressen. Bei ersterem erfolgt der Funktionsaufruf nicht als feste Adresse, sondern indirekt über einen Zeiger auf die Adresse. Die Adresse der Funktion liegt damit nicht fest codiert in gegen Manipulationen geschützten Kernel-Code-Segmenten, sondern liegt als Variable auf dem Stack oder Heap. Stack und Heap sind Datensegmente, die ihrer Natur entsprechend veränderbar sind.

Ebenso legt das System bei jedem Funktionsaufruf auf dem Stack die Rücksprungadresse ab. Dies unabhängig davon, ob der Aufruf über eine statische Adresse oder indirekt über einen Funktionszeiger erfolgt. Der Programmfluss soll nach dem Funktionsaufruf schließlich an die ursprüngliche Stelle zurückkehren. Auch diese Rücksprungadresse lässt sich in bestimmten Angriffsszenarien modifizieren.

Um Manipulationen zu erkennen, prüft CFI die Funktionsadresse vor dem Aufruf und die Rücksprungadresse vor dem Rücksprung. CFI teilt Funktionen anhand ihres Prototyps (Argumente und Rückgabetyp) in Klassen ein. Indirekte Funktionsaufrufe gestattet CFI nur dann, wenn die aufrufende und die aufgerufene Funktion derselben Klasse angehören. Sonst quittiert das System den Klassenunterschied mit einer Kernel-Panic.

Linux und Linux-Softwaretipps

Zum Schützen der Rücksprungadresse setzt CFI auf einen gesicherten Stack (trusted stack). Dieser ist in Form einer Schattenkopie des Aufrufstapels angelegt (shadow call stack). Der ideale Ansatz wäre ein in Hardware gegossenes System wie Intels Control-Flow Enforcement Technoloy (CET) oder ARMs Pointer Authentication. Beides ist aber noch nicht in der Breite verfügbar, weshalb ein Nachbau in Software erfolgt. Auf ARM kommt ein reserviertes Register zum Einsatz, das auf den Shadow-Call-Stack zeigt.

Beim Funktionsaufruf wird die Rücksprungadresse auf dem normalen Stack gesichert. Zusätzlich legt CFI die Rücksprungadresse auf den Schattenstapel. Beim Funktionsende, aber vor dem Rücksprung, vergleicht CFI die gesichtete Adresse auf dem normalen Stapel mit der Schattenkopie. Sind die identisch darf die Funktion an die Adresse zurückspringen. Bei einer Abweichung erfolgt der Abbruch in Form einer Kernel-Panic.

Näheres zur Implementierung von CFI erklärt sein Urheber Kees "Case" Cook in einer Präsentation. CFI ist aktuell in Linux 5.13 auf ARM64 beschränkt. An der Unterstützung für x86-Architekturen (32- und 64-Bit) wird noch gefeilt.

Nach über fünf Jahren Entwicklungszeit hält Landlock Einzug in den Linux Kernel. Das von Mickaël Salaün geschaffene Linux Security Module (LSM) ermöglicht Sandboxen im Userspace. Zu dem Feature inspirierten macOS XNU Sandbox, FreeBSD Capsicum und OpenBSD Pledge/Unveil.

Ursprünglich setzte Landlock auf eBPF und die Kernel-Funktion seccomp(). Durch seccomp() kann ein Prozess sich selbst unumkehrbar in Rechten beschneiden und anschließend nur noch beschränkt System-Calls ausführen. Anfangs nutzte Landlock eBPF-Programme, die sich in Security-Hooks des Kernel einklinkten. Sie regelten den Zugriff auf System-Calls aus der Sandbox heraus. Die eBPF-Regeln assoziierten einzelne Programme mit Teilen des Dateisystems. seccomp() schränkte in einem speziellen Modus den Zugriff auf das Dateisystem ein.

Der System-Call seccomp() engte Landlock jedoch durch die starr vorgegebene Rechtebeschränkung zu sehr ein, da sie nicht gestattete, nur gezielt einzelne System-Calls zu blockieren. Auch der Einsatz von eBPF für die Zugriffskontrolle erwies sich als zu unhandlich. Schließlich setzt Landlock nun auf eigene System-Calls. Der seccomp()-Ansatz wurde fallen gelassen. Zur Zugriffskontrolle ist ebenfalls ein eigener Mechanismus im Einsatz, der das ursprünglich benutzte eBPF in Landlock ersetzt. Statt die System-Calls zu filtern, schränkt es den Zugriff direkt auf Kernel-Objekte, wie die Dateihierachie, ein.

Etwas verwirrend liest sich die Commit-Meldung zu Landlock. Obwohl Landlock im Inneren nicht mehr auf seccomp() und eBPF setzt, sieht Landlock-Entwickler Salaün gerade in der Kombination mit seccomp() und eBPF zusätzliche Stärken. Die Funktion seccomp() kann Sandbox-Prozesse zusätzlich beschneiden, wenn das im Anwendungsfall sinnvoll und machbar ist. Ebenso lassen sich Operationen durch eBPF flexibel flankieren. Beides muss hierzu aber nicht interner Bestandteil von Landlock sein.

Beispiele zur Anwendung von Landlock wären laut Salaün unter anderem Applikationen mit integriertem Sandboxing, wie Webbrowser, Webservices oder SSH-Server. Systemdienste ließen sich mit Landlock auf ihre Aufgaben beschränken. Im Falle eines Exploits bleibt der Schaden auf den jeweiligen Systemdienst beziehungsweise dessen Sandbox eingedämmt. Sandbox-Tools wie Minijail, Firejail und nsjail sowie auch Flatpak könnten von der Sandbox-API profitieren.