Skip to main content

Eine Nachbetrachtung zur bösartigen event-stream-Backdoor

neweracracker suspicious issue

6. Dezember 2018

0 Min. Lesezeit

Letzte Woche geschah das Unvorstellbare. Ein bösartiges Paket namens flatmap-stream wurde auf npm veröffentlicht und später vom Nutzer right9ctrl als Abhängigkeit zum weit verbreiteten Paket event-stream hinzugefügt. Einige Zeit und 8 Millionen Downloads später führten Anwendungen im gesamten Web unwissentlich bösartigen Code in der Produktionsumgebung aus. Wir veröffentlichten letzte Woche kurz nach Bekanntwerden des Vorfalls erste Gedanken dazu in unserem Blog. Nun können wir eine ausführlichere Nachbetrachtung vorlegen, einschließlich einer Chronologie der Ereignisse. Unser Dank gilt vielen anderen, die ebenfalls zu diesem Vorfall recherchiert haben, insbesondere dem GitHub-Nutzer maths22, der den bösartigen Code rückentwickelt hat.

Was ist das Paket event-stream?

Das Paket event-stream ist ein Toolkit mit Hilfsprogrammen zum Erstellen und Verwalten von Streams. Es wurde von Dominic Tarr (~dominictarr auf npmjs) entwickelt und ist eines von 422 Paketen, deren Inhaber er auf npmjs ist. Das Paket event-stream hat insgesamt 84 Releases. Das erste war v0.5.2 im Jahr 2011; regelmäßige Releases gab es bis Version 3.3.4 vor zwei Jahren.

Während der gesamten Entwicklung von event-stream haben 33 verschiedene Mitwirkende Beiträge geleistet. Die meisten davon kamen jedoch in der Anfangszeit; seitdem wurden nur noch kleinere Änderungen überprüft:

Diagramm mit wöchentlichen Hinzufügungen auf GitHub in Grün und Löschungen in Rot. Die meisten Aktivitäten fanden am 12.–13. Januar statt.

Das Projekt hatte über 2.000 Sterne, wurde 139-mal geforkt und 62 GitHub-Nutzer hatten Benachrichtigungen zu Änderungen am Projekt abonniert. Das Projekt wurde von 3.931 anderen Paketen verwendet (Pakete mit Namensräumen ausgenommen).

Die Chronologie der Ereignisse

Hier sehen Sie eine Chronologie mit einigen wichtigen Meilensteinen in der Projektgeschichte und den entscheidenden Momenten des bösartigen Vorfalls. Im Folgenden gehen wir auf jeden Punkt der Chronologie und weitere Aspekte genauer ein.

Snyk-Zeitleiste zu event-stream mit der Paketerstellung 2011, der Wartung, Änderungen an Releases und Sicherheitsvorfallmeldungen im November 2018

Die Ereigniskette

Wir betrachten die Ereigniskette, die zum Einsatz des bösartigen Pakets flatmap-stream führte. Die Recherche stützt sich auf öffentlich zugängliche Informationen von GitHub, den Google-Cache und npm.

31. Juli 2015: Der GitHub-Nutzer devinus kommentiert ein Issue im event-stream-Projekt und fragt, ob eine Flatmap-Funktion willkommen wäre. Der Paketbetreuer dominictarr antwortet zustimmend und erklärt, dass Beiträge von Nutzern angenommen würden:

GitHub-Diskussion über die Annahme eines flatMap-Patches und darüber, warum mapSync emit statt queue verwendet, mit einem Link zu pull-stream.

Es lässt sich vermuten, dass der später als böswillig erkannte Nutzer right9ctrl diese Informationen genutzt haben könnte, um einen ausgeklügelten Social-Engineering-Angriff auf das Projekt zu planen und durchzuführen.

5. August 2018: Ein Nutzer, der sich auf npm als „Antonio Macias“ ausgab, erstellte und veröffentlichte ein harmloses Paket namens flatmap-stream.

Anschließend schlug Antonio Macias vor, dass das event-stream-Projekt das im flatmap-Paket verwendete Paket einbindet. Der GitHub-Nutzer right9ctrl wandte sich an Dominic Tarr und bot seine Unterstützung für das Projekt sowie die erforderlichen Änderungen an, um die Flatmap-Funktion durch Einbinden der Abhängigkeit flatmap-stream hinzuzufügen. Dominic nahm das Angebot von right9ctrl an und machte ihn zum Mitwirkenden am event-stream-GitHub-Projekt. Außerdem erteilte er right9ctrl im npm-Ökosystem vollständige Veröffentlichungsrechte für das Modul. Während des Vorfalls bestätigte Dominic später, dass er selbst keine Veröffentlichungsrechte mehr für das Modul auf npm hatte, um den Vorfall zu beheben (z. B. indem er die infizierte Version 3.3.6 von npm entfernte).

