Linux-Sandboxing und Fil-C
(fil-c.org)- Speichersicherheit und Sandboxing sind voneinander unabhängige Sicherheitskonzepte; erst beides zusammen bildet eine starke Verteidigungsarchitektur.
- Fil-C ist eine speichersichere Implementierung für C/C++, die Sicherheit bis auf Ebene der Linux-Systemaufrufe gewährleistet und sich auch in Systemkomponenten wie OpenSSH einsetzen lässt.
- Bei der Portierung der seccomp-BPF-basierten Sandbox von OpenSSH auf Fil-C waren die Begrenzung der Thread-Erzeugung und die Anpassung des seccomp-Filters die zentralen Aufgaben.
- Für die Verwaltung von Hintergrund-Threads in der Fil-C-Runtime wurde die API
zlock_runtime_threads()hinzugefügt, um das Thread-Verhalten innerhalb der Sandbox zu steuern. - Fil-C implementiert
prctlso, dass der Aufruf synchron auf alle Runtime-Threads angewendet wird, damit no_new_privs und der seccomp-Filter konsistent auf den gesamten Prozess angewendet werden.
Das Verhältnis von Speichersicherheit und Sandboxing
- Speichersicherheit und Sandboxing sind unterschiedliche Sicherheitsebenen; nur eine von beiden reicht nicht für vollständigen Schutz aus.
- Beispiel für speichersicher, aber nicht sandboxed: ein Java-Programm, das über Benutzereingaben Dateien überschreiben kann.
- Beispiel für sandboxed, aber nicht speichersicher: ein in Assembler geschriebenes Programm mit eingeschränkten Rechten.
- Reale Sandboxes haben strukturelle Schwachstellen, etwa die erlaubte Kommunikation mit einem Broker-Prozess.
- Deshalb ist die Kombination aus Speichersicherheit und Sandboxing die beste Verteidigungsstrategie.
Die Kombination von Fil-C und der OpenSSH-Sandbox
- Fil-C ist eine speichersichere Implementierung für C/C++, die Sicherheit auf Ebene der Linux-Systemaufrufe aufrechterhält.
- Die Fil-C-Runtime kann auch in niedrigschwelligen Systemkomponenten wie
initundudevdlaufen. - OpenSSH funktioniert unter Fil-C normal und nutzt die seccomp-BPF-Sandbox.
- Die Fil-C-Runtime kann auch in niedrigschwelligen Systemkomponenten wie
- OpenSSH baut seine Sandbox unter Linux mit den folgenden Werkzeugen auf.
chrootzur Einschränkung des Dateisystemzugriffs- Ausführung als Benutzer/Gruppe
sshd setrlimitzur Begrenzung des Öffnens von Dateien und der Prozesserzeugung- seccomp-BPF-Filter, die nur erlaubte Systemaufrufe zulassen
- Fil-C unterstützt
chrootund den Benutzerwechsel grundsätzlich, dochsetrlimitund seccomp-BPF können mit dem Verhalten der Runtime kollidieren und erfordern daher zusätzliche Anpassungen.
Thread-Steuerung in der Fil-C-Runtime
- Die Fil-C-Runtime verwendet Hintergrund-Threads für die Garbage Collection und hält sie bei Bedarf automatisch an oder startet sie neu.
- Die
setrlimit-Sandbox von OpenSSH soll die Erzeugung neuer Prozesse verbieten, daher kann die Thread-Erzeugung der Runtime damit in Konflikt geraten. - Zur Lösung dieses Problems wurde in
<stdfil.h>die APIzlock_runtime_threads()ergänzt.- Die Runtime erstellt sofort alle benötigten Threads und deaktiviert danach deren automatisches Beenden.
- Der Aufruf erfolgt in der Funktion
ssh_sandbox_childvon OpenSSH vorsetrlimitoder seccomp-BPF.
Anpassung des OpenSSH-seccomp-Filters
- Nach Anwendung von
zlock_runtime_threads()funktionieren die meisten Sandbox-Funktionen unverändert weiter. - Am seccomp-Filter wurden die folgenden Änderungen vorgenommen.
- Bei Verstößen wird
SECCOMP_RET_KILL_PROCESSgesetzt, damit auch die Fil-C-Hintergrund-Threads beendet werden. MAP_NORESERVEwurde zur Allowlist hinzugefügt, um den Speicher-Allocator von Fil-C zu unterstützen.- Aufrufe von
sched_yieldwerden erlaubt, da sie in der Lock-Implementierung von Fil-C verwendet werden.
- Bei Verstößen wird
- Die von Fil-C für die Synchronisierung verwendeten
futex-Aufrufe waren bereits erlaubt, sodass keine weiteren Änderungen nötig waren.
Die Implementierung von prctl in Fil-C
- OpenSSH verwendet beim Installieren des seccomp-Filters zwei
prctl-Aufrufe.PR_SET_NO_NEW_PRIVS, um den Erwerb zusätzlicher Privilegien zu verhindernPR_SET_SECCOMP, SECCOMP_MODE_FILTER, um den Filter zu aktivieren
- Das Problem ist, dass
prctlnur auf den aufrufenden Thread wirkt, wodurch andere Runtime-Threads von Fil-C ohne Filter verbleiben könnten. - Um das zu verhindern, nutzt Fil-C die API
filc_runtime_threads_handshake(), um die Anwendung auf alle Runtime-Threads zu synchronisieren.- Dadurch wird sichergestellt, dass jeder Thread denselben
prctl-Aufruf ausführt. - Wenn mehrere Benutzer-Threads existieren, löst Fil-C einen Sicherheitsfehler aus und verstärkt so den Schutz.
- Dadurch wird sichergestellt, dass jeder Thread denselben
Fazit
- Die Kombination aus Speichersicherheit und Sandboxing ist die stärkste Sicherheitskombination.
- Fil-C integriert die seccomp-basierte Sandbox von OpenSSH vollständig und bewahrt dabei Speichersicherheit ohne Absenkung des Schutzniveaus.
- Mit Fil-C in einer Linux-Umgebung lassen sich Sicherheit auf Systemebene und Sicherheit auf Sprachebene gleichzeitig erreichen.
1 Kommentare
Hacker-News-Kommentare
Ich frage mich, warum landlock nicht erwähnt wird.
Es gibt einen hybriden Compiler-Ansatz von C → WASM → C.
Damit lässt sich die Interaktion mit dem OS vollständig kontrollieren, während der Speicherzugriff wie bei WASM sandboxed wird und es technisch trotzdem C-Code bleibt.
Mehr dazu gibt es bei RLBox.
Man kann den Speicher beschädigen, aber der Schaden bleibt auf die Sandbox begrenzt.
Systeme wie SECCOMP sind bürokratisch, weil jede Interaktionsrichtlinie im Detail definiert werden muss.
Fil-C verfolgt dagegen den Ansatz, dass Sprache und Runtime selbst das korrekte Verhalten des Programms sicherstellen sollen.
Fil-C-Binaries sind normale ausführbare Dateien und können daher auch zusammen mit Sandboxes wie SECCOMP verwendet werden.
Linux hat 20 Jahre gebraucht, um die
prctl-Schnittstelle zu schaffen, daher wird man auf etwas Vergleichbares in WASI wohl noch 10 Jahre warten müssen.Auch innerhalb der Sandbox kann man einen seltsamen Ausführungsfluss erzeugen.
Der Autor von Fil-C macht oft technisch interessante Erfindungen, aber ich bin unsicher, ob die Implementierung ausreichend verifiziert wurde.
Es hieß, man könne setuid-Programme mit Fil-C kompilieren, aber die Änderungen an
ld.sokönnten riskant sein.setuid-Apps müssen bei Umgebungsvariablen, File Descriptors,
rlimit, Signalen usw. sehr defensiv geschrieben sein.Diese Teile wirken noch unfertig, daher ist der Einsatz in echter Infrastruktur mit Risiko verbunden.
Trotzdem könnte es spannend sein, eine Codebasis mit Fil-C zu testen, weil dabei interessante Bugs gefunden werden könnten.
Die Änderung an
ld.soist nur eine kleine Anpassung, um das libc-Layout zu vermitteln.Der setuid-bezogene
getenv-Bug wurde bereits aufsecure_getenvumgestellt.In der Kritik steckt etwas Wahrheit, aber auch etwas FUD.
Bei Fil-C können Pointer und Capability in Data-Race-Situationen inkonsistent werden.
Dadurch kann es zu Verletzungen der Speichersicherheit kommen.
Der Autor bestreitet das, aber der Vergleich mit Java ist unpassend.
Die Technik ist großartig, aber die Haltung des Autors untergräbt das Vertrauen.
WASM ist sowohl Sandbox als auch Laufzeitumgebung und kann je nach Nutzung ein gewisses Maß an Speichersicherheit bieten.
Wenn man C nach WASM kompiliert, bleiben die Bugs zwar bestehen, aber ihre Auswirkungen werden begrenzt.
Deshalb ist es richtig, WASM als Sandboxing-Technik einzuordnen, aber als Laufzeitumgebung bietet es noch mehr Möglichkeiten.
Ein Bug in Modul B kann die Daten von Modul A lesen.
Damit ist WASM nur ein Ersatz für leichtgewichtige Prozess-Sandboxes.
Über C könnte man schließlich auch sagen, es sei „je nach Verwendung sicher“.
WASM verhindert ein Entkommen aus der Runtime, aber nicht das Entkommen aus dem Speicher des internen Programms.
Es wird um einen Vergleich zwischen Fil-C und Rust gebeten.
Fil-C eignet sich gut, um bestehende C-Programme mit Fokus auf Kompatibilität und Sicherheit zu härten.
Rust ist stark, wenn man bei neuen Codebasen statische Sicherheit und Performance erreichen will.
Fil-C hat etwas Performance-Verlust, ist aber nützlich für bestehende C-Codebasen (
ffmpeg,nginx,sudousw.).Rust punktet bei Multithreading und beim Typsystem.
Ziel ist weniger eine Verbesserung des Sprachdesigns als vielmehr die Herstellung von Speichersicherheit.
Die Konkurrenz liegt eher bei D, Nim und Go als bei Rust.
Rust verhindert sie bereits zur Compile-Zeit.
Beide Ansätze sind orthogonal, und man könnte auch Rust um Runtime-Prüfungen im Stil von Fil-C ergänzen.
MicroVMs werden immer populärer.
Ich frage mich, wie Fil-C davon profitieren könnte.
Ich hoffe, dieses Projekt bekommt mehr Aufmerksamkeit.
Es wäre gut, wenn zentrale Werkzeuge wie sudo oder polkit speichersicher ausgeliefert würden.
Ich würde mir wünschen, dass auch in speichersicheren Sprachen Sandboxing stärker genutzt wird.
Schade, dass selbst bei Rust oder Go OS-Sandboxes kaum verwendet werden.
Es ist schwer, es auf Bibliotheksebene zu konfigurieren, und es reagiert empfindlich auf Kernel-Versionen oder libc-Implementierungen.
Eingaben hinter Pointern wie Dateipfade lassen sich nicht filtern, was klare Grenzen setzt.
Deshalb muss man es normalerweise direkt auf Anwendungsebene konfigurieren.
Go hat dagegen eine große Runtime, wodurch es schwieriger ist, es wie Fil-C sicher zu machen.
Ich frage mich, worin sich Fil-C von clangs Address Sanitizer (ASan) unterscheidet.
Wenn es nur darum geht, zur Laufzeit einen Panic auszulösen, ist es dann gerechtfertigt, das als „Speichersicherheit“ zu bezeichnen?
Manche Bugs werden nicht erkannt.
Es arbeitet mit „Red Zones“ um den Speicher herum, sodass ein Fehler nur mit etwas Glück entdeckt wird.
Speichersicherheit bedeutet nicht „stürzt nicht ab“, sondern „falsche Zugriffe können keine Wirkung entfalten“.
Es wird gefragt, warum man keine vollständige VM als Sandbox verwenden sollte.
Ein Prozess parst Eingaben ohne Rechte, ein anderer läuft mit hohen Rechten.
Die beiden Prozesse kommunizieren über IPC.
Eine VM erhöht zwar die Sicherheit, hat aber hohen Overhead, und Funktionen wie GPU- oder Dateizugriff werden komplizierter.
Deshalb ist Sandboxing auf OS-Ebene im Allgemeinen sauberer.
Man muss der GPU dedizierten Zugriff geben, und auch Qubes verbindet nur über X11-Forwarding und hat daher keine Beschleunigung.