AsyncAPI npm compromise delivered an import-time backdoor
A privileged GitHub Actions workflow preceded unauthorized releases of five package versions that executed a modular backdoor when imported.
Operational layer / Use first
Detection & hunt kit
Deployable logic, required telemetry, and review state. Expand an artifact without leaving the intelligence report.
01 / DetectionNode.js Runtime Added to Current User Run KeyDetects a current-user Windows Run key value whose data invokes Node.js. The rule requires registry-set telemetry with the full target path and written data and does not depend on campaign-specific value names or infrastructure.high / stableView detailsMinimize
- Use
- Detection
- Confidence state
- stable
- Priority
- high
- ATT&CK
- T1547.001
Required telemetry
- category
- registry_set
- product
- windows
title: Node.js Runtime Added to Current User Run Key
id: 935cfd8d-6d2b-4b9d-bea9-7af07e9d9d1f
status: stable
author: FRAME ZERO
date: 2026-07-16
description: Detects a current-user Windows Run key value whose data invokes Node.js. The rule requires registry-set telemetry with the full target path and written data and does not depend on campaign-specific value names or infrastructure.
references:
- https://www.microsoft.com/en-us/security/blog/2026/07/15/unpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery/
logsource:
category: registry_set
product: windows
detection:
selection_path:
TargetObject|contains: '\Software\Microsoft\Windows\CurrentVersion\Run'
selection_runtime:
Details|contains:
- 'node.exe'
- 'node '
condition: selection_path and selection_runtime
falsepositives:
- User-scoped developer tooling or legitimate applications that intentionally start a Node.js service at logon
- Software installation or repair activity that registers an approved Node.js component
level: high
tags:
- attack.persistence
- attack.t1547.001
02 / Threat huntAsyncAPI Compromise Reported C2 EndpointsHunts network connection telemetry for the historical IP address and ports reported for the Miasma runtime delivered through compromised AsyncAPI packages. Validate connection time, current ownership, initiating process, and local package exposure before escalation.high / experimentalView detailsMinimize
- Use
- Threat hunt
- Confidence state
- experimental
- Priority
- high
- Review after
- 2026-08-15
Required telemetry
- category
- network_connection
ATT&CK / T1071
title: AsyncAPI Compromise Reported C2 Endpoints
id: 5a474d8b-bc48-4b54-8269-48463d216681
status: experimental
author: FRAME ZERO
date: 2026-07-16
description: Hunts network connection telemetry for the historical IP address and ports reported for the Miasma runtime delivered through compromised AsyncAPI packages. Validate connection time, current ownership, initiating process, and local package exposure before escalation.
references:
- https://www.microsoft.com/en-us/security/blog/2026/07/15/unpacking-asyncapi-npm-supply-chain-compromise-import-time-payload-delivery/
logsource:
category: network_connection
detection:
selection:
DestinationIp: '85.137.53.71'
DestinationPort:
- 8080
- 8081
- 8091
condition: selection
falsepositives:
- Reassigned or shared infrastructure after the reporting period
- Authorized security testing that replays the historical indicator
- Telemetry normalization that records a proxy or gateway address instead of the final destination
level: high
tags:
- attack.command-and-control
- attack.t1071
Analysis
Microsoft Threat Intelligence identified a coordinated compromise of the @asyncapi npm organization on July 14, 2026. An attacker caused five versions across four package names to be released through legitimate GitHub OpenID Connect publishing. Systems that resolved and imported an affected version could execute a loader that retrieved the Miasma modular backdoor, established persistence, and connected to command-and-control infrastructure.
A privileged pull request workflow preceded the releases
The sequence began with pull request 2155 against the asyncapi/generator repository. The pull request targeted a documentation preview workflow that used pull_request_target while checking out the untrusted pull request head. That combination placed attacker-controlled code in a base-repository security context with a broadly privileged GitHub token, persisted checkout credentials, and access to steps that referenced repository secrets.
The attacker-controlled commit was timestamped 05:08:58 UTC on July 14, and the documentation preview workflow started at 05:11:05 UTC. Submitted MDX attempted to retrieve and evaluate JavaScript from rentry[.]co/elzotebo999. The public workflow log confirms that the privileged job processed the malicious commit. It does not confirm that the web request succeeded or that this execution exposed a credential.
Later repository pushes were authenticated as asyncapi-bot. That ordering connects the unsafe workflow to subsequent release activity, but it does not establish how the bot credential was obtained. A proof of concept had examined the workflow weakness on April 29, and a proposal to separate untrusted build activity from secret-bearing steps remained under review from May 17 when the incident occurred.
Unauthorized commits entered trusted publishing
An unauthorized commit timestamped 06:58:42 UTC used a release-compatible message. A push-triggered workflow started at 07:05:42 UTC, and the legitimate release process published three poisoned packages at approximately 07:10 UTC.
The attacker then affected asyncapi/spec-json-schemas. The release workflow published @asyncapi/specs@6.11.2-alpha.1 at 08:06:20 UTC and the stable 6.11.2 version at 08:30:09 UTC. The stable package contained the same payload as the alpha release. A downstream Yarn cache received the stable tarball at 08:49:22 UTC.
All five versions carried valid npm provenance attestations because the legitimate repositories and release workflows built them. The attestations described the actual build lineage, including commits that the attacker had pushed without authorization. Provenance alone therefore did not distinguish an authorized release from an unauthorized commit delivered through a trusted pipeline.
Package imports started the loader
The poisoned packages declared no npm lifecycle hooks. The injected loader occupied an exported entry path and ran when an application called require() or import. The common npm install --ignore-scripts control did not prevent execution because the trigger occurred after installation when the package loaded.
The loader started a hidden detached Node.js child process. That process reconstructed an IPFS retrieval routine, downloaded sync.js, and stored it below an operating-system-specific directory named NodeJS. The second stage contained an approximately 8.2 MB encrypted bundle. Static key material shipped with the loader, allowing the bundle to be recovered without executing it.
Microsoft identified the recovered runtime as M-RED-TEAM v6.4 with campaign configuration miasma-train-p1 and organization label miasma-test-org. Persistence and command-and-control functions were active in the analyzed build. Windows persistence used an HKCU Run value, Linux persistence used a systemd user unit, and macOS persistence modified a shell startup file.
The active runtime supported directory listing, file transfer, remote shell execution, proxying, and data return. Credential harvesting, supply-chain propagation, AI-tool poisoning, metamorphic generation, reconnaissance, and evasion modules were present but disabled. Their presence describes available code in the recovered bundle, not behavior confirmed during this incident.
Detection and telemetry
The included durable Sigma rule detects a Node.js command added to a current-user Windows Run key. It does not use the reported value name, hashes, network address, package versions, or file paths. Registry telemetry must expose the complete target path and written data. Legitimate developer tools can create similar values, so analysts should inspect the executable path, signer, package history, parent process, and nearby network events.
The experimental hunt covers connections to the reported IP address on ports 8080, 8081, and 8091. A match is a historical indicator correlation, not proof of compromise. Confirm the destination, connection time, initiating process, current infrastructure ownership, and whether a poisoned package was present.
Endpoint review should also look for the reported tarball and loader hashes, sync.js below the listed directories, Node.js child execution, and persistence changes. CI telemetry should retain lockfile, cache, artifact, workflow, and provenance records long enough to reconstruct which jobs imported an affected version.
Containment and recovery
Remove the five affected versions from dependency trees, lockfiles, caches, artifact repositories, container layers, and shared build images. Microsoft identified @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 as known-good baselines in its report.
Treat any workstation or build host that imported an affected version as potentially exposed to second-stage execution. Isolate affected systems, rebuild from a known-good dependency baseline, remove persistence, and rotate accessible credentials from a clean host. Release owners should also review GitHub token scopes, workflow permissions, protected environments, approval boundaries, and anomalies involving automated publishing identities.
Scope and limitations
This analysis relies on one Microsoft research report. The report documents the affected packages, release sequence, recovered runtime, and reported indicators in detail, but the public workflow evidence does not prove how the bot credential was acquired. Attribution is limited to the observed attacker activity and recovered runtime identifiers.
The durable rule covers one Windows persistence behavior and does not cover Linux systemd persistence, macOS shell-profile changes, import-time execution, package cache exposure, or all Node.js child-process behavior. Complete investigation requires endpoint process, file, registry, network, dependency, CI, and release telemetry. Indicator observation times were not supplied, and the infrastructure may have changed since the incident.
Reported IOCs
View detailsMinimize
Microsoft did not provide per-value first-seen or last-seen times for the values below. They are time-sensitive historical pivots. Defenders should verify current ownership and resolution, use passive DNS where appropriate, and match each value to local event timing before drawing conclusions.
| Value | Type | Context |
|---|---|---|
rentry[.]co/elzotebo999 | URL | JavaScript retrieval target embedded in the malicious pull request |
npm-oidc-no-reply@github[.]com | Publisher identity | Automated identity recorded for the unauthorized trusted releases |
@asyncapi/specs@6.11.2-alpha.1 | Package version | Poisoned prerelease; injected file index.js |
d425e4583cc6185d41e95c45eda00550045a5d1919b9a012236a4520d009dbd7 | SHA-256 | Tarball for @asyncapi/specs@6.11.2-alpha.1 |
@asyncapi/specs@6.11.2 | Package version | Poisoned stable release; injected file index.js |
9b2e65db653ca8575c9b10eefb9a80c6006404812c2ec212bf5675e3c690233b | SHA-256 | Tarball for @asyncapi/specs@6.11.2 |
@asyncapi/generator@3.3.1 | Package version | Poisoned release; injected file lib/templates/config/validator.js |
bfaeb987faa6de2b5a5eb63b1233d055215b09b0349a9394f2175fd7cdf385e4 | SHA-256 | Tarball for @asyncapi/generator@3.3.1 |
@asyncapi/generator-components@0.7.1 | Package version | Poisoned release; injected file lib/utils/ErrorHandling.js |
082d733db0687dcd768104972b065d4b58cb1e6043688c6c20fa3702337f36ab | SHA-256 | Tarball for @asyncapi/generator-components@0.7.1 |
@asyncapi/generator-helpers@1.1.1 | Package version | Poisoned release; injected file src/utils.js |
34014776d3d3ff11bc4439b02fd7ac0f02a887eb3a052eeafff236e2f6db8ad1 | SHA-256 | Tarball for @asyncapi/generator-helpers@1.1.1 |
8351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0c | SHA-256 | Injected @asyncapi/specs loader in alpha and stable releases |
b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653a | SHA-256 | Injected @asyncapi/generator loader |
b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292 | SHA-256 | Injected @asyncapi/generator-components loader |
6e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71 | SHA-256 | Injected @asyncapi/generator-helpers loader |
24b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168 | SHA-256 | sync.js wrapper from the generator-family IPFS object |
Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf | IPFS CID | Second stage used by @asyncapi/specs |
QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9 | IPFS CID | Second stage used by the generator-family packages |
hxxps://ipfs[.]io/ipfs/Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyf | URL | Reported IPFS second-stage location |
85.137.53[.]71:8080 | IP address and port | Primary command-and-control endpoint |
85.137.53[.]71:8081 | IP address and port | Upload endpoint |
85.137.53[.]71:8091 | IP address and port | Management endpoint |
%LOCALAPPDATA%\NodeJS\sync.js | File path | Windows drop path |
~/.local/share/NodeJS/sync.js | File path | Linux drop path |
~/Library/Application Support/NodeJS/sync.js | File path | macOS drop path |
~/.config/NodeJS/sync.js | File path | Fallback drop path |
~/.config/.miasma/run/node.lock | File path | Runtime lock file |
miasma-monitor | Registry value pattern | Windows HKCU Run value naming convention |
miasma-monitor.service | Service name | Linux systemd user unit |
_miasma._tcp | Service pattern | mDNS service name |
/api/v1/beacon | HTTP path | Reported beacon path |
/api/v1/file-result | HTTP path | Reported file-result path |
/api/v1/file-content/<cid> | HTTP path pattern | Reported content path pattern, not an exact observed URL |