Kai Ole Hartwig
10 Min. Lesezeit
Hoch

AsyncAPI-npm-Kompromittierung: „Pwn Request“ über pull_request_target schleust Miasma-Botnet in generator & specs

Am 14. Juli 2026 haben Angreifer fünf npm-Releases im AsyncAPI-Namespace kompromittiert: @asyncapi/specs@6.11.2 (plus 6.11.2-alpha.1), @asyncapi/generator@3.3.1, @asyncapi/generator-helpers@1.1.1 und @asyncapi/generator-components@0.7.1 — zusammen rund 2,9 Millionen wöchentliche Downloads. Ursache war eine seit dem 17. Mai 2026 ungemergte Fix-PR (#2092) für eine pull_request_target-Schwachstelle im GitHub-Actions-Workflow des generator-Repos: dieser Trigger gewährt Workflows Zugriff auf Repository-Secrets, selbst wenn Code aus einem externen Pull-Request ausgecheckt wird. Ein Angreifer eröffnete 37 Pull-Requests; einer davon exfiltrierte das npm-Publish-Token an einen rentry.co-Dead-Drop. Die daraus resultierende Payload lädt über IPFS eine ~8,2-MB-verschlüsselte Loader-Stage nach, identifiziert sich als „M-RED-TEAM v6.4“ mit Miasma-Branding und baut C2-Verbindungen zu einem eigenen Server, Nostr-Relays und einem Ethereum-Contract auf — mit Beacon-Intervall von rund 30 Sekunden.

TL;DR — 90 Sekunden

Betroffen?

@asyncapi/specs@6.11.2 / 6.11.2-alpha.1, @asyncapi/generator@3.3.1, @asyncapi/generator-helpers@1.1.1, @asyncapi/generator-components@0.7.1 — veröffentlicht am 14.07.2026 zwischen 07:10 und 08:28 UTC. Zusammen rund 2,9 Mio. wöchentliche Downloads.

Risiko?

Ein Angreifer stahl das npm-Publish-Token über eine seit Mai ungemergte pull_request_target-Schwachstelle im GitHub-Actions-Workflow des generator-Repos. Die eingeschleuste Payload lädt über IPFS eine verschlüsselte Loader-Stage nach, die sich als „M-RED-TEAM v6.4“ mit Miasma-Branding identifiziert: C2-Beacon alle ~30s, Shell-Zugriff via child_process.exec, Persistenz pro Betriebssystem. Credential-Harvester und Propagationsmodule sind im Code vorhanden, aber aktuell deaktiviert.

Sofortmaßnahme?

Betroffene Pakete auf 6.11.1 / 3.3.0 / 1.1.0 / 0.7.0 zurückstufen, aus Lockfiles/Caches/Build-Images entfernen, IoCs prüfen, Secrets von einem sauberen Rechner aus rotieren.

Empfehlung?

Der Payload läuft beim require()/Import, nicht erst über einen npm-Install-Hook — --ignore-scripts hätte hier nicht geholfen. Wer die Versionen importiert hat, sollte das als aktive Kompromittierung behandeln, nicht nur als Supply-Chain-Risiko.

Kritikalität?

hoch — aktiver, funktionsfähiger Botnet-Payload mit Mio.-Downloads im Blast-Radius, auch wenn Kernfunktionen wie der Credential-Harvester aktuell abgeschaltet sind.

Was ist passiert?

Das asyncapi/generator-Repository auf GitHub nutzte in seiner CI einen Workflow mit pull_request_target-Trigger. Dieser Trigger führt den Workflow im Kontext des Zielrepositorys aus — inklusive Zugriff auf dessen Secrets — selbst wenn der Code aus einem externen, nicht vertrauenswürdigen Pull-Request ausgecheckt wird. Dieses Muster ist als „pwn request“-Schwachstellenklasse seit Jahren bekannt. Eine Fix-PR (#2092) war bereits am 17. Mai 2026 eröffnet worden, blieb aber knapp zwei Monate lang ungemergt liegen.

Am 14. Juli 2026 nutzte ein Angreifer dieses Zeitfenster: Um 05:08 UTC eröffnete er 37 Pull-Requests gegen das Repository. Einer davon, PR #2155, enthielt obfuskiertes JavaScript, das um 05:16 UTC über den verwundbaren Workflow das npm-Publish-Token des Projekts an den Paste-/Dead-Drop-Dienst rentry.co exfiltrierte. Mit diesem Token pushte der Angreifer um 06:58 UTC bösartige Commits in den next-Branch. Um 07:10 UTC erschienen die ersten kompromittierten Paket-Versionen auf npm; zwischen 07:51 und 08:28 UTC folgten elf weitere Commits im Repository asyncapi/spec-json-schemas, aus denen die kompromittierten specs-Versionen veröffentlicht wurden.

Die eingeschleuste Payload sitzt in Modulen, die bereits bei normalen import/require()-Aufrufen ausgeführt werden — nicht in einem npm-Lifecycle-Hook wie postinstall. In @asyncapi/specs startet ein Downloader per spawn() einen detachten Node-Kindprozess, der eine verschlüsselte Payload nachlädt.

Wer ist betroffen?

PaketBetroffene Version(en)Sichere Version
@asyncapi/specs6.11.2, 6.11.2-alpha.16.11.1
@asyncapi/generator3.3.13.3.0
@asyncapi/generator-helpers1.1.11.1.0
@asyncapi/generator-components0.7.10.7.0

Betroffen ist jeder, dessen Abhängigkeitsbaum — direkt oder transitiv — eine dieser Versionen auflöst und dabei das jeweilige Modul tatsächlich importiert, insbesondere Build-Pipelines und Doku-/SDK-Generatoren, die AsyncAPI-Spezifikationen verarbeiten. Da der Payload beim Import ausgeführt wird, reicht bloßes Vorhandensein in node_modules ohne Import nicht zwangsläufig für eine Kompromittierung — Build-Tools requiren Module aber häufig automatisch, weshalb dieser Unterschied in der Praxis kaum als verlässlicher Schutz taugt.

Auswirkungen: was die Payload tatsächlich kann

Die Infektionskette läuft in drei Stufen: Ein Downloader (per spawn('node', [...], {detached: true, stdio: 'ignore'})) holt eine verschlüsselte Node.js-Loader-Datei über IPFS (eigene Content-IDs für die specs- und generator-Builds) und schreibt sie als sync.js ins nutzerspezifische NodeJS-Verzeichnis. Der Loader (~8,2 MB obfuskiertes JavaScript) leitet per HKDF-SHA256 einen AES-256-GCM-Schlüssel ab, entschlüsselt einen eingebetteten Vault und wertet rotierten ASCII-Klartext aus. Das Implant identifiziert sich selbst als „M-RED-TEAM v6.4“, generiert ein secp256k1-Schlüsselpaar, richtet Persistenz ein und sendet alle ~30 Sekunden ein Beacon an 85.137.53.71:8080.

Funktional bietet das Implant: Persistenz je Betriebssystem (macOS: Anhängen an Shell-RC-Dateien; Windows: HKCU\Run; Linux: systemd-User-Service), signierte/verschlüsselte C2-Kommandos mit unverschlüsseltem HTTP-Fallback, Shell-Ausführung via child_process.exec() (nur der Befehl killall ist geblacklistet), sandboxed Dateioperationen auf einen /sim-fs-Pfad (Löschen ist nicht implementiert) sowie einen Update-Mechanismus, der alle zwei Minuten über signierte Nostr-Relays und einen unsignierten Ethereum-Contract nach neuen Payload-Versionen pollt.

Im Code vorhanden, aber aktuell deaktiviert: ein Credential-Harvester für Cloud-, SSH-/GPG-, Browser-, CI/CD-, KI-Tool- und Wallet-Zugangsdaten, Propagationsvektoren für npm/PyPI/Cargo/Ruby, eine Mutation-Engine, Evasion-Techniken, KI-Tool-Poisoning und ein Deadman-Switch. Drei Konfigurationsfelder, die wie Sicherheitsmechanismen aussehen, erwiesen sich bei der Analyse als wirkungslos: safeMode: true (der zugehörige Validator wird nie aufgerufen), actualPersist: false (der Code liest stattdessen toggles.persist: true) und canaryPercent: 5 (die zuständige BatchDispatch-Funktion ist nicht implementiert). Da der Update-Mechanismus aktiv ist, könnten deaktivierte Module jederzeit nachträglich aktiviert werden.

Mitigation / Sofortmaßnahmen

Operativer Entscheidungsblock

Schritt 1 — betroffene Versionen finden und zurückstufen

 

# installierte Versionen prüfen
npm ls @asyncapi/specs @asyncapi/generator @asyncapi/generator-helpers @asyncapi/generator-components

# Lockfile nach betroffenen Versionen durchsuchen
grep -E '"(@asyncapi/(specs|generator|generator-helpers|generator-components))":' package-lock.json | grep -E '6\.11\.2|3\.3\.1|1\.1\.1|0\.7\.1'

# auf bekannt-gute Versionen zurückstufen
npm install @asyncapi/specs@6.11.1 @asyncapi/generator@3.3.0 @asyncapi/generator-helpers@1.1.0 @asyncapi/generator-components@0.7.0

# aus lokalen/CI-Caches und Build-Images entfernen
npm cache clean --force
# Build-Images mit betroffenen Versionen neu bauen, nicht nur neu deployen

 

Schritt 2 — Artefakte bereinigen

 

# nach IoC-Dateien suchen (siehe Detection-Abschnitt)
# Persistenz-Service entfernen (Linux)
systemctl --user disable --now miasma-monitor.service 2>/dev/null
rm -f ~/.config/systemd/user/miasma-monitor.service
rm -f ~/.config/.miasma/run/node.lock

 

Schritt 3 — Secrets von einem sauberen Rechner aus rotieren

 

# npm-Publish-Token, Source-Control-Zugriff, Cloud-Credentials,
# CI/CD-Secrets, SSH-/Signing-Keys, Browser-Sessions
# Rotation NIEMALS von einem möglicherweise kompromittierten Rechner ausführen

 

Hinweis: Der Tarball von @asyncapi/specs@6.11.2-alpha.1 kann laut Quellenlage trotz entfernter Registry-Metadaten weiterhin über CDN erreichbar sein — explizit gegenprüfen, nicht nur auf die npm-Registry-Ansicht verlassen.

Detection / Prüfung

Netzwerk-Indikatoren (IoCs)

 

# C2-Server (Beacon-Port 8080, Upload 8081, Proxy 8091)
85.137.53.71:8080
85.137.53.71:8081
85.137.53.71:8091

# Ethereum-Contract (Chain-ID 1) als Update-Kanal
0x12c37A86a0Ed0beBe5d1d6a43E42f07860eAc710

# Nostr-Relays als Update-Kanal
wss://relay.damus.io
wss://relay.nostr.com/

 

Host-Artefakte suchen

 

# Drop-Datei
find ~ -name "sync.js" -path "*nodejs*" 2>/dev/null

# Lock-Datei
ls -la ~/.config/.miasma/run/node.lock 2>/dev/null

# macOS Identity-Datei
ls -la ~/Library/"Application Support"/com.apple.spotlight/index-v2.cache 2>/dev/null

# Linux Identity-Datei
ls -la ~/.cache/mesa_shader_cache/gl_cache.bin 2>/dev/null

# Windows Identity-Datei (PowerShell)
# Test-Path "$env:USERPROFILE\AppData\Roaming\Microsoft\CryptnetUrlCache\Content\msrt.dat"

# Linux Persistenz-Service
systemctl --user status miasma-monitor.service 2>/dev/null
cat ~/.config/systemd/user/miasma-monitor.service 2>/dev/null

 

Laufende Prozesse und Verbindungen

 

# detachte Node-Prozesse ohne erkennbaren Elternprozess/TTY
ps -eo pid,ppid,tty,cmd | grep -i node

# aktive Verbindungen zu den C2-Ports
ss -tnp | grep -E "85\.137\.53\.71"

# ungewöhnliche ausgehende Verbindungen zu IPFS-Gateways von CI-/Build-Hosts

 

Da der Payload beim import/require() läuft, ist eine reine Paket-Versionsprüfung nicht ausreichend — relevant ist, ob das Modul auf einem Host tatsächlich geladen wurde (Build-Logs, CI-Job-Historie, Runtime-Monitoring).

Betreiberempfehlung

Mid-Market

Falls AsyncAPI-Tooling (Dokumentations- oder SDK-Generierung aus Event-API-Spezifikationen, etwa für Kafka-/MQTT-/WebSocket-APIs) in der eigenen Node-Toolchain oder CI eingesetzt wird: Versionen sofort prüfen und bei Treffer als aktive Kompromittierung behandeln, nicht als reines Update-Thema.

Enterprise / CI-Verantwortliche

Unabhängig von der direkten Betroffenheit: die eigenen GitHub-Actions-Workflows auf pull_request_target-Nutzung durchsuchen (grep -rl "pull_request_target" .github/workflows/). Wo dieser Trigger auf Code aus externen Pull-Requests angewendet wird, entweder auf pull_request ohne Secret-Zugriff umstellen oder ein explizites Review-Gate vor der Workflow-Ausführung erzwingen. Offene Fix-PRs für genau diese Klasse von Workflow-Schwachstellen sollten priorisiert werden — zwei Monate Liegezeit waren hier das entscheidende Zeitfenster für den Angreifer.

Entscheidungsblock

Heute handeln, wenn: Ihr Abhängigkeitsbaum eine betroffene Version auflöst und nach 07:10 UTC am 14.07.2026 installiert/gebaut wurde — vollständiger IoC-Sweep plus Credential-Rotation. Beobachten und trotzdem prüfen, wenn: keine betroffene Version im Lockfile gefunden wurde — dann als Anlass nutzen, die eigenen pull_request_target-Workflows zu auditieren, unabhängig vom AsyncAPI-Bezug.

Häufige Fragen zur AsyncAPI-Kompromittierung

Wurde die pull_request_target-Lücke im generator-Repo inzwischen behoben?+

Die zugehörige Fix-PR #2092 war seit dem 17. Mai 2026 offen. Ob sie im Zuge dieses Vorfalls gemerged wurde, geht aus den herangezogenen Quellen nicht eindeutig hervor — vor einer Wiederverwendung des CI-Workflows den aktuellen Stand im Repository selbst prüfen.

Betrifft mich das als TYPO3-/PHP-Entwickler überhaupt?+

Direkt nur, wenn AsyncAPI-Tooling in der eigenen Node-Toolchain zum Einsatz kommt — etwa zur Dokumentation von Event-APIs neben einem TYPO3-/PHP-Backend. Das zugrunde liegende CI-Muster (pull_request_target mit Secret-Zugriff auf Fork-PRs) ist aber unabhängig vom Sprachstack relevant für jedes GitHub-Actions-Setup, auch reine PHP-Projekte.

Was, wenn ich die Pakete nur installiert, aber nie importiert habe?+

Das Risiko ist geringer, aber nicht null — Build-Tools und Doku-Generatoren requiren Abhängigkeiten häufig automatisch im Rahmen ihres normalen Betriebs. Best Practice bleibt Downgrade plus IoC-Check, auch ohne bewussten eigenen Import-Aufruf.

Ist das dieselbe Kampagne wie die früheren Miasma-Wellen auf diesem Blog?+

Unklar. Das Branding (Dateinamen, Pfade, Bezeichner) ist identisch mit früheren Miasma-Vorfällen, die technische Umsetzung unterscheidet sich aber deutlich — Node statt Bun, secp256k1/AES-GCM statt RSA/AES-CBC, HTTP-C2 statt aktiver Propagation. Ob derselbe Akteur, Code-Wiederverwendung oder bloße Namensübernahme vorliegt, lässt sich aus den verfügbaren Quellen nicht abschließend klären.

Ist der Credential-Harvester im Code aktiv ausnutzbar?+

Laut den herangezogenen Analysen aktuell nein — er ist im Code vorhanden, aber über Toggle-Felder deaktiviert. Da das Implant per Nostr- und Ethereum-Polling regelmäßig nach Updates sucht, kann sich das jedoch jederzeit ändern, ohne dass ein neues npm-Release nötig wäre.

Reicht --ignore-scripts, um mich zu schützen?+

Nein. Der Payload läuft beim require()/Import des Moduls in normalem Anwendungscode, nicht über einen npm-Lifecycle-Hook wie postinstall. --ignore-scripts hätte diesen Angriff nicht verhindert.

Fazit

Es ist bereits der dritte Vorfall auf diesem Blog innerhalb weniger Monate, der Miasma-Branding trägt — nach der npm-Wurm-Welle bei @redhat-cloud-services und der Editor-Konfigurations-Welle gegen KI-Coding-Agenten. Die technische Umsetzung unterscheidet sich diesmal deutlich genug, dass eine einfache „gleicher Akteur“-Zuordnung verfrüht wäre — aber das Branding allein ist bereits ein Signal, dass Toolkits und Namenskonventionen in diesem Bereich wiederverwendet oder kopiert werden.

Die eigentliche Lehre liegt aber nicht im Payload, sondern im Einfallstor: pull_request_target mit Secret-Zugriff auf extern eingereichten Code ist eine seit Jahren dokumentierte, unter dem Namen „pwn request“ bekannte Schwachstellenklasse in GitHub Actions — und trotzdem lag hier eine fertige Fix-PR zwei Monate lang ungemergt herum, bis ein Angreifer sie fand. Offene Sicherheits-PRs sind kein Backlog-Item, das warten kann; sie sind ein bekanntes Zeitfenster, das jemand irgendwann nutzt.

Quellen

Ich prüfe Ihre GitHub-Actions-Workflows auf pull_request_target-Fallstricke, downgrade betroffene Pakete und rotiere kompromittierte Secrets.

Audit aller Workflow-Trigger auf Secret-Zugriff bei extern eingereichtem Code, Downgrade betroffener AsyncAPI-Pakete, vollständige Credential-Rotation und IoC-Sweep.

Plattform-Betrieb statt Beratung auf Papier: Ich prüfe, härte und überwache Ihre CI/CD- und Node-Toolchain laufend.

Termin buchen

Über den Autor

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.