Kurz darauf übermittelte right9ctrl eine Reihe harmlos wirkender Commits an das event-stream-GitHub-Repository:

4. September 2018: ein harmloses Update einer Abhängigkeit.

4./5. September 2018: Beispiele für map und split hinzugefügt: 0cc, ee8, c08, 05b

5. September 2018: event-stream-Version 3.3.5 mit 918 veröffentlicht.

9. September 2018: Die Abhängigkeit flatmap-stream wurde mit e31 hinzugefügt.

9. September 2018: Ein neues Minor-Release von event-stream, Version 3.3.6, wurde mit 599 erstellt.

16. September 2018: flatmap-stream wurde mit 908 aus dem event-stream-Code und mit 2bd aus dem Abhängigkeitsbaum entfernt und als Major-Version 4.0.0 veröffentlicht.

20. September 2018: right9ctrl nimmt weitere kosmetische Code-Änderungen vor, die die Schlüsselwörter des Projekts mit 60d ergänzen – vermutlich, um die Suchergebnisse auf der offiziellen npmjs.com-Registry-Website weiter zu verbessern.

5. Oktober 2018: Eine neue Minor-Version, flatmap-stream@0.1.1, wird veröffentlicht. Ihr minimierter Quellcode enthält einen Injection-Angriff. Installationen von event-stream laden nun als transitive Abhängigkeit auch die neue infizierte Version 0.1.1 von flatmap herunter.

Es gibt keine weiteren Hinweise auf Arbeiten des Nutzers right9ctrl am event-stream-Projekt. Sein Profil wurde inzwischen von GitHub und npm entfernt, ist jedoch zur Einsicht weiterhin über den Google-Cache abrufbar:

GitHub-Profil von right9ctrl mit drei beliebten Repositorys und einer Beitrags-Heatmap mit 22 Beiträgen im letzten Jahr.

29. Oktober 2018: jaydenseric erstellte ein Issue zu nodemon und meldete eine unerwartete Deprecation-Warnung. Diese Meldung entspricht der Empfehlung von OpenSSL, statt EVP_BytesToKey einen moderneren Algorithmus zu verwenden. Entwickler sollten selbst einen Schlüssel und einen IV ableiten, dafür crypto.scrypt() verwenden und mit crypto.createDecipheriv() das Decipher-Objekt erstellen.

GitHub-Issue mit dem Titel „Deprecation warning at start #1442“, in dem eine Nodemon-Warnung beim Starten von Node.js beschrieben wird. Zu sehen sind Versionsdetails und ein Befehlsbeispiel.

19. November 2018: NewEraCracker eröffnete ein Issue zu event-stream.

Screenshot eines Kommentars zu verdächtigen npm-Updates der flatmap-stream-Versionen 0.1.0, 0.1.1 und 0.1.2.

19. November 2018: NewEraCracker eröffnete ein Issue zu nodemon.

GitHub-Issue mit dem Titel „Manipulierte transitive Abhängigkeit? flatmap-stream-0.1.1 und flatmap-stream-0.1.2“, in dem eine verdächtige Abhängigkeitskette beschrieben wird.

20. November 2018: FallingSnow vermutet einen Injection-Angriff.

20. November 2018: FallingSnow eröffnet das Issue zu event-stream.

26. November 2018: Ein Beitrag auf HackerNews erscheint, und das Issue verbreitet sich in den sozialen Medien.

26. November 2018: Das Paket flatmap-stream wird von npm entfernt.

26. November 2018: Mehrere Nutzer melden das Problem über das Formular zur Meldung von Schwachstellen an Snyk.

26. November 2018: Snyk nimmt das bösartige Paket in seine Schwachstellendatenbank auf und benachrichtigt alle überwachten Projekte.

26. November 2018: Danny Grander von Snyk meldet das Problem an die Security Working Group der Node.js Foundation.

27. November 2018: Snyk veröffentlicht einen Blogbeitrag zu dem Problem.

Das Ziel: Copay

Bei genauerer Untersuchung des Codes von flatmap-stream zeigt sich, dass es sich um einen gezielten Angriff auf Copay handelte, eine sichere Bitcoin-Wallet-Plattform.

