1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • Seit Linux 6.9 schlug ein Tool, das beim Suspend von Laptops Laufwerke sperrt, still fehl, sodass die Schlüssel der LUKS-Vollverschlüsselung im Arbeitsspeicher verblieben
  • Ursache war eine unerwartete Wechselwirkung zwischen einem im Mai 2024 in Linux 6.9 aufgenommenen Refactoring des Block-Device-Zugriffs und dem Verschlüsselungscode; der vorgeschlagene Fix ist ein Einzeilen-Patch
  • Beim vollständigen Herunterfahren trat das Problem nicht auf, aber bei suspend-to-RAM blieben die Schlüssel erhalten, sodass ein Angreifer, der einen eingeschalteten Laptop in die Hände bekommt, die Schlüssel aus dem RAM extrahieren konnte
  • Entdeckt wurde das Problem beim Aufräumen eines NixOS-Ports von Debians cryptsetup-suspend, als Einträge in /proc/keys auffielen; ein QEMU-Speicherdump bestätigte, dass der eigentlich zu löschende Volume Key noch vorhanden war
  • NixOS-Integrationstests und ein cryptsetup-Patch mit Warnhinweisen wurden vorgeschlagen; Sicherheitsfunktionen wie das Löschen von Schlüsseln direkt vor dem Suspend können zwar korrekt wirken, doch ohne echte Speicherprüfung sind Fehlschläge leicht zu übersehen

Seit Linux 6.9 verbleiben LUKS-Schlüssel während suspend im Speicher

  • Seit Linux 6.9, also seit Mai 2024, schlug ein Tool, das beim Suspend von Laptops Laufwerke sperrt, still fehl
  • LUKS-Vollverschlüsselung wird eingesetzt, um Daten bei Verlust, Beschlagnahmung oder Diebstahl eines Laptops zu schützen; in diesem Fall blieben die Verschlüsselungsschlüssel während des Suspend jedoch im Arbeitsspeicher
  • Beim vollständigen Herunterfahren funktionierte es weiterhin, doch die Auswirkungen sind größer, weil viele Laptops nicht komplett ausgeschaltet, sondern in suspend-to-RAM versetzt werden
  • Wenn jemand einen noch eingeschalteten Laptop in die Hände bekam, konnten die im Speicher verbliebenen Schlüssel offengelegt werden
  • Als Software mit ähnlichem Zweck unter Windows wurde VeraCrypt erwähnt; später wurde in den Kommentaren jedoch klargestellt, dass „canonical software“ nicht die am weitesten verbreitete Software meint, sondern eine typische Empfehlung aus der IT-Sicherheit

Ursache und Einzeilen-Patch

  • Ursache war der Linux-Kernel-Refactoring-Commit md: port block device access to file
    • Die Änderung selbst war ein sinnvolles und nützliches Refactoring, führte aber zu einer Wechselwirkung über entfernte Codebereiche hinweg mit dem Verschlüsselungscode
  • Der vorgeschlagene Fix ist ein Einzeilen-Patch
  • Der Patch-Autor erklärte, ohne formale Verifikation könne er nicht sagen, ob dieser Patch korrekt sei und ob es keine weiteren Wechselwirkungen über entfernte Codebereiche hinweg gebe
  • Auch Folgearbeiten zur Vermeidung erneuter Regressionen wurden vorgeschlagen

Entdeckungsprozess

  • Ausgangspunkt war das Aufräumen eines NixOS-Ports von Debians cryptsetup-suspend
  • Sowohl das Debian-Original als auch der NixOS-Port hatten eine störende, aber nicht schädliche Race Condition, durch die der Laptop manchmal nicht einschlief
  • Um das zu beheben, sollte Pali Rohárs nicht gemergter Kernel-Patch zur Schlüssellöschung bei dm-crypt suspend/hibernation wiederbelebt werden
  • Dabei wurden cryptsetup und der Kernel-Quellcode untersucht; in der Dokumentation stand, dass ein Keyring an den aufrufenden Thread gebunden ist und beim Beenden des Threads entfernt wird
  • In dem zuvor unbekannten /proc/keys waren jedoch Einträge sichtbar, was den Verdacht verstärkte
  • Schließlich wurde eine QEMU-VM gestartet und der Speicher gedumpt; dabei zeigte sich, dass der eigentlich zu löschende LUKS Volume Key unverändert vorhanden war

