Der XZ-Backdoor-Vorfall: Erste Analyseergebnisse
(securelist.com)- Die Backdoor in XZ Utils/liblzma wurde am 29. März 2024 auf der Openwall-OSS-security-Mailingliste veröffentlicht. Das wahrscheinliche Endziel der Angreifer war es, dem OpenSSH-Server sshd auf systemd-basierten Distributionen die Fähigkeit zur Remote Code Execution einzupflanzen.
- Die Infektionskette bestand aus einer mehrstufigen Einschleusung, die Testdateien und Build-Infrastruktur des XZ-Repositories nutzte. Über
build-to-host.m4,bad-3-corrupt_lzma2.xzundgood-large_compressed.lzmawurde während des Builds eine bösartige Objektdatei inliblzmagelinkt. - Die mit der Backdoor versehenen XZ 5.6.0 und 5.6.1 wurden in Beta- und experimentellen Builds einiger großer Anbieter ausgeliefert, und CVE-2024-3094 wurde mit dem Schweregrad 10 eingestuft.
- Die binäre Backdoor wird durch Missbrauch von GLIBCs IFUNC und des
cpuid-Aufrufpfads geladen und versucht danach, mit OpenSSL/libcrypto verbundene Funktionen zu hooken, um Verbindungen auf infizierten Maschinen zu überwachen. - Der Schadcode prüft, ob
/usr/bin/sshdläuft, sowie eine Kill-Switch-Umgebungsvariable, und erschwert Analyse und Erkennung durch trie-basierte String-Verarbeitung, dynamische Symbolauflösung und Runtime-Patching vonrtdl-audit.
Überblick über den Vorfall und den Wirkungsbereich
- Am 29. März 2024 wurde über eine Nachricht auf der Openwall-OSS-security-Mailingliste die Entdeckung der XZ-Backdoor öffentlich gemacht.
- XZ ist ein Komprimierungswerkzeug, das in viele große Linux-Distributionen integriert ist.
- Der Kern des Risikos besteht darin, dass das kompromittierte
liblzmaauf einigen systemd-basierten Distributionen mit dem OpenSSH-Serverprozesssshdverbunden werden konnte.- Ubuntu, Debian und RedHat/Fedora Linux patchen OpenSSH so, dass systemd-Funktionen genutzt werden und dadurch eine Abhängigkeit von dieser Bibliothek entsteht.
- Arch Linux und Gentoo gelten als nicht betroffen.
- Das wahrscheinliche Endziel der Angreifer war es, eine Remote-Code-Execution-Funktion in sshd einzubauen, die andere nicht nutzen konnten.
- Anders als bei anderen Supply-Chain-Angriffen, die sich auf einen einzelnen bösartigen Patch, ein gefälschtes Paket oder ein Typosquatting-Paket konzentrieren, kommt dieser Vorfall einer mehrstufigen Operation gleich, die beinahe weltweit SSH-Server kompromittiert hätte.
Wie die Backdoor eingeschleust wurde
- Die
liblzma-Backdoor wurde auf zwei Ebenen eingeschleust, indem sowohl der Build-Prozess als auch Testdateien genutzt wurden.- Der Quellcode der Build-Infrastruktur, der das Endpaket erzeugt, wurde durch das Hinzufügen von
build-to-host.m4verändert. - In Testfalldateien verborgene Skripte und Binärkomponenten wurden während des Builds extrahiert.
- Der Quellcode der Build-Infrastruktur, der das Endpaket erzeugt, wurde durch das Hinzufügen von
- Der Infektionsablauf drehte sich um drei Dateien.
build-to-host.m4: ein Build-Skript, das das Skript der nächsten Stufe extrahiertbad-3-corrupt_lzma2.xz: eine Testdatei mit verstecktem Shell-Skriptgood-large_compressed.lzma: eine Testdatei mit verstecktem bösartigem Binärobjekt
- Die extrahierte bösartige Binärkomponente wurde während der Kompilierung mit der legitimen Bibliothek gelinkt und konnte so in Linux-Repositories gelangen.
- Große Anbieter verteilten diese bösartige Komponente in Beta- und experimentellen Builds.
- Der Kompromittierung von XZ Utils wurde CVE-2024-3094 zugewiesen, mit dem maximalen Schweregrad 10.
Wichtige Zeitleiste
-
- Januar 2024: Der neue Maintainer
jiaT75verlegt die XZ-Website auf GitHub Pages.
- Januar 2024: Der neue Maintainer
-
- Februar 2024:
build-to-host.m4wird zu.gitignorehinzugefügt.
- Februar 2024:
-
- Februar 2024: Zwei „Testdateien“ mit Stufen des bösartigen Skripts werden hinzugefügt.
-
- Februar 2024: XZ 5.6.0 wird veröffentlicht.
-
- Februar 2024: Ein Commit in
CMakeLists.txtwird eingefügt, der die Landlock-Sicherheitsfunktion behindert.
- Februar 2024: Ein Commit in
-
- März 2024: Die Backdoor verursacht Probleme mit Valgrind.
-
- März 2024: Die beiden Testdateien werden aktualisiert, die CRC-Funktion wird geändert und das Valgrind-Problem wird „behoben“.
-
- März 2024: XZ 5.6.1 wird veröffentlicht.
-
- März 2024: Der Bug wird entdeckt und Debian sowie RedHat werden informiert.
-
- März 2024: Debian setzt XZ 5.6.1 auf Version 5.4.5-0.2 zurück.
-
- März 2024: Die öffentliche Mail wird auf der OSS-security-Mailingliste veröffentlicht.
-
- März 2024: RedHat bestätigt, dass XZ mit Backdoor in Fedora Rawhide und Fedora Linux 40 beta ausgeliefert wurde.
-
- März 2024: Debian stoppt Builds und beginnt mit dem Rebuild-Verfahren.
-
- April 2024: Der Hauptentwickler von XZ erkennt den Backdoor-Vorfall an.
Die 3 Stufen der Infektionskette
-
Stufe 1: Modifiziertes
build-to-host.m4- Die Release-Versionen im offiziellen GitHub-Repository von XZ enthielten zwar die bösartigen Testdateien, waren aber zunächst harmlos, weil diese Dateien keine Gelegenheit zur Ausführung hatten.
- Der Schadcode, der die Infektion bootstrappt, scheint in den Releases enthalten gewesen zu sein, die von
jiaT75kontrolliert aufxz[.]tukaani.orgbereitgestellt wurden. - Die meisten Distributionen nutzten diese URL, und die heruntergeladenen Releases enthielten die Datei
build-to-host.m4mit dem Schadcode. build-to-host.m4wird während des Build-Prozesses ausgeführt, repariert die „beschädigten“ Daten inbad-3-corrupt_lzma2.xzmittrund dekomprimiert sie dann mitxz -d.- Das Ergebnis der Dekomprimierung enthält ein Shell-Skript, das später mit
/bin/bashausgeführt wird.
-
Stufe 2: Eingeschleustes Shell-Skript
- Das durch die bösartige
.m4-Datei eingeschleuste Skript prüft, ob es auf einer Linux-Maschine und innerhalb des beabsichtigten Build-Prozesses läuft. - Für die Ausführung der nächsten Stufe wird
good-large_compressed.lzmaverwendet. - Diese Datei ist regulär mit XZ komprimiert, enthält aber Junk-Daten in den dekomprimierten Daten.
- Das Skript extrahiert die Daten, indem es eine
head-Pipeline mitevalausführt. - Jeder
head-Befehl verwirft die nächsten 1024 Byte oder extrahiert die nächsten 2048 Byte beziehungsweise 724 Byte. - Insgesamt werden 33.492 Byte extrahiert.
tailbehält die letzten 31.265 Byte der Datei und verwirft den Rest.- Danach wird mit
treine einfache Ersetzung angewandt, um die Obfuskation aufzuheben, und ein zweiter XZ-Befehl dekomprimiert die transformierten Bytes als rohenlzma-Stream, bevor das Ergebnis an die Shell übergeben wird.
- Das durch die bösartige
-
Stufe 3: Extraktion des Backdoor-Objekts
- Das Shell-Skript der letzten Stufe prüft anhand mehrerer Bedingungen, ob es in der erwarteten Umgebung läuft.
- Es prüft, ob das Projekt so konfiguriert ist, dass IFUNC verwendet wird.
- Es prüft, ob mit GCC kompiliert wird.
- Danach prüft es, ob bestimmte Dateien, die das Skript benötigt, im Projekt vorhanden sind.
- Diese Stufe extrahiert eine Objektdatei, die an einem anderen Offset in
good-large_compressed.lzmaversteckt ist. - Das Extraktionsverfahren führt mehrere Befehle nacheinander aus und übergibt die Ausgabe jedes Befehls als Eingabe an den nächsten.
- Das XZ-Werkzeug extrahiert
good-large_compressed.lzma. - Verwendet werden
eval $iund mehrerehead-Aufrufe. - Mit einem benutzerdefinierten, RC4-ähnlichen Algorithmus werden die Binärdaten entschlüsselt.
- Die entschlüsselte komprimierte Datei wird erneut mit XZ extrahiert.
- Nach dem Entfernen einiger Bytes am Anfang wird das Ergebnis als
liblzma_la-crc64-fast.ogespeichert. - Abschließend verändert das Skript in der Datei
crc_x86_clmul.hvonliblzmadie Funktionis_arch_extension_supported. - Dabei wird der Aufruf
__get_cpuidzu_get_cpuidgeändert, also ein Unterstrich entfernt. - Durch diese Änderung kann das Backdoor-Objekt in die Bibliothek gelinkt werden.
Wie die binäre Backdoor geladen wird
- Im ursprünglichen XZ-Code gibt es die speziellen Funktionen
lzma_crc32undlzma_crc64zur CRC-Berechnung. - Beide Funktionen sind in der ELF-Symboltabelle als GLIBC-Typ IFUNC gespeichert.
- IFUNC erlaubt dem dynamischen Linker, beim Laden einer Shared Library die zu verwendende Funktion dynamisch auszuwählen.
- XZ nutzt dies, um nach Prüfung moderner Prozessorfunktionen wie CLMUL, SSSE3 und SSE4.1 zu entscheiden, ob optimierte CRC-Funktionen verwendet werden sollen.
- Für die Prüfung der Prozessorfunktionen ist der
cpuid-Befehl nötig, und dazu wird der von GLIBC bereitgestellte Wrapper__get_cpuidaufgerufen. - Indem das bösartige Skript
__get_cpuidzu_get_cpuidändert, wird der Codeaufruf mit dem_get_cpuid-Symbol in der Backdoor-Objektdatei verbunden. - Der anfängliche Backdoor-Code wird zweimal aufgerufen, weil
lzma_crc32undlzma_crc64dieselbe modifizierte Funktion_get_cpuidverwenden.- Ein einfacher Zähler prüft, ob die Ausführung bereits erfolgt ist.
- Wenn die IFUNC von
lzma_crc64_get_cpuidaufruft und der Zählerwert 1 ist, beginnt die Umleitung zum endgültigen Einstiegspunkt.
Laufzeitverhalten und Hooking-Ziele
- Die Backdoor initialisiert Strukturen, die zentrale Informationen über den laufenden Prozess enthalten.
- Über hartcodierte Offsets findet sie die Adresse der Global Offset Table(GOT) und darin den
cpuid-Pointer. - Anschließend ersetzt sie den Pointer durch die Haupt-Schadfunktion und ruft die bösartige Funktion so auf, als würde
cpuidaufgerufen. - Das Hauptziel besteht darin, bestimmte Funktionen zu hooken, um eingehende Verbindungen auf der infizierten Maschine überwachen zu können.
- Die Zielfunktionen stehen mit
libcryptoin Verbindung.RSA_public_decrypt: wird verwendet, um mit einem privaten Schlüssel signierte Chiffretexte zu entschlüsselnEVP_PKEY_set1_RSA: wird verwendet, um einen RSA-Schlüssel in einem bestimmten Kontext zu setzen- In neueren SSH-Server-Versionen kommt die Funktion nicht vor, wohl aber in eigenständigen Werkzeugen zur SSH-Schlüsselerzeugung.
RSA_get0_key: wird verwendet, um einen RSA-Schlüssel abzurufen
- Der Hooking-Prozess erfordert Zugriff auf mehrere interne Strukturen und Manipulation prozessspezifischer Strukturen, wobei der Schadcode zugleich Prüfungen zur Analysevermeidung durchführt.
Prüfungen der Laufzeitumgebung und Techniken zur Analysevermeidung
-
Prüfung von
sshdund Kill Switch- Die Backdoor parst zunächst den dynamischen Linker
ld-linux, um Symboladressen dynamisch aufzulösen. - Sie prüft, ob die aktuelle Laufzeitumgebung die Kriterien erfüllt.
- Sie prüft, ob der aktuelle Prozess
/usr/bin/sshdist. - Sie prüft, ob ein Kill Switch vorhanden ist.
- Der Name des aktuellen Prozesses wird aus
argv[0]extrahiert. - Alle Umgebungsvariablen des Prozesses werden geparst und mit Werten in einer internen String-Tabelle verglichen.
- Gibt es eine passende Umgebungsvariable oder ist der Prozess nicht
sshd, beendet der Schadcode seine Ausführung. - Die einzige Variable in der internen Tabelle,
yolAbejyiejuvnup=Evjtgvsh5okmkAvj, fungiert in diesem Kontext als Kill Switch.
- Die Backdoor parst zunächst den dynamischen Linker
-
Trie-basierte String-Verarbeitung
- Ein charakteristisches Merkmal der Backdoor ist die Verwendung einer einzigen Trie-Struktur für String-Operationen.
- Statt Konstanten wie Funktionsnamen direkt oder per String-Hash zu vergleichen, prüft sie, ob das Ergebnis einer Trie-Abfrage einem bestimmten Konstantwert entspricht.
- Der Magic-Wert eines ELF-Headers liefert im Trie 0x300 zurück.
- Der Funktionsname
systementspricht dem Rückgabewert 0x9F8. - Das Trie dient nicht nur Vergleichen, sondern auch dazu, Strings im Host-Binary zu finden.
- Beispielsweise suchen Funktionen, die String-Pointer wie
ssh-2.0verwenden, den String im Host-Binary, damit im eigentlichen Backdoor-Code keine verdächtigen Daten zurückbleiben. - Die Implementierung verwendet 16-Byte-Bitmasken.
- Jede Hälfte entspricht den Eingabebytebereichen
0x00-0x3Fbeziehungsweise0x40-0x7F. - Trie-Blattknoten sind 2 Byte groß; 3 Bit dienen als Flags, der Rest ist für Werte oder die Position des nächsten Knotens reserviert.
Symbolauflösung und rtdl-audit-Hooking
-
Routinen zur Symbolauflösung
- Die Backdoor enthält mindestens drei Routinen zur Symbolauflösung, um ELF-Symbolstrukturen zu finden.
- Alle Funktionen zur Symbolauflösung nehmen als Eingabe einen im Trie nachzuschlagenden Schlüssel entgegen.
- Eine Auflösungsfunktion iteriert durch alle Symbole, sucht nach dem Symbol mit dem gewünschten Schlüssel und gibt bei Erfolg die
Elf64_Sym-Struktur zurück. - Diese Struktur wird anschließend verwendet, um interne Strukturen der Backdoor zu füllen und benötigte Funktionspointer zu speichern.
- Das ähnelt den aus Windows-Bedrohungen bekannten API-Hashing-Routinen.
- Die Backdoor sucht danach zahlreiche OpenSSL-
libcrypto-Funktionen, die später in Kryptoroutinen verwendet werden. - Sie verfolgt, wie viele Funktionen gefunden und aufgelöst wurden, und entscheidet anhand dieses Werts, ob die Ausführung normal fortgesetzt oder abgebrochen wird.
-
Missbrauch von
lzma_allocdurch einen Fake-Allocator- Ein weiterer Symbolauflöser missbraucht die
liblzma-eigene Funktionlzma_alloc. lzma_allochilft dabei, Speicher effizient zuzuweisen, indem entweder der Standard-Allocatormallocoder ein benutzerdefinierter Allocator verwendet wird.- In der XZ-Backdoor fungiert diese Funktion über einen Fake-Allocator wie ein weiterer Symbolauflöser.
- Der Parameter, der eigentlich als „Allokationsgröße“ dient, wird tatsächlich als Symbolschlüssel innerhalb des Tries verwendet.
- Dieser Trick soll die Analyse der Backdoor erschweren.
- Die Backdoor löst Symbole zur Laufzeit dynamisch auf und ist nicht darauf beschränkt, entweder alle Symbole auf einmal oder nur bei Bedarf aufzulösen.
- Zu den aufgelösten Zielen gehören normale OpenSSL-Funktionen ebenso wie Funktionen wie
system, mit denen auf der Maschine Befehle ausgeführt werden können.
- Ein weiterer Symbolauflöser missbraucht die
-
Runtime-Patching von
rtdl-audit- Für das Hooking von Funktionen nutzt die Backdoor die rtdl-audit-Funktion des dynamischen Linkers.
- Normalerweise erlaubt
rtdl-auditEntwicklern, eine benutzerdefinierte Shared Library zu erstellen, um über Linker-Ereignisse wie Symbolauflösung informiert zu werden. - Die XZ-Backdoor erstellt jedoch keine separate Shared Library, sondern patcht zur Laufzeit die bereits im Speicher registrierte Standard-Schnittstelle, um die Symbolauflösungsroutine zu kapern.
- Die bösartige
audit_iface-Struktur, die in der globalen Variablendl_auditim Speicherbereich des dynamischen Linkers abgelegt wird, enthält die Adresse dessymbind64-Callbacks. - Wenn der dynamische Linker dieses Callback aufruft, gerät die gesamte Symbolinformation unter die Kontrolle der Backdoor, und für Zielfunktionen werden bösartige Adressen zurückgegeben, wodurch das Hooking erreicht wird.
- Die Adressen von
dl_auditunddl_nauditwerden durch Disassemblierung der Funktionendl_mainunddl_audit_symbind_altgewonnen. - Die Backdoor enthält einen internen minimalistischen Disassembler zur Befehlsdekodierung, der breit eingesetzt wird, wenn bestimmte Werte wie
*audit-Adressen gefunden werden sollen.
Bestätigte Backdoor-Distributionen und Erkennungsinformationen
-
Quell-Distributionen mit Backdoor
- xz-5.6.0
- MD5:
c518d573a716b2b2bc2413e6c9b5dbde - SHA1:
e7bbec6f99b6b06c46420d4b6e5b6daa86948d3b - SHA256:
0f5c81f14171b74fcc9777d302304d964e63ffc2d7b634ef023a7249d9b5d875
- MD5:
- xz-5.6.1
- MD5:
5aeddab53ee2cbd694f901a080f84bf1 - SHA1:
675fd58f48dba5eceaf8bfc259d0ea1aab7ad0a7 - SHA256:
2398f4a8e53345325f44bdd9f0cc7401bd9025d736c6d43b372f4dea77bf75b8
- MD5:
- xz-5.6.0
-
Analysierte Hauptartefakte
bad-3-corrupt_lzma2.xz:86fc2c94f8fa3938e3261d0b9eb4836be289f8aebuild-to-host.m4:b4dd2661a7c69e85f19216a6dbbb1664good-large_compressed.lzma:540c665dfcd4e5cfba5b72b4787fec4fliblzma_la-crc64-fast.o:212ffa0b24bb7d749532425a46764433
-
Bekannte Bibliotheken mit Backdoor
- Debian Sid
liblzma.so.5.6.0- MD5:
4f0cf1d2a2d44b75079b3ea5ed28fe54 - SHA1:
72e8163734d586b6360b24167a3aff2a3c961efb - SHA256:
319feb5a9cddd81955d915b5632b4a5f8f9080281fb46e2f6d69d53f693c23ae
- MD5:
- Debian Sid
liblzma.so.5.6.1- MD5:
53d82bb511b71a5d4794cf2d8a2072c1 - SHA1:
8a75968834fc11ba774d7bbdc566d272ff45476c - SHA256:
605861f833fc181c7cdcabd5577ddb8989bea332648a8f498b4eef89b8f85ad4
- MD5:
- Debian Sid
-
Erkennungsnamen
- Kaspersky-Produkte erkennen mit dem Angriff verbundene bösartige Objekte als HEUR:Trojan.Script.XZ und Trojan.Shell.XZ.
- Kaspersky Endpoint Security for Linux erkennt den Schadcode im Speicher des SSHD-Prozesses im Rahmen des Critical Areas Scan als MEM:Trojan.Linux.XZ.
- Die bereitgestellte Yara-Regel ist die Regel
liblzma_get_cpuid_function, die die bösartigeget_cpuid-Funktion im Zusammenhang mit CVE-2024-3094 finden soll.
1 Kommentare
Meinungen auf Hacker News
Dieser Satz scheint das, was tatsächlich passiert ist, eher herunterzuspielen.
Erschreckender als die technischen Aspekte der Backdoor ist das Ausmaß und Niveau des Social Engineerings. Die Backdoor war das Endprodukt; möglich wurde sie, weil das gesamte xz-Projekt zu diesem Zeitpunkt bereits über längere Zeit von böswilligen Akteuren, also „Jia Tan“ und seinem Umfeld, vereinnahmt worden war. Gegen den Maintainer wurde über mehr als ein Jahr psychologische Kriegsführung betrieben, ohne dass der Maintainer oder sonst jemand es bemerkte.
Das klingt wie aus einem Spionageroman, und wenn so etwas möglich ist, fragt man sich, was gerade in anderen Projekten vor sich geht.
Auch im Backdoor-Code selbst zeigt sich dieselbe Denkweise. Es ging nicht nur darum, harmlos zu wirken, sondern über Commit-Messages, Kommentare, Variablennamen, die Wahl von Befehlen usw. aktiv eine Erzählung darüber aufzubauen, was der Code oberflächlich betrachtet tut, während er in Wirklichkeit etwas völlig anderes macht. Die Struktur bringt jemanden, der den Code zuerst anschaut, dazu, zunächst am eigenen Verständnis zu zweifeln, dann einen Bug zu vermuten und erst viel später Böswilligkeit in Betracht zu ziehen.
Ich hoffe, dass irgendwelche Nachrichtendienste diesen Fall gründlicher untersuchen.
Es ist frustrierend zu sehen, wie in jedem HN-Thread zu Backdoors diese Möglichkeit als Paranoia oder Aluhut-Denken abgetan und bestritten wird. Man tut so, als passiere so etwas nicht, dabei ist dies nur ein konkreter Fall, der erwischt wurde, und es gibt unzählige, die noch nicht entdeckt wurden.
In diesem Fall war die Entdeckung noch vergleichsweise einfach, weil es ein Open-Source-Projekt war, und trotzdem war es Glück. Wenn man nun an Closed-Source-Produkte denkt, reduziert sich das Einbringen einer Backdoor darauf, eine Organisation zu infiltrieren oder unter Druck zu setzen. So etwas passiert häufig. Niemand will es glauben, aber es ist üblich. Wer in einem Unternehmen für technische Infrastruktur gearbeitet hat, dürfte ein paar Geschichten dazu kennen. Wegen NDAs oder noch schlimmeren Gründen ist es schwer, darüber zu sprechen, aber es passiert tatsächlich.
Es könnte das Ergebnis der Besessenheit einer einzelnen Person sein, oder die Arbeit einer privaten Sicherheitsfirma oder eines staatlichen Akteurs, der solche Dinge von neun bis fünf für mehrere Projekte betreibt.
Es ist verständlich, dass sich die Aufmerksamkeit bisher darauf konzentriert hat, ob die Backdoor funktioniert und wie sie ihr Ziel erreicht.
Trotzdem würde ich gern eine tiefere Analyse der Fehler und der überkonstruierten Teile sehen. In Bryan Cantrils Interview [1] sagt Andrés, das wirke wie ein Backdoor-Bauteil von der Stange, das nicht unbedingt mit Kenntnis des konkreten Distributionswegs gebaut wurde, und deshalb gebe es viele dumme Stellen. Ein Beispiel war die Symboltabellenabfrage, die ihn zur Untersuchung brachte.
Ebenso frage ich mich, warum mit RC4 48 Bytes herausgeschnitten wurden [2].
Ich würde gern hören, wie es mit mehr Zeit oder einem besseren Team besser hätte gebaut werden können – oder wo die Täter noch größere Fehler gemacht haben.
[1] https://youtu.be/jg5F9UupL6I?si=gvXsYFXgagkGOMd4
[2] https://twitter.com/matthew_d_green/status/17744729080201014...
Wenn ich es richtig verstanden habe, wäre eine sinnvolle Härtungsmaßnahme, jeder dynamisch gelinkten Bibliothek ihre eigene GOT zu geben und die Tabelle nach Abschluss des dynamischen Linkens als read-only zu markieren. Mit anderen Worten: Man könnte dann nicht über dynamische Grenzen hinweg ifunc-Einträge auf der anderen Seite patchen.
Das könnte die Supply-Chain-Sicherheit von Code verbessern, der irgendwo gelinkt, aber nicht ausgeführt wird.
Darüber hinaus wäre es vielleicht besser, ifunc deklarativ zu implementieren, damit beim Linken nicht in jeder Bibliothek beliebiger Code ausgeführt werden kann. Wegen Abwärtskompatibilität dürfte das heute schwer umzusetzen sein, aber langfristig scheint eine schrittweise, hierarchische Einführung möglich. Zum Beispiel könnte man eine Bibliothek mit einem Feature-Bit „deklaratives Link-ifunc“ bauen, und der dynamische Linker würde die Ausführung verweigern, wenn nicht alle verknüpften Bibliotheken dasselbe Feature-Flag haben.
Derzeit laufen bei den meisten Library-Builds extrem komplexe und undurchsichtige Skripte, die eine Turing-vollständige Umgebung verlangen. Das gibt Angreifern eine endlose Angriffsfläche und schafft Gelegenheiten, wenn der Build-Prozess kompromittiert wird.
Ein Wechsel zu einem deklarativen Build-Prozess, bei dem der Ausführer nur eine eingeschränkte Zustandsmaschine ist, würde helfen. Auch die Anforderung, dass jeder Quellcode-Brocken reproduzierbar sein muss, wäre überlegenswert.
Ab diesem Zeitpunkt sind alle Abwehrmaßnahmen wirkungslos. Wenn er will, kann er die GOT wieder beschreibbar mappen, und selbst wenn ein solches Verhalten als „verdächtig“ erkannt würde oder das Betriebssystem die Umstellung verhindern könnte, kann der eingeschleuste Code den Kontrollfluss auf hunderte andere Arten umdrehen. Beliebiges Lesen/Schreiben, Codeausführung, alles ist möglich. Gegen eine Kompromittierung auf dieser Stufe gibt es keine Security-Mitigation. Wenn er will, kann er private Schlüssel exfiltrieren und direkt an den Angreifer senden oder eine Shell starten. In dieser Phase Schutzmechanismen entwerfen zu wollen, ist vergebliche Mühe.
Der korrekte Funktionszeiger wird erst beim ersten Aufruf geladen und statt des Stubs in die Tabelle eingetragen, und dieser Zeitpunkt kann beliebig weit in der Zukunft liegen. In großen Library-Ökosystemen wie etwa bei gtk-Apps werden die meisten gelinkten Funktionen tatsächlich nie aufgerufen.
-march=nativezu bauen, und mit-multiarchin den USE-Flags von glibc lässt sich ifunc einfach deaktivieren. Negative Auswirkungen habe ich nicht gesehen.Zu den ersten drei Phasen fügt dieser Artikel gegenüber dem, was in den letzten zwei Wochen bekannt wurde, nicht viel hinzu. Eher eine gute Zusammenfassung mit Flussdiagramm.
Neu wirkt allerdings der Teil, in dem das Binary derart detailliert analysiert wurde.
Wie ist der dort gezeigte Quellcode entstanden? Hat man einen Disassembler laufen lassen, verstanden, was der Code tut, und dann alle Namen durch sprechende Namen ersetzt? Für etwas, das in zwei Wochen geschafft wurde, wirkt das wie eine beachtliche Leistung.
Global Research & Analysis Team, Kaspersky Lab
https://securelist.com/author/great/
Da der Verfasser offenbar das Malware-Analyseteam von Kaspersky Lab ist, ist die Wahrscheinlichkeit hoch, dass sie Binary-Reverse-Engineering ziemlich gut beherrschen.
https://hex-rays.com/ida-pro/
Was mich wirklich interessiert: Wodurch genau entstand die anfängliche SSH-Verzögerung, die die Untersuchung von xz ausgelöst hat? Hat das jemand herausgefunden?
Laut den Leuten, die den Code reverse-engineert haben, muss die Befehlsnachricht auch an den SSH-Host-Key gebunden werden. Wenn der Host-Key also ein RSA-Schlüssel war, könnte pro Verbindung zusätzlich auch eine RSA-Entschlüsselung ausgeführt worden sein.
Das dürfte als Ursache für die Verzögerung ausreichen.
Es ist eine einfache Möglichkeit, von außen zu erkennen, ob ein Server infiziert ist, ohne zuerst eine Code-Injection versuchen zu müssen.
Die Autoren kennen sich extrem tief mit den Interna von glibc aus. Das ist Wissen, das man nur hat, wenn man bis zum Hals im Quellcode steckt, und es gibt viele neue Techniken.
Der Custom-ELF-Parser und der Disassembler sind so komplex, dass man sich kaum vorstellen kann, dass dieser Code früher nicht schon anderswo eingesetzt wurde oder künftig nicht wiederverwendet wird.
Ich frage mich, ob dieser Vorfall die ernsthafte Untersuchung bekommt, die er verdient, aber ich glaube eher nicht.
Hat jemand den Backdoor-Bug analysiert, der letztlich den Valgrind-Fehler und die Verlangsamung von SSH sichtbar gemacht hat?
Der Valgrind-„Fix“ bestand darin, ifunc zu deaktivieren, wodurch die Backdoor deaktiviert wurde und der Fehler verschwand.
Die Verlangsamung kam meines Wissens von all den Symbol- und Instruktions-Lookups, die die Backdoor durchführte.
Als Wendung betrachtet: Wenn man den Aufwand bedenkt, den der Angreifer in Skripten und Code betrieben hat, um Erkennung zu vermeiden, könnte dieses ganze Projekt auch eine Ablenkung gewesen sein oder ein Fallback, während parallel mehrere Versuche liefen.
Wie bleibt man solchen Dingen einen Schritt voraus? Beeinflusst es andere Teile des Systems, wenn sich die Community auf SSHD konzentriert? Andere technische oder soziale Aspekte?
Aluhüte machen Spaß.
Oder man kauft Closed Source von einem Anbieter wie Microsoft und hofft, dass dort die Ressourcen und der Wille vorhanden sind, den Code strenger zu prüfen.
Und es gibt natürlich immer den Ansatz, ein gutes Security-Operations-Team zu haben, das ungewöhnliche Netzwerkaktivität und Versuche zur Rechteausweitung erkennt.
Aber Contributors, die außerhalb des Projekts, an dem sie arbeiten, keine Historie und keine Spuren haben, sollten künftig als Warnsignal gelten.
Frühere Gruppen wie GOBBLES, ADM, ac1db1tch3z und ~el8 haben so etwas ebenfalls gemacht, ebenso private „Sicherheitsforscher“ wie isec.pl.
Problematisch ist es diesmal, weil staatliche Akteure den Unternehmenskapitalismus ausnutzen, der eine Ära geschaffen hat, in der Basisprojekte für wenig Geld am Leben gehalten werden. Böswillige Akteure verfügen über praktisch unbegrenzte Ressourcen, um ihre Ziele zu erreichen.
Das hat letztlich die Nachfrage nach und die Entstehung von Organisationen wie NSO und Zerodium geschaffen.
Davor hatten Exploits und Backdoors kaum Wert, und Hacker hofften auf Sponsoring oder Anstellung bei Firmen wie Qualys.
Ich habe einige Analysen von Google zu Zero-Day-Hacks gesehen, und die waren ebenfalls unrealistisch beeindruckend, aber dieser Hack dürfte zu den größten aller Zeiten gehören.
Ich habe gesehen, dass das xz-Repository wieder auf GitHub ist und Lasse sowie ein neuer Contributor aufräumen. Sie haben die ifunc-Unterstützung entfernt und Code zur Erzeugung der Testdateien ins Repository committet, damit Testdateien ohne Blob erstellt werden können. Es sieht so aus, als würden sie in eine gute Richtung arbeiten.