zurück zum Artikel

Linux 5.13 mit Angriffsschutz und Sandbox

Oliver Müller

(Bild: J0hnTV/Shutterstock.com)

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

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 [14]. 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 [15].

Nach über fünf Jahren Entwicklungszeit hält Landlock [16] 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 [17]. 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.

Der extended Berkeley Packet Filter (eBPF) erlaubt es, Programme aus dem Userspace zu laden und im Kernel-Context ablaufen zu lassen, ohne hierfür den Kernel-Quellcode zu ändern oder Kernel-Module zu laden. Damit das Sinn ergibt, müssen die eBPF-Programme beziehungsweise die eBPF-Virtual-Machine mit dem Kernel interagieren. Bislang wurde der Zugang zur Funktion im Kernel – aus gutem Grund – restriktiv gehalten. Der Kernel bringt nun eine Neuerung: eBPF-Programme können zuvor festgelegte Kernel-Funktionen direkt aufrufen. Dabei operiert der neue Mechanismus am Kernel-ABI vorbei.

Ausgangspunkt für diese Lockerung war die TCP-Congestion-Control in eBPF von Martin KaFai Lau. Die Congestion-Control soll Überlast auf dem Netzwerk verhindern. Hierzu kommen ausgefeilte Algorithmen zum Einsatz, die ursprünglich im Kernel-Code beheimatet sind. Lau führt diese teilweise in die eBPF mit dem Ziel kürzerer Durchlaufzeiten (turn-around) und schlankerer Kernel ein. Zudem sind neue TCP-Congestion-Control-Algorithmen aus dem Userspace einfacher in die eBPF zu schieben und zu testen als über modifizierten Kernel-Code.

Dem Unterfangen standen Beschränkungen der eBPF im Weg, die unterbanden, benötigte Kernel-Funktionen zu verwenden. Daher reimplementierte die eBPF-Variante der Congestion-Control viele Funktionen erneut. Es entstand somit eine "Parallelwelt" in und für eBPF zur bereits im Kernel vorhandenen Funktion. Um diese Redundanz aufzulösen, schuf Lau einen neuen Mechanismus, der es eBPF-Programmen gestattet, solche Kernel-Funktionen zu verwenden.

In eBPF werden Kernel-Funktionen über ein schlichtes extern-Statement eingebunden. Damit das nicht in einen "Freibrief" für alle eBPF-Programme ausartet, muss auf der Kernel-Seite die jeweilige Kernel-Funktion für die betreffenden eBPF-Programmtypen freigegeben werden. Das geschieht über die zum jeweiligen eBPF-Programmtyp gehörigen bpf_verifier_ops-Struktur. Über den Member check_kfunc_call dieser Struktur wird ein Zeiger auf eine Funktion hinterlegt, die als "Prüfstelle" und Berechtigungsfreigabe dient. Wenn ein eBPF-Programm eine Kernel-Funktion aufrufen will, startet der eBPF-Verifier die hinterlegte "Prüffunktion". In dieser Prüffunktion ist der Entwickler frei, eigene Regeln zur Berechtigungsprüfung zu hinterlegen. Die Funktion gibt über einen booleschen Wert zurück, ob das eBPF-Programm die jeweilige Kernel-Funktion nutzen darf.

Aktuell nutzen diesen Mechanismus nur Congestion-Control-Programme für die Kernel-Funktion tcp_slow_start(). Zukünftig dürften das jedoch mehr werden, denn ist ein Feature vorhanden, finden sich dafür auch Anwendungen. Öffnet sich hier die Büchse der Pandora?

Der Mechanismus ist aus zwei Gründen mit Vorsicht anzuwenden. Zunächst ist die extern-Deklaration im eBPF-Programm problematisch. Ein solides Prototyping ist ausgehebelt. Ändert sich die Signatur einer Kernel-Funktion und wird das eBPF-Programm nicht nachgezogen, wird die Kernel-Funktion mit falschen Parametern aufgerufen. Die Folgen sind logische Fehler bis hin zur Kernel Panic. Ob das der jeweilige C-Kompiler im Vorfeld bemängelt, bleibt vom Einzelfall abhängig. Sicherer wäre hier ein klarer Weg über die Kernel-ABI.