NixOS-Projekt secure suspend-to-RAM

  • Das separat veröffentlichte Projekt secure-suspend bietet experimentelles secure suspend-to-RAM für NixOS
  • Bei üblicher Vollverschlüsselung bleiben die Schlüssel im Speicher, wenn ein Laptop im Suspend-Zustand ist, wodurch er für Cold-Boot-Angriffe oder RAM-Exfiltration anfällig sein kann
  • Dieses Projekt belebt Pali Rohárs alten Kernel-Patch wieder und löscht beim Suspend die LUKS-Verschlüsselungsschlüssel
  • Es ist von Debians cryptsetup-suspend inspiriert, nutzt aber einen Kernel-Patch, um die Race Condition zu vermeiden, bei der Laptops gelegentlich nicht einschlafen, und ergänzt weitere Schutzmaßnahmen
  • Verschlüsselte Root-Dateisysteme werden vollständig unterstützt, außerdem werden Integrationstests bereitgestellt
  • Kernel-Patch und User-Space-Tools können auch für andere Linux-Distributionen angepasst werden
  • Das Projekt ist als secure-suspend veröffentlicht

Warum die Sicherheitsprüfung bei suspend schwierig ist

  • Ein sichtbarer Sperrbildschirm nach dem Suspend bedeutet nicht, dass das Speichergerät tatsächlich gesperrt wurde
  • Wenn unmittelbar nach dem Aufwachen aus dem Suspend direkt auf die Festplatte zugegriffen werden kann, war das Speichergerät von Anfang an nicht gesperrt
    • Zum Beispiel lässt sich der Festplattenzugriff mit einem Skript prüfen, das hinter dem Sperrbildschirm weiterläuft
    • Wenn nach dem Suspend eine Anmeldung per SSH-Public-Key möglich ist, ohne zuvor den verschlüsselten Speicher zu entsperren, lässt sich leicht feststellen, dass das Speichergerät nicht gesperrt war
  • In den Kommentaren wurde auch angemerkt, dass die Standardkonfigurationen von Ubuntu oder Debian gar nicht erst versuchen, einen solchen Schutz bereitzustellen
  • Ob ein Versuch, das Speichergerät zu sperren, tatsächlich korrekt funktioniert hat, muss separat verifiziert werden
    • Log-Zeitstempel können vor dem Suspend entstanden sein, aber erst nach dem Wake geschrieben worden sein
    • Umgekehrt können Logs, die direkt nach dem Wake erstellt wurden, bevor die Systemzeit angepasst wurde, so aussehen, als stammten sie vom Zeitpunkt des Suspend
  • Ob die Sperrung des Speichergeräts direkt vor dem Suspend oder unmittelbar nach dem Resume erfolgt, kann für Nutzer gleich aussehen, ist sicherheitstechnisch aber entscheidend verschieden
  • Der NixOS-Integrationstest bootet das System in einer virtuellen Maschine und dumpt den Speicher, um zu prüfen, ob der Schlüssel beim Suspend tatsächlich gelöscht wurde

