Intelligence / TIPublic intelligence

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.

Published
2026-07-16
Reading time
6 min read

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
Sigma sourceInline / YAML
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

Sigma sourceInline / YAML
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.

ValueTypeContext
rentry[.]co/elzotebo999URLJavaScript retrieval target embedded in the malicious pull request
npm-oidc-no-reply@github[.]comPublisher identityAutomated identity recorded for the unauthorized trusted releases
@asyncapi/specs@6.11.2-alpha.1Package versionPoisoned prerelease; injected file index.js
d425e4583cc6185d41e95c45eda00550045a5d1919b9a012236a4520d009dbd7SHA-256Tarball for @asyncapi/specs@6.11.2-alpha.1
@asyncapi/specs@6.11.2Package versionPoisoned stable release; injected file index.js
9b2e65db653ca8575c9b10eefb9a80c6006404812c2ec212bf5675e3c690233bSHA-256Tarball for @asyncapi/specs@6.11.2
@asyncapi/generator@3.3.1Package versionPoisoned release; injected file lib/templates/config/validator.js
bfaeb987faa6de2b5a5eb63b1233d055215b09b0349a9394f2175fd7cdf385e4SHA-256Tarball for @asyncapi/generator@3.3.1
@asyncapi/generator-components@0.7.1Package versionPoisoned release; injected file lib/utils/ErrorHandling.js
082d733db0687dcd768104972b065d4b58cb1e6043688c6c20fa3702337f36abSHA-256Tarball for @asyncapi/generator-components@0.7.1
@asyncapi/generator-helpers@1.1.1Package versionPoisoned release; injected file src/utils.js
34014776d3d3ff11bc4439b02fd7ac0f02a887eb3a052eeafff236e2f6db8ad1SHA-256Tarball for @asyncapi/generator-helpers@1.1.1
8351d251cf0b5a0bd82242deaa0a14e3e1394418d55c0f4259dac4303b79fc0cSHA-256Injected @asyncapi/specs loader in alpha and stable releases
b9993a8ad0518849416798cf29668256ccb96598fc4423501ccab5312812653aSHA-256Injected @asyncapi/generator loader
b270bdf8e2274ea1af0a6eed74d8f10e5fe61012d6cc226a43cc7cc7fd9f6292SHA-256Injected @asyncapi/generator-components loader
6e78713b75bd34828d49896176627f7face7aa9036cd874f2e02d9f23a9a9c71SHA-256Injected @asyncapi/generator-helpers loader
24b9ee242f21a73b55f7bb3297eafb33c60840907386b542ed79fc6b72365168SHA-256sync.js wrapper from the generator-family IPFS object
Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyfIPFS CIDSecond stage used by @asyncapi/specs
QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9IPFS CIDSecond stage used by the generator-family packages
hxxps://ipfs[.]io/ipfs/Qmet4fhsAaWMBUxNDfREHwgiyDeSWy4YSYs9wiKUW5jGyfURLReported IPFS second-stage location
85.137.53[.]71:8080IP address and portPrimary command-and-control endpoint
85.137.53[.]71:8081IP address and portUpload endpoint
85.137.53[.]71:8091IP address and portManagement endpoint
%LOCALAPPDATA%\NodeJS\sync.jsFile pathWindows drop path
~/.local/share/NodeJS/sync.jsFile pathLinux drop path
~/Library/Application Support/NodeJS/sync.jsFile pathmacOS drop path
~/.config/NodeJS/sync.jsFile pathFallback drop path
~/.config/.miasma/run/node.lockFile pathRuntime lock file
miasma-monitorRegistry value patternWindows HKCU Run value naming convention
miasma-monitor.serviceService nameLinux systemd user unit
_miasma._tcpService patternmDNS service name
/api/v1/beaconHTTP pathReported beacon path
/api/v1/file-resultHTTP pathReported file-result path
/api/v1/file-content/<cid>HTTP path patternReported content path pattern, not an exact observed URL

Sources

Evidence register
  1. Unpacking the AsyncAPI npm supply chain compromise and import-time payload deliveryresearch