Der zweite Grund sind Sicherheitsbedenken. eBPF-Programme prüft das eBPF-System zwar. Das neue und wesentlich offenere Freigabesystem für Kernel-Funktionen stellt das eBPF-System vor neue Herausforderungen. Bislang war starr geregelt, was ein eBPF nutzen darf. Jetzt kann theoretisch das Kernel-Repertoire nahezu beliebig geöffnet werden. Die Frage ist, ob das eBPF-System seiner Prüfrolle für alle Kernel-Funktionen gerecht werden kann.

Mit Apple M1 erhält Linux 5.13 einen prominenten Neuzugang. Der Linux-Kernel lässt sich für den neuen ARM-basierten, aber Apple-spezifischen Prozessor kompilieren. Linux 5.13 läuft somit grundsätzlich auf den 2020er ARM-basierten Ausgaben von Mac Mini, MacBook Pro und MacBook Air.

Damit ist Apples eigene ARM-Hardware zwar im Mainline-Kernel angekommen, für den produktiven Einsatz ist Linux auf dieser Plattform aber noch nicht geeignet. Deutlich ist das beispielsweise daran zu sehen, dass es die M1 GPU noch nicht in den Mainline-Kernel geschafft hat. Die M1-Grafik ist mit Linux 5.13 noch nicht Hardware-beschleunigt.

Bei den anderen Hardware-Plattformen bekommt 32-Bit-PowerPC das Debugging-System KFENCE und eBPF spendiert. Zudem unterstützen PowerPC-Systeme nun Time-Namespaces, mit denen sich Abweichungen zur Systemzeit für Namespaces und Container definieren lassen.

RISC-V bekommt kexec und insbesondere Crash-Dumps (Speicherabzüge) über kexec spendiert. kexec (kernel execute) dient zum Laden eines anderen Kernel aus einem laufenden heraus, was den Start eines neuen Kernels beschleunigt und in Hochverfügbarkeitsszenarien eine Rolle spielt.

Zusätzlich beherrscht Linux 5.13 auf RISC-V nun auch Execute-in-place (XIP). Das im Embedded-Umfeld begehrte Feature gestattet es, Programme aus ROM oder Flash-Memory direkt ablaufen zu lassen. Ohne XIP müssten die Programme zuerst aus ROM oder Flash ins RAM geladen und von dort aus gestartet werden. Zu guter Letzt rundet der Debugging-Mechanismus kprobes die Neuerungen bei RISC-V ab.

Der exFAT-Treiber kann jetzt mit dem ioctl()-Kommando FITRIM Laufwerke über nicht mehr benötigte Blöcke informieren. Zudem wurde das Auffinden von Dateien im Dateisystem (Look-up) beschleunigt. Mit gesetzter dirsync-Option lässt sich außerdem die Schreibgeschwindigkeit erhöhen.

XFS kann nun Speicherplatz von der letzten Allocation-Group entfernen. Da das Dateisystem aus einer Kette von Allocation-Groups besteht, lässt sich durch das Entfernen der letzten Gruppe die Kette verkürzen. Das ist ein erster Schritt das Dateisystem schrumpfen zu lassen. XFS kommt damit dem Ziel näher, online Dateisysteme zu schrumpfen. Neben dieser Grundlagenforschung erhält XFS mit dem neuen Kernel einige Performance-Verbesserungen.

Mit Linux 5.13 entfällt eine Einschränkung für das Dateisystem ext4: Dieses beherrscht jetzt auch die Dateisystemverschlüsselung, wenn Case-Folding aktiviert ist. Ist es eingeschaltet, unterscheidet ext4 – Unix-untypisch – nicht mehr zwischen Groß- und Kleinschreibung für das betreffende Dateisystem. Case-Folding und Verschlüsselung schlossen sich bislang aus; entweder das eine oder das andere. Des Weiteren überschreibt ext4 jetzt beim Löschen von Dateien, deren Verzeichniseinträge mit Nullbytes. Auf diese Weise begegnet das ext4-Team Datenschutzbedenken und verhindert das Offenlegen von Dateinamen und eventuell noch nicht überschriebenen Datenblöcken. Zudem verbessert sich auch die ext4-Performance.