1 Kommentare

 
GN⁺ 3 시간 전
Meinungen auf Hacker News
  • Es ist zwar ein interessanter Bug, aber der Titel wirkt ein wenig wie Clickbait.
    Soweit ich es verstanden habe, ist cryptsetup luksSuspend weniger eine offiziell unterstützte Funktion als eher eine von Debian erstellte Erweiterung; daher frage ich mich, ob diese Regression nicht nur Debian betroffen hat.
    Ich bin mir nicht sicher, ob man dem Kernel die Schuld für eine Funktion geben kann, die weder unterstützt noch breit getestet wird.
    Trotzdem ist es beeindruckend, und es ist gut, dass es nun Tests gibt, damit diese Regression nicht wieder auftritt. Ich stimme auch der Meinung des OP zu, dass NixOSTests wirklich großartig ist.
    Nur sieht der Titel so aus, als wäre es ein weitverbreitetes Problem und nicht eines einer bestimmten Distribution.

    • Ich wollte einen technisch korrekten Titel, nicht Klicks provozieren.
      Stimmt. Für Leute mit Standardeinstellungen hat es keine Auswirkungen, weil sie von vornherein nicht erwarten würden, dass der Volume-Key während des Suspend sicher ist.
      Debians Lösung wurde auf mehrere, wahrscheinlich die meisten anderen Distributionen portiert, und es dürfte auch einige Leute gegeben haben, die eigene Ports gepflegt haben.
      Die Manpage thread-keyring(7) verspricht, dass „der Thread-Keyring zerstört wird, wenn der Thread, der ihn referenziert, beendet wird“.
      Das Projekt cryptsetup hat sich bei dem Mechanismus, Schlüssel vom Userspace in den Kernelspace zu bringen, auf diese Eigenschaft verlassen; Kernel 6.9 führte eine Regression ein, die diese Eigenschaft brach.
    • Ich bin verwirrt, warum das als Debian-spezifisch bezeichnet wird. luksSuspend ist eine Upstream-Funktion und wurde 2009 mit Release v1.1.0 hinzugefügt.
      Ich habe es früher gelegentlich auch unter Arch und openSUSE verwendet, und es existiert definitiv auch in Nicht-Debian-Distributionen.
      Vermutlich ist die automatische Integration mit System-Suspend gemeint, aber das geht am Kern vorbei. luksSuspend ist so dokumentiert, dass es die Schlüssel aus dem Systemspeicher löscht, und durch den betreffenden Refactoring-Patch in Linux 6.9 funktionierte dieses Verhalten nicht mehr.
      Allerdings kann man es in der Praxis auch als Bug in cryptsetup sehen, weil es sich auf ein sehr spezifisches Lebensdauerverhalten von Kernel-Keyring-Schlüsseln verlassen hat; man könnte auch argumentieren, dass der Userspace sie expliziter hätte löschen müssen.
      [1]: https://gitlab.com/cryptsetup/cryptsetup/-/commit/3cea5dcc7b...
      [2]: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/docs/v1...
      [3]: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/93...
    • Der Unterbefehl befindet sich im offiziellen cryptsetup-Repository, und die Beschreibung scheint auch zu passen: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/man/cry...
    • Ich habe diese Funktion unter Arch verwendet, und sie ist auch mit normalem LUKS nutzbar. Soweit ich weiß, wird sie beim Suspend allerdings nicht standardmäßig verwendet.
      Gemeint sind vermutlich Geräte, die nach luksSuspend tatsächlich auf sinnvolle Weise RAM-Suspend ausführen; das war anfangs auf Debian ausgerichtet und kam später auch zu Arch, war aber bei beiden nicht der Standard.
    • Ich frage mich, ab welcher Debian-Version 6.9 erstmals ausgeliefert wurde.
  • Ich sehe keine wirklich andere Möglichkeit. Wenn man Sleep, also RAM suspend, nutzt, wird alles im RAM gehalten und ist zwar verschlüsselt, aber der Master-Key bleibt meines Wissens im Kernel-Speicher.
    Bei Hibernate, also Suspend auf die Festplatte, wird dagegen der gesamte RAM-Inhalt einschließlich Master-Key auf die Platte geschrieben und verschlüsselt, und der RAM wird gelöscht.
    Beim Aufwachen muss man die Passphrase erneut eingeben, um den Master-Key zu entschlüsseln und den Platteninhalt wieder in den Speicher zu laden.

    • Genau. Wenn man bei den meisten Standard-Linux-Distributionen einfach den Laptop suspendiert, bleibt alles im Speicher, einschließlich des Master-Key.
      Debian hat jedoch zuerst das optionale Add-on cryptsetup-suspend gebaut; dieses führt den Befehl luksSuspend aus, der die Schlüssel aus dem Speicher löschen soll, und verlangt beim Resume erneut die Passphrase.
      Bis Kernel 6.8 funktionierte es wie beschrieben, ab Kernel 6.9 hörte es stillschweigend auf zu funktionieren.
    • Intel- und AMD-CPUs der letzten etwa fünf Jahre unterstützen beide vollständige Speicherverschlüsselung, die für das Betriebssystem transparent ist.
      Wenn man diese Funktion einschaltet, gehören Cold-Boot-Angriffe der Vergangenheit an. Sie ist normalerweise nur deshalb standardmäßig deaktiviert, weil sie die RAM-Geschwindigkeit um etwa 0,5 % senkt.
  • Da ich nach dem Sleep das Boot-Passwort nicht erneut eingebe, ist klar, dass der Verschlüsselungsschlüssel noch im Speicher liegt.

    • Offensichtlich verwendet deine Distribution cryptsetup-luksSuspend nicht.
  • Das ist für mich kein besonders großes Problem.
    Der einzige Grund, warum ich Festplattenverschlüsselung nutze, ist, dass ich mir beim Verkauf eines Laptops keine Sorgen machen muss, dass jemand Steuerunterlagen oder Kreditkartendaten durchsucht.
    Natürlich lösche ich den Laptop trotzdem, aber wenn die Daten auf Laufwerksebene verschlüsselt sind, sehe ich nur ein sehr geringes Risiko, dass sie mit Forensik-Tools o. Ä. wiederhergestellt werden.

    • Als brauchbarer Kompromiss reicht es auch, nur den LUKS-Header zu löschen.
      LUKS verwendet einen Anti-Forensik-Algorithmus, bei dem der vollständige Volume-Key vorhanden sein muss, um die Platte zu öffnen. Dabei werden Schlüsselblöcke mit einem Diffusionsalgorithmus kombiniert und per XOR zum eigentlichen Master-Key zusammengesetzt; theoretisch sollte schon das Löschen eines einzigen Sektors des Volume-Keys alles unrecoverbar machen.
      Das heißt: Wenn auch nur ein Schlüsselblock fehlt, lässt sich der Rest nicht einfach erraten.
    • Unter der Annahme, dass der Verschlüsselungsschlüssel stark ist, ist Löschen theoretisch redundante Arbeit.
  • Ich bin keineswegs Sicherheitsexperte, aber wenn man sieht, dass heutzutage regelmäßig kritische Sicherheitsbugs entdeckt werden, die dadurch entstanden sind, dass „beim Refactoring eine einzelne C-Prüfzeile über Dateigrenzen hinweg übersehen wurde“, wirkt schon die Prämisse einer riesigen sicheren Open-Source-C-Codebasis fragwürdig.
    Das ist kein reines C-Problem, aber gerade in C ist es meiner Ansicht nach schwieriger, Invarianten konsistent durchzusetzen und nachzuverfolgen — erst recht bei Codeänderungen.
    Ich weiß auch nicht, ob funktionale Programmierung, die Invarianten in Typen kodiert, eine realistisch skalierbare Lösung ist. Model Checking? LLM-Fuzzing? Weniger Grundbausteine mit klaren Grenzen? Wurde seLinux auf diese Weise „geprüft“?

    • Die Nachteile von C sind sichtbar, und ich würde es für neue Projekte im Allgemeinen auch nicht empfehlen, aber ich sehe diesen konkreten Bug nicht als gutes Beispiel für etwas, das Rusts Borrow Checker oder das Typsystem einer anderen Sprache abgefangen hätte. Selbst statische Analyzer hätten ihn wohl nicht gefunden.
      Im Kern ist es so etwas:
      original: DoTheThing()
      new: DoTheThingSlightlyDifferentButKeepMyCredentialsAlive()
      fix: DoTheThingSlightlyDifferentButDoInFactNOTKeepMyCredentialsAlive()
      Meiner Erfahrung nach stammt ein großer Teil der kniffligen Bugs aus Verletzungen von Systeminvarianten auf höherer Ebene, und das wirkt nicht wie etwas, das sich automatisieren lässt.
      Selbst mit etwas wie Lean kann man beweisen, dass ein Programm bestimmte Eigenschaften erfüllt, aber man hätte zuerst auf diese Eigenschaft kommen müssen. Der Beweis entdeckt die Invariante nicht an deiner Stelle.
      Wenn man an die relevante Sicherheitseigenschaft gedacht hätte, wäre es nicht schwer gewesen, einen Regressionstest zu schreiben. Der wirklich schwierige Teil liegt meiner Ansicht nach nicht darin, die Implementierung sicher auszudrücken, sondern zu erkennen, dass es eine Eigenschaft gibt, die die Implementierung bewahren muss.
    • Die Prämisse einer sicheren öffentlichen Codebasis ist an sich in Ordnung.
      Das Problem ist, dass höhere Auditierbarkeit nicht automatisch bedeutet, dass auch mehr auditiert wird.
      Es braucht Leute mit ausreichender Kompetenz, die genügend Zeit in die Arbeit investieren.
    • Auch bei einer Übersetzung nach Rust wäre daraus „eine Rust-Prüfung in einer Zeile übersehen“ geworden.
      Das ist ein Bug, der aus sich überschneidenden Belangen und mangelndem domänenübergreifendem Wissen entstanden ist. In Lisp oder Assembler wäre es vermutlich genauso gewesen.
    • Die Lehre daraus ist: Wenn es für eine Funktion nicht mindestens einen zugehörigen Testfall gibt, ist sie keine echte Funktion.
    • Die Prämisse einer „riesigen sicheren Open-Source-C-Codebasis“ wirkt deshalb fragwürdig, weil Code Review manchmal nicht viel anders ist als eine idealisierte Variante des Halteproblems, bei der man Zugriff auf eine formalisierte Version der Spezifikation haben kann.
      Mit anderen Worten: Es gibt keine strenge Definition dessen, was ein Sicherheitsproblem ist.
  • Brauchte eine Bundesbehörde dringend eine Möglichkeit, an die Schlüssel zu kommen? Ist das eine Bugdoor? Wurde der Commit nachverfolgt?
    Ich habe in letzter Zeit viele solcher Muster gesehen und werde langsam etwas misstrauisch. Vielleicht liegt es auch daran, dass die Leute sensibler dafür geworden sind und mehr davon posten.

    • Das ist eine Regression. Auch die Userspace-Anwendung wäre stillschweigend fehlgeschlagen, und es war das Ergebnis mehrerer Unachtsamkeiten.
      Dass sich ein Verschlüsselungsschlüssel im Speicher befindet, heißt nicht automatisch, dass er extrahierbar ist. Es ist eher so, dass er unnötig auf unbestimmte Zeit an einem Ort verbleibt, an dem er eigentlich nicht sein sollte.
  • Solche Regressionen sind leicht zu übersehen, weil alles weiter „funktioniert“. Sicherheitsbugs zeigen sich oft nicht von selbst.

    • Genau. Deshalb sind Integrationstests für solche Funktionen umso wichtiger.
      Es hat auch Spaß gemacht, sie zu schreiben, und sie haben es ermöglicht, git-bisect laufen zu lassen, um das konkrete Kernel-Refactoring zu finden, das diesen Bug eingeführt hat: https://github.com/NixOS/nixpkgs/pull/532499
  • Auf meinem Fedora-Laptop habe ich Linux so konfiguriert, dass es 15 Minuten nach dem Suspend in den Hibernate-Modus auf die Festplatte wechselt. Wenn man den Arbeitsspeicher stromlos macht, spielt dieser Debian-spezifische Bug keine Rolle.
    Debians Erweiterung der Linux-Tools ist theoretisch gut, aber wenn man sich tatsächlich Sorgen um Cold-Boot-Angriffe macht, müssen nicht nur die LUKS-Schlüssel, sondern alle Schlüssel und wichtigen Dokumente aus dem Speicher gelöscht werden.
    Deshalb ist Hibernate am Ende der einzig richtige Weg, Cold Boot zu verhindern.

    • Stimme zu. Oder man belebt FridgeLock wieder: https://www.sec.in.tum.de/i20/publications/fridgelock-preven...
    • Aber woher kommt beim Resume der Schlüssel zum Entschlüsseln des Speichers?
      Soweit ich weiß, ist das ohne TPM nicht praktikabel. Und wenn man TPM nutzt, legt man sein Schicksal faktisch in die Hände des TPM.
  • Man muss sich nur vorstellen, wie dieser HN-Thread ausgesehen hätte, wenn diese Schwachstelle in einem kommerziellen Betriebssystem gesteckt hätte.
    Der Top-Kommentar hätte garantiert gelautet, dass Applosoft sich nicht mehr um Softwarequalität kümmert, oder „genau das passiert, wenn man Vibe-Coding-Müll ins OS lässt“.
    Die Kommentare darunter wären Verschwörungstheorien über den Überwachungsindustriekomplex und die NSA gewesen — anderswo wäre das verrückt, aber auf HN nicht.

  • Ich verstehe nicht, warum etwas so Wichtiges nicht bei jedem Build getestet wird.