Der bösartige Code von flatmap-stream wurde millionenfach heruntergeladen und noch viele Millionen Mal häufiger ausgeführt. Die Angreifer hätten damit unzählige Schäden anrichten können. Stattdessen warteten sie gezielt auf die Gelegenheit, den Code beim Build der Copay-App auszuführen. Das gelang ihnen: Der Code wurde in die Copay-Versionen 5.0.2 bis 5.1.0 eingebunden.

Der Entschlüsselungscode suchte in einer Umgebungsvariable namens npm_package_description nach dem Schlüssel. npm legt diese Umgebungsvariable anhand der Beschreibung des Root-Pakets fest. Entschlüsselt wurde der Code nur, wenn es sich bei der Client-Anwendung um die Bitcoin-Wallet Copay handelte, die den Schlüssel zur Entschlüsselung der Payload „A Secure Bitcoin Wallet“ verwendete. maths22 fand den Schlüssel durch Brute-Force-Versuche mit verschiedenen npm-Paketbeschreibungen.

Um das herauszufinden, ging der Nutzer maths22 verschiedene npm-Paketbeschreibungen durch und verwendete sie als Schlüssel, um die Payload zu entschlüsseln. Doch damit nicht genug: Eine zweite Payload wurde ausgeführt, sobald bestimmte Build-Befehle ausgeführt wurden – im Wesentlichen nur beim Build der iOS-, Android- oder Desktop-Anwendungen.

Die dritte und letzte Payload besteht aus JavaScript-Code, der in eine andere Abhängigkeit eingeschleust wird, nämlich in ./node_modules/@zxing/library/esm5/core/common/reedsolomon/ReedSolomonDecoder.js. Anders als die ersten beiden Payloads, die während des Builds ausgeführt wurden, lief dieser Code anschließend direkt in der App.

Der bösartige Code stahl Bitcoins und die privaten Schlüssel der Wallets, wenn deren Guthaben mehr als 100 Bitcoins oder 1.000 BHC (Bitcoin Cash) betrug. Copay gab seinen Nutzern folgende Empfehlung:

Nutzer sollten nicht versuchen, Guthaben in neue Wallets zu übertragen, indem sie die zwölf Wörter langen Backup-Phrasen betroffener Wallets importieren (diese entsprechen möglicherweise kompromittierten privaten Schlüsseln). Zuerst sollten sie ihre betroffenen Wallets (Version 5.0.2–5.1.0) aktualisieren und anschließend mit der Funktion „Send Max“ das gesamte Guthaben an eine brandneue Wallet mit Version 5.2.0 senden.

Außerdem wurde Nutzern empfohlen, „davon auszugehen“, dass ihre privaten Schlüssel kompromittiert sein könnten, und „sofort“ alle Guthaben auf neue, sichere Wallets mit Version 5.2.0 zu übertragen.

Die Nachbetrachtung der Ereignisse und des Angriffs zeigt, dass es sich um einen gut geplanten und professionell ausgeführten Angriff handelte, dessen Vorbereitung wahrscheinlich Monate dauerte.

Fazit

Die in diesem Blog beschriebenen Ereignisse erinnern erneut daran, wie anfällig das Open-Source-Modell sein kann, wenn es nicht angemessen unterstützt wird. Hätten auch nur wenige der Nutzer, die das weit verbreitete Paket event-stream verwenden und davon profitieren, zu seiner Pflege beigetragen, wäre die bösartige Übernahme leicht zu verhindern gewesen. Das Paket event-stream war im gesamten npm-Ökosystem als Abhängigkeit eingebunden – in mindestens 3.931 Paketen. Betroffen waren insbesondere übergeordnete Pakete wie @vue/cli-ui, vscode, nodemon und ps-tree.

Das bösartige Paket wäre möglicherweise unentdeckt geblieben, wenn die Deprecation-Meldung Jayden Seric nicht dazu veranlasst hätte, ein Issue zum Paket nodemon zu eröffnen. Andernfalls wäre es wahrscheinlich noch lange nicht entdeckt worden.

Bei Snyk setzen wir uns stark für verantwortungsvolle Offenlegung ein und betreiben Sicherheitsforschung als Teil unserer Unternehmenskultur. Wenn Sie eine Schwachstelle entdecken, die Sie verantwortungsvoll offenlegen möchten, freuen wir uns über Ihre Meldung über unser Formular zur verantwortungsvollen Offenlegung.

Eine Meldung einreichen.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.