- Die im März 2024 entdeckte
xz-Hintertür hätte in betroffenensshd-Umgebungen zu Remote Code Execution führen können, und der NixOS-Fall zeigt, dass reproduzierbare Builds Manipulationen in der Supply Chain maschinell sichtbar machen können - Die schädlichen Bestandteile versteckten sich nicht im gesamten
xz-Git-Repository, sondern in den maintainerbereitgestellten Release-Tarballs von5.6.0und5.6.1; während des Builds erzeugten sie aus gefälschten.xz-Testdateien Shell-Skripte und Objektdateien - Der Angriff nutzte ifunc in
glibc, um beim Laden vonliblzmaCode auszuführen, und zielte auf Debian- und Fedora-artige Umgebungen, in denensshdüberlibsystemdtransitiv vonliblzmaabhängt - NixOS bevorzugt normalerweise automatisch von GitHub erzeugte Quellarchive, doch weil
xzim Bootstrap-Schritt vonnixpkgsliegt, musste man den Maintainer-Tarballs vertrauen, um einenautoconf-Abhängigkeitszyklus zu vermeiden - Als vorgeschlagene Abwehrmaßnahme wird
xznach dem Bootstrap noch einmal aus GitHub-Quellen gebaut und das Ergebnis mit dem bisherigen Tarball-Artefakt verglichen; nach Aktivierung der Bedingung zeigten sich Unterschiede in der Größe vonliblzma.sound das zusätzliche Symbol_get_cpuid
Wie sich die xz-Hintertür versteckte
xzist eine Komprimierungs- und Dekomprimierungssoftware, die in zentralen Pfaden von Linux-Distributionen häufig verwendet wird, wenn Quell-Tarballs entpackt werden- Im März 2024 wurde in
xzeine Hintertür entdeckt. Der bösartige Maintainer Jia Tan gewann über etwa drei Jahre hinweg Vertrauen, erhielt Push-Rechte für das Repository und schleuste dann zwischen legitimen Beiträgen schrittweise obfuskierten Code ein - Andres Freund untersuchte eine
ssh-Leistungsverlangsamung von 500 ms auf mehreren Debian unstable-Maschinen, verfolgte sie bis zuliblzmazurück und identifizierte sowie dokumentierte die Hintertür - Ziel der Hintertür war es,
sshzu kapern, sodass ein Angreifer beim Login mit einem bestimmten RSA-Schlüssel beliebige Befehle auf dem Rechner des Opfers ausführen konnte
Bösartige Tarballs und während des Builds erzeugte Objekte
- Die schädlichen Komponenten befanden sich nicht direkt im gesamten Code des
xz-Git-Repositories, sondern waren in den von Jia Tan gebauten, signierten und veröffentlichten Release-Tarballs vonxz5.6.0 und 5.6.1 enthalten - In einigen
.xz-Testdateien im Git-Repository war Maschinencode versteckt, und den Release-Tarballs wurden getarnte Änderungen hinzugefügt, um ihn herauszuholen - Insbesondere die Änderung an
m4/build-to-host.m4wirkte auf den ersten Blick harmlos und war sogar kommentiert, verbarg in Wirklichkeit aber eine Befehlskette zum Entschlüsseln und Entobfuskieren mehrerer gefälschter.xz-Testdateien - Das Ergebnis dieses Prozesses war zweierlei
- ein Shell-Skript, das während des
xz-Builds ausgeführt wird - eine schädliche binäre Objektdatei
- ein Shell-Skript, das während des
- Das während des Builds ausgeführte Shell-Skript übernahm zwei Aufgaben
- Es prüfte Ausführungsbedingungen der Hintertür wie bestimmte Linux-Distributionen,
glibc-Funktionen und das Vorhandensein einerssh-Installation - Es veränderte das legitime Objekt
liblzma_la-crc64_fast.oso, dass es das Symbol_get_cpuidaus der Hintertür-Objektdatei verwendet
- Es prüfte Ausführungsbedingungen der Hintertür wie bestimmte Linux-Distributionen,
- Russ Cox’
xz-Skriptanalyse beschreibt im Detail, wie die schädlichen Ressourcen während des Builds erzeugt werden
ifunc-Hijacking bis zu sshd
- Bei dynamisch gelinkten Programmen lädt der dynamische Loader zur Laufzeit Shared Libraries in den Speicher und trägt Symboladressen in die Global Offset Table (GOT) ein
- ifunc in
glibcist eine Funktion, mit der beim dynamischen Laden eine von mehreren Implementierungen derselben Funktion ausgewählt werden kann; diexz-Hintertür nutzte dies als Pfad zur Ausführung von Schadcode sshauf einigen Distributionen wie Debian und Fedora ist fürsystemd-Benachrichtigungsunterstützung mitlibsystemdgelinkt, undlibsystemdist wiederum mitliblzmagelinkt- In diesen Umgebungen hängt
sshdtransitiv vonliblzmaab
- In diesen Umgebungen hängt
- Beim Start von
sshdlädt der dynamische Loaderlibsystemdundliblzma; ist die Hintertür installiert, wird beim Laden vonliblzmaSchadcode ausgeführt - Die Hintertür sorgte dafür, dass der ifunc-Resolver das bösartige Symbol
_get_cpuidaufruft, und manipulierte die noch nicht schreibgeschützte GOT, um die Adresse vonRSA_public_decryptauf eine schädliche Funktion umzubiegen - Als Referenz können die Detailanalyse von Securelist sowie der Forschungsartikel zu Angriffsvektoren und Gegenmaßnahmen dienen
Vertrauenswürdige Quellen und die Bootstrap-Beschränkungen von NixOS
- Dieser Angriff konnte wirksam sein, weil viele Distributionen
xzaus maintainerbereitgestellten Tarballs statt aus den ursprünglichen Quellen der Git-Forge bauten - Der Tarball-Workflow hat historische und praktische Gründe
- Er stammt aus der Zeit früher Linux-Distributionen vor
git - Er bewahrt den Zustand des Quellcodes zum Release-Zeitpunkt in einem in sich geschlossenen Archiv
- Er kann Zwischenartefakte wie Manpages und
configure-Skripte enthalten und so den Build-Aufwand verringern - Komprimierung bringt Vorteile bei der Speichereffizienz
- Er stammt aus der Zeit früher Linux-Distributionen vor
- Aus Sicherheitssicht sollte man, wo technisch möglich, aus Quellen bauen, die von der vertrauenswürdigsten verfügbaren Instanz authentifiziert wurden
- Bei Projekten, die auf GitHub entwickelt werden, können die von GitHub zu jedem Release automatisch erzeugten Archive verwendet werden
- Dieselbe Logik gilt auch für vertrauenswürdige Drittplattformen wie Codeberg, SourceHut und GitLab
- NixOS verwendet ein funktionales Paketmanagement-Modell, bei dem Build-Rezepte für Pakete in Nix-Ausdrücken definiert werden
- In NixOS gibt es, wo möglich, eine Kultur der Nutzung automatisch von GitHub erzeugter Quellarchive via
fetchFromGitHub, doch dasxz-Paket holte perfetchurleinen manuell hochgeladenen Maintainer-Tarball - Der Grund ist, dass
xzTeil des Bootstrap-Schritts vonnixpkgsist- Bootstrap ist der Prozess, mit dem sich der gesamte Paketbestand von
nixpkgsausgehend von einer kleinen Menge Seed-Binaries neu bauen lässt stdenvist die grundlegende Compiler-Umgebung, die andere Pakete verwendenstdenvhängt zur Laufzeit vonxzab, und Pakete wiecoreutilsbenötigenxz, um Quellarchive im Format.tar.xzzu entpacken
- Bootstrap ist der Prozess, mit dem sich der gesamte Paketbestand von
- Um
xzaus GitHub-Quellen zu bauen, wirdautoconfbenötigt, um dasconfigure-Skript zu erzeugen, dochautoconfhängt selbst ebenfalls vonxzab- Im Maintainer-Tarball ist das
configure-Skript bereits enthalten und durchbricht so diesen Abhängigkeitszyklus - Daher ist es an der Stelle im
nixpkgs-Graphen, an derxzgebaut wird, schwierig, GitHub-Quellarchive zu verwenden, und man musste den Maintainer-Tarballs vertrauen
- Im Maintainer-Tarball ist das
Vergleich der Konvergenz von Artefakten statt Vergleich der Quellen
- Es wirkt zunächst naheliegend, den Maintainer-Tarball mit dem GitHub-Quell-Tarball auf Gleichheit zu vergleichen, in der Praxis passt dieser Ansatz aber nicht gut
- Daniel Stenberg erklärte, dass es ein Feature sei, wenn sich Release-Tarballs von den Quellen unterscheiden
- Zwischenartefakte wie Manpages und
configure-Skripte können im Tarball enthalten sein - Das ist besonders nützlich, wenn Distributionen
autoconf-Abhängigkeiten vermeiden wollen
- Zwischenartefakte wie Manpages und
- Aus Sicht der Supply-Chain-Sicherheit wird diese Flexibilität jedoch zu der Last, dem Maintainer auf ehrliches Handeln vertrauen zu müssen
- Reproduzierbare Builds sind die Eigenschaft, dass zwei Builds unter denselben Bedingungen bitgenau identische Artefakte erzeugen
- Die Gruppe reproducible-builds hat das Ziel, möglichst viele Pakete reproduzierbar zu machen, und Reproducible Builds: Increasing the Integrity of Software Supply Chains behandelt dies als Eigenschaft zur Erhöhung des Vertrauens in die Verteilung binärer Artefakte
- Einen Ansatz, auch die Erzeugung von Tarballs selbst reproduzierbar zu machen, hat das PostgreSQL-Projekt übernommen
- Nutzer können damit unabhängig prüfen, ob ein Maintainer-Tarball ehrlich aus den ursprünglichen Quellen erzeugt wurde
- Allerdings muss dies vom Maintainer des Upstream-Projekts umgesetzt werden, weshalb die Verbreitung in der FOSS-Community langsam sein kann
Vorgeschlagenes Erkennungsverfahren in NixOS
- Wenn
xzbitgenau reproduzierbar ist und der Maintainer-Tarball keine Änderungen enthält, die den Build-Prozess beeinflussen, dann sollten ein Build aus dem GitHub-Tarball und ein Build aus dem Maintainer-Tarball dieselben Artefakte erzeugen - Die vorgeschlagene Methode baut
xznach dem Bootstrap ein weiteres Mal, diesmal mit dem GitHub-Quellarchiv viafetchFromGitHub, und vergleicht das Ergebnis mit demxz-Artefakt aus dem bisherigen Bootstrap-Schritt - Das Implementierungsbeispiel legt unter
pkgs/tools/compression/xzeine Dateiafter-bootstrap.nixan und bautxz5.6.1 aus GitHub-Quellen- Der Maintainer-Tarball enthält das
configure-Skript bereits, beim Build aus GitHub-Quellen müssen dagegenautoconf,libtool,automakeund weitere Werkzeuge verwendet werden, um./autogen.shauszuführen
- Der Maintainer-Tarball enthält das
- Als Vergleichsschritt wird
compareArtifactshinzugefügt, das am Ende des Buildsliblzma.sovergleicht- Im Beispiel wurde nur
liblzma.soverglichen, weil genau diese Datei von der Hintertür verändert wurde, doch der Ansatz lässt sich auf alle binären und Bibliotheksartefakte verallgemeinern
- Im Beispiel wurde nur
- Auf einer sauberen
master-Revision war der Buildxz-after-bootstraperfolgreich, und beide Artefakte waren identisch - Bei der Revision
c53bbe3, die die Hintertür enthielt, sah es zunächst ebenfalls identisch aus- Unter NixOS wurde die Hintertür nicht aktiviert; ein Grund dafür war, dass das Installationsskript prüfte, ob die Variable
RPM_ARCHgesetzt ist - Nachdem mit
env.RPM_ARCH = true;die Bedingung aktiviert wurde, traten binäre Unterschiede inliblzma.sozutage
- Unter NixOS wurde die Hintertür nicht aktiviert; ein Grund dafür war, dass das Installationsskript prüfte, ob die Variable
- Nach dem Speichern und Vergleichen der beiden
liblzma.so-Dateien war das Artefakt mit Hintertür 258 KB groß, das nach dem Bootstrap aus GitHub-Quellen gebaute Artefakt 210 KB, also eine Differenz von rund 48 KB - Ein Symbolvergleich mit
nmzeigte die zusätzlichen Symbole_cpuid,_get_cpuidund__tls_get_addr@GLIBC_2.3;_get_cpuidist ein in mehreren technischen Berichten zurxz-Hintertür dokumentiertes Symbol
Einsatzmöglichkeiten und Grenzen
- Die vorgeschlagene Schutzmaßnahme würde auf alle Pakete angewendet, die im Bootstrap-Schritt gebaut werden und keine vertrauenswürdigen Quellarchive verwenden
- Fügt man
*-after-bootstrap-Pakete als Channel-Blocker hinzu, kann ein Build-Fehler zu einem starken Alarm werden, der Eingreifen von Maintainern erfordert - Als Proof of Concept für den
xz-Fall wurde imnixpkgs-Repository ein Pull Request eröffnet - Diese Methode setzt voraus, dass das Zielpaket bitgenau reproduzierbar ist
- Von 2017 bis 2023 wurden 17
nixpkgs-unstable-Revisionen als Stichprobe genommen und die non-fixed-output derivations vonstdenvmitnix-build --checkneu gebaut - In allen Revisionen war
xzbitgenau reproduzierbar - In 12 der 17 Revisionen gab es 1 bis 2 Pakete, die zwar baubar, aber nicht reproduzierbar waren; zum Beispiel war
gccvon 2017 bis 2021 undbashbis 2019 durchgehend nicht reproduzierbar
- Von 2017 bis 2023 wurden 17
- Der Ansatz lässt sich nicht auf alle
stdenv-Pakete anwenden, langfristig könnte er aber selektiv für Pakete mit guter Reproduzierbarkeit aktiviert werden - Ken Thompsons Problem des trusting trust bleibt eine theoretische Grenze dieses Verfahrens
- Es muss angenommen werden, dass ein nicht vertrauenswürdiges
xzim Bootstrap-Schritt den Build vonxz-after-bootstrapnicht indirekt so kontaminieren kann, dass die Artefakte identisch erscheinen - Ein solcher Angriff wäre zwar sehr komplex, doch die Methode setzt diese Annahme voraus
- Es muss angenommen werden, dass ein nicht vertrauenswürdiges
- Dieses Erkennungsverfahren schlägt nur dann an, wenn Änderungen am Tarball das endgültige Artefakt beeinflussen
- Da die Hintertür in NixOS-Binärdateien nicht enthalten war, hätte man sie in diesem Fall ohne separate Aktivierung der Bedingung nicht entdeckt
1 Kommentare
Hacker-News-Kommentare
Es sollte festgehalten werden, dass NixOS und reproduzierbare Builds die xz-Backdoor nicht erkannt haben. Tatsächlich hat auch NixOS den bösartigen xz-Build an Nutzer ausgeliefert; er funktionierte nur nicht, weil die Malware nicht auf NixOS abzielte.
Auch ein NixOS-Entwickler sagte: „Als die Backdoor öffentlich wurde, war ich überrascht, dass die bösartige Version von xz an unsere Nutzer ausgeliefert worden war.“ Wie immer sind Theorie und Praxis verschieden, und was xz möglich gemacht hat, war weniger eine technische Schwachstelle als vielmehr eine menschliche Schwachstelle in der realen Welt. Die Community tut sich schwer damit anzuerkennen, dass sich solche Probleme nicht immer allein durch bessere Software patchen lassen.
Wenn man jeden Prozess mit eingeschränkten Rechten ausführen und ihm, abgesehen von den für die Aufgabe nötigen Verzeichnissen, sogar den Zugriff auf
~/verwehren könnte, ließe sich zum Beispiel verhindern, dass ein bösartigespip-Paket SSH-Schlüssel stiehlt. Trotzdem denke ich, dass der Grund, warum die xz-Backdoor unter NixOS nicht funktionierte, die ungewöhnliche Nicht-FHS-Dateisystemstruktur von NixOS war.Heutzutage wird die tatsächliche Realität oft abgewertet, wenn sie nicht zu den persönlichen Gefühlen oder Wertvorstellungen über die objektive Realität passt. Ich habe zwar persönliche Wertvorstellungen, sehe die tatsächliche Realität aber nicht als weniger wichtig an als meine Gefühle dazu. Wie es in einem beliebten Spruch heißt: Der Unterschied zwischen Theorie und Praxis besteht darin, dass beide in der Theorie gleich sind, in der Praxis aber nicht. Ich hoffe, dass die Erkenntnis, dass die reproduzierbaren Builds von NixOS den xz-Angriff hätten finden können, es aber nicht taten, zu Fortschritten führt, bei denen solche Builds künftig analysiert werden, um andere Angriffe schneller zu entdecken.
Der Autor scheint zu sehr auf die Art und Weise fixiert zu sein, wie es diesmal zufällig passiert ist. Der Jia-Tan-Vorfall ist nur ein einzelnes Beispiel; zu glauben, nur diese Methode sei möglich, ist kurzsichtig.
Man kann sich genügend Szenarien vorstellen, in denen die hier vorgeschlagene Verteidigung nicht greifen würde. Auch als Nix-Nutzer halte ich es für unwahrscheinlich, dass NixOS das erkannt hätte. Tatsächlich hat es das ja nicht. Zwar habe ich gerade gesagt, dass es beim nächsten Mal anders passieren könnte, aber Nix ohne Belege zu vertrauen, wäre ebenfalls töricht.
NixOS ist hier nicht besonders relevant. Die xz-Backdoor zielte konkret auf Red Hat und Debian ab.
Nach derselben Logik wäre es ähnlich relevant zu sagen, dass die xz-Backdoor Windows nicht betroffen hat. Ironischerweise wurde diese Backdoor am Ende von einem Microsoft-Mitarbeiter entdeckt, was oft übersehen wird.
Im Artikel heißt es, Distributionen sollten den Quellcode statt aus traditionellen Installations-Tarballs direkt aus einem Versionskontrollsystem, zum Beispiel Github, beziehen.
Ich verstehe aber nicht, was das lösen soll. Ein böswilliger Maintainer könnte doch einfach einen binären Blob direkt ins Quellcode-Repository aufnehmen, oder? Der Autor scheint Github so zu vertrauen, als würde es Code verifizieren, aber natürlich macht Github keine solche Prüfung.
Verifizierte reproduzierbare Builds hätten helfen können, Fälle wie die Kompromittierung von xz utils oder die Manipulation von SolarWinds Orion zu verhindern, und das ist den Aufwand durchaus wert.
Wenn man von einem Github-Release eine Binärdatei herunterlädt und ausführt, bleibt einem nichts anderes übrig, als dem Maintainer vollständig zu vertrauen. Nix macht das nur bei Closed-Source-Paketen so.
Die Argumentation stützt sich darauf, dass der XZ-Maintainer bösartigen Code in einem Tarball versteckt hat, der nicht in Git eingecheckt war.
Der Autor zeigt, dass Nix so konfiguriert werden kann, dass es aus Git einen Tarball erzeugt und diesen in den Binär-Build einfließen lässt. Aber ich sehe nicht, warum das eine Funktion sein soll, für die man Nix oder NixOS braucht. Jedes Build-System, auch solche für RPMs oder Debs, kann so konfiguriert werden, dass es als Zwischenschritt einen Tarball erzeugt. Tatsächlich arbeitet Debian schon lange mit reproduzierbaren Builds als wichtigem Ziel. https://wiki.debian.org/ReproducibleBuilds
Erstens konnte der NixOS-Build-Prozess keinen vollständigen Source-Build von xz durchführen, weil xz sehr früh im Bootstrap zu früh benötigt wurde. Zweitens schlägt er Anpassungen an nixpkgs vor, um automatisch zu erkennen, ob für die frühe Bootstrap-Phase nötige nixpkgs-Abhängigkeiten kompromittiert wurden. Andere Ökosysteme können natürlich ebenfalls vollständige Source-Builds versuchen und Abweichungen finden. Der zentrale Punkt des Artikels ist, dass nixpkgs das derzeit nicht kann.
Wenn man sich auf einen Fall konzentrieren will, den NixOS hätte verhindern können, sollte man sich den CrowdStrike-Vorfall ansehen. Allein die Möglichkeit, mit der gestrigen Konfiguration zu booten, wenn die heutige nicht funktioniert, hätte den größten Teil des Schadens abgemildert.
Wer ein vertrauenswürdiges Framework nutzt, ist sicher, bis dieses Framework selbst angegriffen wird. Die xz-Backdoor wurde zwar entdeckt, war aber nicht mit dem Ziel entworfen worden, im Nix-Ökosystem zu funktionieren.
Irgendwann, wenn ein Nix-Core-Entwickler ein Spion ist oder eine ähnliche Situation entsteht, wird es auch Angriffe geben, die auf das Nix-Ökosystem abzielen. Ich hoffe, niemand antwortet so, als sei Nix inhärent sicher. Wenn Nix in ein bis zwei Jahren erfolgreich angegriffen wird, könnte ich fast versucht sein, wiederzukommen und die Leute eingestehen zu lassen, dass sie falschlagen.
Die stärkere Behauptung lautet: „Nix ist schwerer und teurer anzugreifen als traditionelle Build-Systeme.“ Wenn du also einen billigen Weg findest, Nix anzugreifen, komm dann wieder. Bis dahin ist die Aussage, dass Nix auf technischer Ebene schwerer anzugreifen ist als alternative Systeme, zumindest plausibel und tatsächlich sehr wahrscheinlich.
NixOS geht zwar ein Stück weiter, aber die meisten anderen Distributionen kompilieren ebenfalls alles aus dem Quellcode, verifizieren kryptografisch, dass die verwendeten Quellen nicht manipuliert wurden, und haben versionsgebundene Abhängigkeiten zwischen Paketen. Auch Debian hat reproduzierbare Builds.
Das Problem war, dass das Build-System vorkompilierte Objektdateien nicht entfernt hat, bevor es aus dem Quellcode gebaut hat. Selbst wenn man das behebt, kann man beliebig viele Backdoors einbauen, wenn niemand den Quellcode prüft; weder NixOS noch irgendeine andere Distribution kann das verhindern.
Eine hervorragende technische Analyse, aber der Titel ist falsch und irreführend. Auch wenn man sagen könnte, er sei „technisch korrekt“, trifft es im besten Fall eher den Sinn einer eingeschleusten Backdoor.
Das zeigt gut, dass wir Build-Manager-Tools brauchen, die über Union-Dateisystem-Schichten hinausgehen. Zum Beispiel müssen sie nachverfolgen und erzwingen, dass Tests die Build-Artefakte nicht kontaminieren können. Nötig wäre ein kausaler Tracking-Graph, der zeigt, wie Dateien im Build-Prozess andere Dateien beeinflussen; dieser Graph sollte explizit erstellt und dann erzwungen werden oder Unterschiede zu früheren Tracking-Graphen melden.
Stimmt. Es wäre definitiv schwieriger geworden, die Backdoor zu verstecken. Aber unmöglich ist es bei Weitem nicht.
Wenn man will, kann man jederzeit eine Backdoor im Quellcode verstecken. Es erfordert nur mehr Aufwand, sie wie einen plausiblen Bug aussehen zu lassen, und die Wahrscheinlichkeit, entdeckt zu werden, steigt.