Jeder Angriff begann mit einem Test: Über einen Branch namens wfx-docs eröffnete der Angreifer einen Pull Request mit dem Titel „docs: dependency audit notes“. So prüfte er, ob das Konto in das Repository schreiben darf. Solche Test-Pull-Requests tauchten in 61.764 Repositories auf, in einem Teil davon folgte anschließend der Stealer. Laut der technischen Analyse von Aikido ist jedes Konto, das einen dieser Test-Pull-Requests geöffnet hat, kompromittiert, auch wenn der Stealer nie lief. Wie der Angreifer an die Zugangsdaten kam, geht aus der Analyse nicht hervor.
In den meisten anderen getesteten Repositories wurde danach nichts gepusht, oder der Stealer lief wegen der Repository-Einstellungen oder der Kontoberechtigungen nicht. Die Zahlen umfassen nur die öffentlichen Repositories, die Aikido einsehen konnte. Betroffen sind auch Repositories, die nicht den kompromittierten Konten gehören: 10.431 der Test-Pull-Requests kamen über Mitarbeiter- oder Organisationszugriffe zustande, und 92 Prozent davon waren bei der Prüfung durch Aikido noch offen. Auch nach dem Ende von Aikidos eigener GitHub-Suche am Mittwoch wurden weitere Pull Requests mit demselben Titel eröffnet. Drei überprüfte Exemplare, die zwischen 05:04 und 05:38 UTC entstanden, stammten aus wfx-docs-Branches. Ob dort auch der Stealer folgte, ist unklar.
Forscher von SafeDep, einem Anbieter von Sicherheitswerkzeugen für Software-Lieferketten, kamen in einer eigenen Analyse auf 8.093 Repositories, die zwischen Dienstag, 12:46 UTC, und Donnerstag, 04:36 UTC, bösartige Commits erhielten. Diese Zahl umfasst alle Repositories mit bösartigen Commits, nicht nur jene, in denen der Stealer tatsächlich lief. Da SafeDep nur die Standard-Branches öffentlicher Repositories durchsucht hat, liegt die tatsächliche Zahl nach Angaben der Forscher höher. In 7.309 Fällen wurde der Stealer in einen bestehenden Workflow eingefügt. In 771 Fällen kam er als npm-postinstall-Hook, der bei npm install auch auf den Rechnern von Entwicklern ausgeführt wird.
Nach dem Push machte der Angreifer die Änderung meist innerhalb weniger Minuten rückgängig. Lief der Stealer, wurden seine Logs gelöscht, in der Regel zusammen mit dem Workflow-Lauf selbst. In diesen Repositories sind alle Secrets und Cloud-Rollen, auf die die Workflows zugreifen können, als kompromittiert zu betrachten.
SafeDep rät Betreibern betroffener Repositories, Token, OAuth-App-Berechtigungen, SSH-Schlüssel und Sitzungen des Commit-Autors zu widerrufen und die Zwei-Faktor-Authentifizierung einzuschalten. Zudem sollten alle Secrets und Variablen ausgetauscht werden, die das Repository lesen kann. Wer npm install auf einem betroffenen Commit ausgeführt hat, sollte den Rechner als kompromittiert behandeln und dessen Schlüssel erneuern.
Folgende Merkmale deuten ab Dienstag auf einen Angriff hin und sollten in allen Repositories mit Schreibzugriff geprüft werden:
- Branch wfx-docs
- kurzlebige Branches nach dem Muster chore/[a-z0-9]{8}
- Branches nach dem Muster ci-check-[0-9]{4}
- Pull-Request-Titel „docs: dependency audit notes“
- Commit „ci: refresh build cache“, der einem bestehenden Workflow rund 1.500 Zeilen hinzufügt
- Commit „chore: add ci helper“, der ein Skript .ci-xxxxxx.sh und einen postinstall-Hook in package.json ergänzt, der es bei jedem npm install startet
- Commit „chore: add dependency audit workflow“, der auf einem chore/-Branch die Datei .github/workflows/build-audit-xxxxxx.yml anlegt
- Commit „chore: initial commit“ in einem leeren Repository vor einer neuen Workflow-Datei
- Commits „ci: restore“ und „ci: cleanup“
- SHA-256 des Stealer-Skripts: 89695abb392d5cfd4972672961e644bb46baa98aa1ed7173c2a2816c762735cf
- Server: 194.150.166[.]244:8080/exfil, 18.143.168[.]96:8080/exfil und 153.75.82[.]159:8080
Ein Repository kann Daten verloren haben, auch wenn keine wfx-loot-Artefakte, keine wfx-Cache-Einträge und keine WFX-BEGIN-Zusammenfassungen zu finden sind. Ein verlässlicheres Indiz ist eine fehlende Laufnummer beim Commit des Angreifers.
Nach Erkenntnissen von SafeDep schickt der Stealer die Daten per HTTP-POST an die genannten Adressen oder an einen Telegram-Chat. In einer Stichprobe von 200 Payload-Commits nutzte etwa ein Fünftel ausschließlich Telegram.
Aikido zufolge sammelt der Stealer Zugangsdaten aus allen Quellen auf dem Runner: Secrets und Variablen von GitHub Actions, das GITHUB_TOKEN des Workflows, OIDC- und Actions-Runtime-Token, Schlüssel für KI-Dienste sowie Secrets im Arbeitsspeicher der Runner-Prozesse. Außerdem liest er Credential-Dateien für AWS, Azure, GCP, Kubernetes, npm, PyPI, SSH und weitere Werkzeuge an ihren üblichen Speicherorten aus. Er kopiert zudem das Repository, dessen letzte 100 Commits und aktuelle Workflow-Artefakte. Auf selbst gehosteten Runnern greift er Cloud-Zugangsdaten aus den Instanz-Metadaten ab, dazu den Zustand von Docker-Containern und systemd-Units. Da der Stealer auch Einstellungen für private Repositories enthält, hält Aikido es für möglich, dass er auch dort läuft.
Mit den erbeuteten Daten soll der Stealer tiefer in Cloud-Konten vordringen. Er tauscht GitHub-OIDC-Token gegen Sitzungen bei AWS, GCP und Azure ein und kann bei AWS langlebige Zugriffsschlüssel für IAM-Benutzer erzeugen. Manche Schritte sind darauf ausgelegt, einen Austausch der Zugangsdaten zu überstehen: So kann er die Vertrauensrichtlinie einer AWS-Rolle so umschreiben, dass jedes beliebige AWS-Konto die Rolle nutzen kann. In Azure-AD-Anwendungen kann er föderierte Identitätsnachweise mit dem Präfix wfx-ci- anlegen. Workflows im kompromittierten Repository können sich dann ohne gespeichertes Secret bei Azure anmelden, selbst nachdem alle Token ausgetauscht wurden.
Um solche Hintertüren aufzuspüren, empfehlen die Forscher folgende Prüfungen:
- Entra ID: selbst nicht angelegte föderierte Nachweise mit dem Namen wfx-ci-*
- AWS CloudTrail: Sitzungen über AssumeRoleWithWebIdentity oder AssumeRole mit dem Namen wfx-*, gefolgt von CreateAccessKey oder UpdateAssumeRolePolicy
- GCP: Aufrufe von GenerateAccessToken und SignJwt aus dem eigenen GitHub-Workload-Identity-Pool
- Vault: JWT-Anmeldungen über den GitHub-OIDC-Aussteller
Über automatisierte Veröffentlichungsabläufe gelangte der Stealer in mindestens ein Packagist-Paket und ein Go-Modul. Mindestens 50 der kompromittierten Nutzer veröffentlichen offenbar Pakete für npm, Go, Packagist, PyPI oder crates. Nach Einschätzung von Aikido geht es dem Angreifer jedoch eher um CI-Secrets als um eine Verbreitung über Pakete.
Version 3.14.3 des Packagist-Pakets digitalelvis/neuronai-studio ist bösartig. Aikido rät, jeden Rechner, auf dem sie installiert oder geöffnet wurde, als kompromittiert zu betrachten. Laut Paketseite wurde die Version am Mittwoch veröffentlicht und war am frühen Donnerstag noch gelistet, versehen mit einem Hinweis, dass Versionen des Pakets als Malware markiert wurden. Ein normales composer install startet den Stealer nicht, wohl aber ein npm install im vendor-Verzeichnis des Pakets. Nutzer sollten v3.14.3 sperren und auf v3.14.2 oder älter festlegen.
Beim Go-Modul github.com/onuragtas/docker-env enthalten die Versionen v1.0.278 und v1.0.280 den Stealer nur in einer GitHub-Actions-Workflow-Datei. Go-Code und Release-Binärdateien sind unverändert, und Go-Builds führen keine Workflow-Dateien aus, sodass Nutzer des Moduls womöglich nicht betroffen sind. Aikido empfiehlt dennoch, diese Versionen zu entfernen. Im Go-Modul github.com/vkuznet/gocurl fanden die Forscher den Stealer in der Pseudo-Version v0.0.0-20261111095532-8d5aad0cb0da, in einem Workflow, der nie ausgeführt wurde. Diese Pseudo-Version sollte gesperrt werden.
Zudem erreichte der Stealer 28 Repositories von WebdriverIO über Commits, die unter dem Bot-Konto wdio-bot erstellt wurden. Kein Paket-Release war betroffen, das Token des Bots ist aber als gestohlen zu betrachten.
Die diese Woche genutzten Konten überschneiden sich laut Aikido mit denen der GhostAction-Welle im Oktober, bei der gestohlene GitHub-Konten ebenfalls Secrets stehlende Workflows in Repositories einschleusten. Im Oktober betroffene Konten wurden etwa 1.000-mal häufiger missbraucht als andere aktive Konten. In mehr als 100 Repositories wurde der neue Stealer in die Workflow-Dateien vom Oktober eingefügt, wobei der ältere Schritt zur Datenausleitung erhalten blieb. Aikido erklärte dennoch: „Wir schreiben die November-Welle derzeit nicht demselben Akteur zu.“ Infrastruktur, Werkzeuge und Methoden unterschieden sich, und gestohlene Zugangsdaten landeten oft bei mehreren Angreifern.
Eine zweite Analyse ergänzte frühere Erkenntnisse: Neben zwei HTTP-Adressen nutzt der Stealer einen dritten Server und einen Telegram-Kanal, und einige Änderungen blieben länger als eine Minute bestehen.