Sowohl AMD als auch Intel haben ihre Architekturen um das Absichern und Verschlüsseln von virtuellen Maschinen erweitert. Die AMD Secure Encryption Virtualization (SEV) und die Intel Secure Guard Extensions (SGX) sollen verschlüsselte und abgesicherte Gastsysteme selbst auf kompromittierten Host-Systemen schützen. Gerade bei diesen für den virtualisierten Rechenzentrumsbetrieb wichtigen Features baut Linux 5.13 seine Unterstützung weiter aus.

Die neue KVM-API unterstützt die Live-Migration von Gastsystemen unter AMD Secure Encrypted Virtualization (SEV). Die Entwicklung in Linux 5.13 ist aber noch nicht vollständig, sodass 5.14 in diesem Punkt noch mal nachlegen wird. Neu hinzugekommen ist auch die Option, mehrere virtuelle Maschinen den gleichen Verschlüsselungskontext nutzen zu lassen.

Auf Intel-Plattformen ist es möglich, die Software Guard Extensions (SGX) nun auch in virtuellen Maschinen zu nutzen. Bei KVM verbessert und optimiert Linux 5.13 den neuen MMU-Code und bringt neue Virtio-Treiber für Bluetooth-Controller und Soundkarten mit.

Linux 5.13 ist sicherlich kein reines Standard-Update. Es enthält Neuerungen und versucht, neue Wege zu definieren. Auch für 5.14 sind die ersten Weichen gestellt. Einige neue Features sind erst "Zwischenstände" wie etwa die Live-Migration bei SEV und allen voran die Lauffähigkeit und Verfügbarkeit von Treibern für Apples M1-Systeme. Was genau die Kernel-Entwickler nachlegen, wird man in etwa zwei Wochen beim ersten Release-Candidate von Linux 5.14 sehen.

(olb [21])


URL dieses Artikels:
https://www.heise.de/-6129642

Links in diesem Artikel:
[1] https://www.heise.de/tests/Linux-Distribution-im-Test-Elementary-OS-7-setzt-konsequent-auf-Flatpak-7495252.html
[2] https://www.heise.de/tests/Linux-Distribution-MX-Linux-MX-21-fuer-Mausliebhaber-im-Test-6319444.html
[3] https://www.heise.de/tests/Linux-Distribution-Debian-11-im-Update-Check-6199625.html
[4] https://www.heise.de/ratgeber/Daten-verschluesselter-Linux-Installationen-retten-6197593.html
[5] https://www.heise.de/ratgeber/Anleitung-Home-Verzeichnisse-unter-Linux-verschluesseln-mit-Gocryptfs-6193379.html
[6] https://www.heise.de/tests/PetaLinux-Embedded-Linux-per-Kommandozeile-im-Kurztest-6054536.html
[7] https://www.heise.de/tests/Security-Linux-fuer-digitale-Forensik-Kali-Linux-2021-1-im-Test-6043821.html
[8] https://www.heise.de/hintergrund/Linux-Grafikarchitektur-Wayland-etabliert-sich-aber-X-Server-bleiben-6038002.html
[9] https://www.heise.de/tests/Fedora-Linux-34-im-Test-Neuer-Sound-Server-PipeWire-und-Wayland-Verbesserungen-6042242.html
[10] https://www.heise.de/hintergrund/Linux-Wer-die-Entwicklungsrichtung-des-Linux-Kernel-wirklich-vorantreibt-6029402.html
[11] https://www.heise.de/hintergrund/Linux-Wie-Aenderungen-in-den-Linux-Kernel-fliessen-6029596.html
[12] https://www.heise.de/hintergrund/Treiber-unter-Linux-Warum-das-manchmal-so-muehsam-ist-5023706.html
[13] https://www.heise.de/ratgeber/Linux-Komplexe-Audio-Setups-mit-PulseAudio-konfigurieren-6000628.html
[14] https://outflux.net/slides/2020/lca/cfi.pdf
[15] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.13&id=57fa2369ab17d67e6232f85b868652fbf4407206
[16] https://landlock.io/
[17] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=17ae69aba89dbfa2139b7f8024b757ab3cc42f59
[18] https://lore.kernel.org/lkml/CAHk-=wj7E9iTGHbqfgtaTAM09WrVzwXjda2_D59MT8D_1=54Rg@mail.gmail.com/
[19] https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.13
[20] https://www.heise.de/thema/Linux-und-Open-Source
[21] mailto:olb@heise.de