Unpacking the AsyncAPI npm supply chain compromise and import-time payload delivery
A coordinated supply chain compromise of the @asyncapi npm organization delivered malicious payloads through five package versions published via the project's legitimate GitHub Actions OIDC trusted publishing pipeline. The attack originated from a pwn request exploiting a misconfigured pullrequesttarget workflow that exposed the asyncapi-bot PAT, enabling unauthorized commits to auto-publish branches. Unlike typical npm supply-chain attacks, the payload executes at import/require time rather than through lifecycle hooks, bypassing --ignore-scripts mitigations. The second stage fetches an encrypted ~8.2 MB Miasma modular runtime from IPFS, establishing C2 at 85.137.53.71 with OS-specific persistence and decentralized fallback channels.
- filenamesync.jsSecond-stage payload dropped by the import-time loader into OS-specific NodeJS masquerade directories; contains the encrypted Miasma runtime
- ip85[.]137[.]53[.]71Primary C2 server for Miasma runtime — port 8080 (C2 beacon), port 8081 (file upload/exfiltration), port 8091 (management configuration)
- registry_keyHKCU\Software\Microsoft\Windows\CurrentVersion\Run\miasma-monitorWindows persistence mechanism installed by Miasma runtime via HKCU Run key to maintain access across reboots
- sha256082d733db0687dcd768104972b065d4b58cb1e6043688c6c20fa3702337f36abTarball SHA-256 of compromised @asyncapi/[email protected] — hunt for this hash in npm/Yarn caches
- sha25624b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168SHA-256 of sync.js wrapper (generator-family IPFS object) — the second-stage payload containing the encrypted Miasma runtime
- sha25634014776d3d3ff11bc4439b02fd7ac0f02a887eb3a052eeafff236e2f6db8ad1Tarball SHA-256 of compromised @asyncapi/[email protected] — hunt for this hash in npm/Yarn caches
- sha2566e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71SHA-256 of injected utils.js in @asyncapi/[email protected]
- sha2568351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0cSHA-256 of injected index.js in @asyncapi/specs (identical in both alpha 6.11.2-alpha.1 and stable 6.11.2)
- sha2569b2e65db653ca8575c9b10eefb9a80c6006404812c2ec212bf5675e3c690233bTarball SHA-256 of compromised @asyncapi/[email protected] stable — widest downstream reach due to transitive dependency usage
- sha256b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292SHA-256 of injected ErrorHandling.js in @asyncapi/[email protected]
- sha256b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653aSHA-256 of injected validator.js in @asyncapi/[email protected]
- sha256bfaeb987faa6de2b5a5eb63b1233d055215b09b0349a9394f2175fd7cdf385e4Tarball SHA-256 of compromised @asyncapi/[email protected] — hunt for this hash in npm/Yarn caches
- sha256d425e4583cc6185d41e95c45eda00550045a5d1919b9a012236a4520d009dbd7Tarball SHA-256 of compromised @asyncapi/[email protected] — hunt for this hash in npm/Yarn caches and CI artifact stores
- urlhxxps://ipfs[.]io/ipfs/Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyfIPFS gateway URL for fetching second-stage sync.js payload for @asyncapi/specs compromised versions
- urlhxxps://ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9IPFS gateway URL for fetching second-stage sync.js payload for @asyncapi/generator family compromised versions
- urlhxxps://rentry[.]co/elzotebo999URL used in the initial GitHub Actions pwn request to retrieve and evaluate JavaScript from an external source via malicious MDX content in PR #2155
Detection / Hunteropenrouter
What Happened
On July 14, 2026, attackers compromised a popular set of npm packages used by developers for the AsyncAPI specification. They gained access by exploiting a misconfigured automated workflow in the project's GitHub repository, which let them publish malicious package updates through the project's normal, trusted publishing process. When developers or automated build systems downloaded and used these packages, hidden malicious code ran automatically — not during installation, but when the package was actually used in code, which means a common security workaround (skipping install scripts) did not help. The malicious code downloaded a larger payload from a decentralized file system (IPFS) that gave attackers remote access to the affected machines, installed persistence to survive reboots, and could steal credentials from cloud services, code repositories, and other developer tools. Organizations that used these packages should immediately remove the affected versions, clear their package caches, check for signs of infection, and rotate any credentials that may have been exposed.
Key Takeaways
- Five @asyncapi npm package versions were compromised via a GitHub Actions pwn request that stole the asyncapi-bot PAT, enabling unauthorized pushes to auto-publish branches that triggered legitimate OIDC release workflows.
- Unlike typical supply-chain attacks, this campaign executes at module-load (import/require) time rather than via npm lifecycle hooks, making the common --ignore-scripts mitigation ineffective.
- The second-stage payload (sync.js, ~8.2 MB) is fetched from IPFS and decrypts a modular runtime called Miasma (M-RED-TEAM v6.4) with active C2, persistence, and decentralized fallback channels via Nostr, Ethereum, BitTorrent DHT, libp2p, and IPFS.
- Although credential harvesting, supply-chain propagation, AI-tool poisoning, and metamorphic generation modules are disabled in this build, the framework targets over 100 environment variable names and multiple credential files across cloud, CI/CD, and AI platforms.
- All five malicious versions carry valid npm provenance attestations because they were published through the project's legitimate trusted publishing pipeline, making provenance verification alone insufficient to detect the compromise.
Affected Systems
- Developer workstations (Windows, macOS, Linux) resolving @asyncapi npm packages
- CI/CD pipelines and build runners that install or cache npm dependencies
- Container build environments that include @asyncapi packages in their dependency trees
- Production services that import @asyncapi/specs, @asyncapi/generator, @asyncapi/generator-components, or @asyncapi/generator-helpers
Vulnerabilities (CVEs)
None identified.
Attack Chain
- Initial Access: Attacker submitted a malicious pull request (PR #2155) targeting the asyncapi/generator repository's docs-preview workflow, which used pull_request_target and checked out untrusted PR content in a privileged context
- Credential Theft: The privileged workflow executed attacker-controlled code that retrieved JavaScript from rentry.co/elzotebo999, leading to the exposure of the asyncapi-bot personal access token (PAT)
- Supply Chain Compromise: Using the stolen bot PAT, the attacker pushed unauthorized commits to auto-publish branches, triggering legitimate GitHub Actions OIDC release workflows that published five poisoned npm packages with valid provenance attestations
- Delivery: Consumers pulled poisoned package versions through normal npm/Yarn dependency resolution; the --ignore-scripts flag was ineffective because the payload executes at import/require time, not via lifecycle hooks
- Execution & Second Stage: Importing the compromised module triggered an obfuscated loader that spawned a hidden detached Node.js child process, which downloaded sync.js from IPFS to an OS-specific NodeJS masquerade directory
- C2 & Persistence: The sync.js payload decrypted the ~8.2 MB Miasma modular runtime (M-RED-TEAM v6.4), establishing C2 at 85.137.53.71 and installing OS-specific persistence (Windows HKCU Run key, Linux systemd service, macOS shell RC injection)
Detection Availability
- YARA Rules: No
- Sigma Rules: No
- Snort/Suricata Rules: No
- KQL Queries: Yes
- Splunk SPL Queries: No
- EQL Queries: No
- Other Detection Logic: No
- Platforms: Microsoft Defender XDR Advanced Hunting, Microsoft Defender Antivirus, Microsoft Defender for Endpoint
The article provides four KQL advanced hunting queries for Microsoft Defender XDR covering sync.js drop and detached spawn, IPFS CID retrieval, poisoned tarball hashes in caches, and suspicious Node.js execution. Microsoft Defender Antivirus signatures (Trojan:JS/MiasmStealer.SC, Trojan:Script/Supychain.A, Trojan:JS/SpawnLoader.MKV!MTB, Trojan:JS/VaultLoader.MJZ!MTB) and Defender for Endpoint behavioral detections are also referenced.
Detection Engineering Assessment
| Dimension | Rating | Rationale |
|---|---|---|
| EDR Visibility | Medium | Detached Node.js process spawns, file writes to NodeJS masquerade directories, and persistence modifications (registry Run keys, systemd services, shell RC files) are visible to EDR. However, developer workstations and CI/CD build runners may not always have EDR agents deployed, and Node.js process spawning is common in development environments, creating noise. |
| Network Visibility | Medium | C2 connections to 85.137.53.71 on ports 8080/8081/8091 and IPFS gateway requests are network-detectable. However, IPFS traffic to ipfs.io may be legitimate in some organizations, and decentralized fallback channels (Nostr, Ethereum, BitTorrent DHT, libp2p) use protocols that may not be routinely monitored. |
| Detection Difficulty | Moderate | The import-time execution bypasses common supply-chain controls focused on lifecycle hooks, and valid provenance attestations make automated trust verification insufficient. However, the dropped artifacts (sync.js in NodeJS directories), C2 IP, and persistence mechanisms are relatively distinctive and huntable once the indicators are known. |
Required Log Sources
- DeviceProcessEvents (Node.js process creation, detached spawns)
- DeviceFileEvents (sync.js file creation, NodeJS directory creation)
- DeviceNetworkEvents (C2 connections, IPFS gateway requests)
- npm/Yarn cache logs and CI/CD pipeline logs (dependency resolution events)
- GitHub Actions workflow run logs (pull_request_target execution, OIDC publishing)
- Registry modification events (HKCU Run key changes)
- Linux systemd journal (user service creation)
Hunting Hypotheses
| Hypothesis | Telemetry | ATT&CK Stage | FP Risk |
|---|---|---|---|
| Consider hunting for Node.js processes spawning detached child processes with stdio set to ignore and windowsHide enabled, as this is an unusual execution pattern for legitimate development workflows. | EDR process telemetry — specifically process command lines, parent-child relationships, and process creation flags for node/node.exe | Execution | Medium — some legitimate Node.js tooling may use detached processes, but the combination of detached, stdio ignored, and windowsHide is uncommon in normal development. |
| Consider hunting for files named sync.js appearing in directories named NodeJS under user profile paths (e.g., %LOCALAPPDATA%\NodeJS, ~/.local/share/NodeJS), as these are attacker-created masquerade directories not associated with legitimate Node.js installations. | EDR file events — file creation events filtered by filename and folder path patterns | Defense Evasion / Persistence | Low — the specific combination of a file named sync.js in a directory named NodeJS under user profile paths is highly suspicious and unlikely in normal operations. |
| Consider hunting for network connections from Node.js processes to IPFS gateway domains, especially those fetching specific content identifiers, as this may indicate second-stage payload retrieval. | EDR network events — DNS resolution and HTTP requests initiated by node/node.exe processes | Command and Control | Medium — some organizations legitimately use IPFS, but connections from Node.js processes to public IPFS gateways during build or runtime should be investigated. |
| Consider hunting for persistence mechanisms with 'miasma' in the name across Windows registry Run keys, Linux systemd user units, and macOS shell RC files, as the Miasma runtime uses this naming convention consistently. | EDR persistence telemetry — registry modifications, systemd service creation, shell profile modifications | Persistence | Low — the string 'miasma' in persistence locations is highly specific to this threat and unlikely to appear in legitimate software. |
| Consider hunting for npm/Yarn cache entries or CI artifact stores containing tarballs matching the five known compromised SHA-256 hashes, as these indicate the poisoned packages were resolved during the exposure window. | File integrity monitoring on package cache directories, CI/CD pipeline logs, artifact repository audit logs | Initial Access / Delivery | Low — exact SHA-256 hash matches are deterministic indicators with no false positive risk. |
Control Gaps
- npm --ignore-scripts flag does not mitigate import-time payload execution, leaving a gap in supply-chain controls that rely on lifecycle hook suppression
- npm provenance attestation verification alone cannot detect this compromise because the packages were published through the project's legitimate OIDC trusted publishing pipeline with valid attestations
- Preinstall/postinstall hook auditing scanners will not flag these packages because no lifecycle hooks are declared in package.json
- Traditional network perimeter controls may not block IPFS gateway traffic if IPFS is not specifically filtered, allowing second-stage payload retrieval
- Decentralized fallback channels (Nostr, Ethereum, BitTorrent DHT, libp2p) may bypass traditional network security controls that focus on TCP/HTTP traffic to known C2 infrastructure
- Developer workstations and CI/CD build runners may lack EDR coverage, creating blind spots for post-compromise activity
Key Behavioral Indicators
- Node.js process spawning a detached child process with stdio set to 'ignore' and windowsHide set to true
- File named sync.js written to a directory named NodeJS under user profile paths (%LOCALAPPDATA%\NodeJS, ~/.local/share/NodeJS, ~/Library/Application Support/NodeJS, ~/.config/NodeJS)
- Registry value 'miasma-monitor' created under HKCU\Software\Microsoft\Windows\CurrentVersion\Run
- Linux systemd user service named 'miasma-monitor.service' created in user space
- Modifications to macOS shell RC files (.zshrc, .bashrc, .bash_profile) containing references to miasma or NodeJS paths
- Node.js process making outbound connections to 85.137.53.71 on ports 8080, 8081, or 8091
- Node.js process requesting IPFS content from ipfs.io gateway with specific CIDs
- Lock file created at ~/.config/.miasma/run/node.lock
- mDNS service advertisement for _miasma._tcp on local network
- Node.js process executing with command line containing obfuscated variable patterns (e.g., const _0x5af5e1)
False Positive Assessment
Low — the IOCs are highly specific (exact SHA-256 hashes, specific C2 IP, distinctive file names like sync.js in NodeJS masquerade directories, and persistence mechanisms using the 'miasma' naming convention). The main source of potential false positives would be IPFS gateway traffic from Node.js processes in organizations that legitimately use IPFS, and detached Node.js process spawns in development environments.
Recommendations
Immediate Mitigation
- Verify against your organization's incident response runbook and team escalation paths before acting. Immediately identify and remove all five affected package versions (@asyncapi/[email protected], @asyncapi/[email protected], @asyncapi/[email protected], @asyncapi/[email protected], @asyncapi/[email protected]) from dependency trees, lockfiles, and artifact repositories.
- Consider purging npm and Yarn caches on all developer endpoints and build hosts, especially shared CI caches where compromised tarballs may persist.
- If applicable, consider blocking outbound connections to 85.137.53.71 on ports 8080, 8081, and 8091 at network perimeter controls.
- If any environment imported a compromised version, consider rotating all credentials and secrets accessible from that environment (cloud provider keys, CI/CD tokens, SSH keys, npm tokens, secrets manager tokens) from a known-clean host.
- Consider hunting for sync.js and NodeJS masquerade directories across endpoints, and investigate any detached Node.js execution referencing known IPFS CIDs.
Infrastructure Hardening
- Consider pinning known-good versions: @asyncapi/specs 6.11.1 or earlier, @asyncapi/generator 3.3.0, @asyncapi/generator-components 0.7.0, and @asyncapi/generator-helpers 1.1.0.
- If your organization does not rely on IPFS for business operations, consider blocking public IPFS gateways (ipfs.io, dweb.link, cloudflare-ipfs.com) at the network perimeter to reduce attack surface.
- Consider updating to npm CLI v11.10.0+ or enabling the npm CLI min-release-age feature to delay automatic resolution of newly published package versions.
- Evaluate your GitHub Actions workflows for pull_request_target usage with untrusted checkout, and consider separating untrusted build activity from steps that access repository secrets.
- Consider reviewing token scopes, workflow approvals, protected environments, and anomaly detection around automated package publication in your CI/CD pipelines.
- If your organization shares base images or golden build runners, consider rebuilding them to ensure poisoned caches or post-compromise persistence are not inherited by future jobs.
User Protection
- Consider ensuring EDR coverage (e.g., Microsoft Defender for Endpoint) is deployed across developer workstations and CI/CD build runners where npm packages are resolved and imported.
- If supported by your endpoint protection, consider enabling cloud-delivered protection and behavioral detection features to catch suspicious Node.js process behavior, IPFS retrieval, and persistence activity.
- Consider implementing package version pinning policies in development environments to prevent automatic resolution of newly published versions without review.
Security Awareness
- Consider incorporating guidance into existing developer awareness programs about the limitations of --ignore-scripts as a supply-chain mitigation, since import-time execution bypasses this control.
- Consider educating development teams on the risk of transitive dependencies and the importance of reviewing dependency trees for unexpected version changes.
- If applicable, consider rolling into existing security training a reminder that valid package provenance attestations do not guarantee package safety if the publishing pipeline itself has been compromised.
MITRE ATT&CK Mapping
Initial Access
Execution
Persistence
Stealth
Credential Access
Command and Control
Additional IOCs
- Urls:
hxxps://ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9- IPFS gateway URL for fetching second-stage sync.js payload for @asyncapi/generator family compromised versionshxxps://rentry[.]co/elzotebo999- URL used in the initial GitHub Actions pwn request to retrieve and evaluate JavaScript from an external source via malicious MDX content in PR #2155
- File Hashes:
8351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0c(SHA256) - SHA-256 of injected index.js in @asyncapi/specs (identical in both alpha 6.11.2-alpha.1 and stable 6.11.2)b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653a(SHA256) - SHA-256 of injected validator.js in @asyncapi/[email protected]b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292(SHA256) - SHA-256 of injected ErrorHandling.js in @asyncapi/[email protected]6e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71(SHA256) - SHA-256 of injected utils.js in @asyncapi/[email protected]24b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168(SHA256) - SHA-256 of sync.js wrapper (generator-family IPFS object) — the second-stage payload containing the encrypted Miasma runtime
- File Paths:
%LOCALAPPDATA%\NodeJS\sync.js- Windows drop path for second-stage sync.js payload in NodeJS masquerade directory~/.local/share/NodeJS/sync.js- Linux drop path for second-stage sync.js payload in NodeJS masquerade directory~/Library/Application Support/NodeJS/sync.js- macOS drop path for second-stage sync.js payload in NodeJS masquerade directory~/.config/NodeJS/sync.js- Fallback drop path for second-stage sync.js payload when OS-specific path is unavailable~/.config/.miasma/run/node.lock- Runtime lock file created by Miasma runtime to prevent concurrent execution
- Command Lines:
- Purpose: Spawning a hidden, detached Node.js child process to execute the second-stage payload outside the parent process tree | Tools:
node,node.exe| Stage: Stage 1 — Import-time execution - Purpose: Executing obfuscated JavaScript directly via Node.js eval flag as a detached process | Tools:
node,node.exe| Stage: Stage 1 — Import-time execution |node -e "const _0x5af5e1
- Purpose: Spawning a hidden, detached Node.js child process to execute the second-stage payload outside the parent process tree | Tools:
- Other:
@asyncapi/[email protected]- Compromised alpha prerelease of @asyncapi/specs with import-time loader injected into index.js@asyncapi/[email protected]- Compromised version of @asyncapi/generator with import-time loader injected into lib/templates/config/validator.js@asyncapi/[email protected]- Compromised version of @asyncapi/generator-components with import-time loader injected into lib/utils/ErrorHandling.js@asyncapi/[email protected]- Compromised version of @asyncapi/generator-helpers with import-time loader injected into src/utils.js[email protected]- Publisher identity used by the legitimate GitHub Actions OIDC release workflow that published all five compromised package versionsQmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf- IPFS content identifier for @asyncapi/specs second-stage sync.js payloadQmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9- IPFS content identifier for @asyncapi/generator-family second-stage sync.js payload_miasma._tcp- mDNS service name used by Miasma runtime for local network peer discoverymiasma-monitor.service- Linux systemd user unit name used by Miasma runtime for persistence