# Snipping the Long Tail of Shai-Hulud 2.0

Shai-Hulud npm worm persisted via a malicious OpenVSX asyncapi-preview v1.0.1 extension that IDEs never updated after registry rollback to v1.0.0, plus stale private registries and caches.

- Payout: Real-world hack
- Severity: critical
- Program: Shai-Hulud 2.0
- Analysis by: Wiz Research
- Disclosed: 2025-12-30
- Write-up quality: 8/10
- Original: https://www.wiz.io/blog/snipping-the-long-tail-of-shai-hulud-2-0
- Library page: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-snipping-the-long-tail-of-shai-hulud-2-0
- Tags: area:oss domain:oss impact:data-leak impact:funds-loss kind:incident platform:threat-intel sev:critical tech:nodejs technique:incomplete-fix technique:source-review vuln:dependency-confusion vuln:secrets-leak

**Target:** npm packages, OpenVSX IDE extension, private registries, npm local cache
**Root cause:** Malicious asyncapi-preview v1.0.1 was pulled from OpenVSX but the registry rolled back to v1.0.0, so IDEs saw no higher version and never overwrote the installed malicious v1.0.1; private registries and npm caches also kept serving revoked poisoned packages.
**Steps:**
1. Worm publishes malicious asyncapi-preview v1.0.1 to OpenVSX; developers install it.
2. Extension is pulled from OpenVSX on Nov 26, but registry version rolls back to v1.0.0.
3. IDEs compare installed v1.0.1 against available v1.0.0, see no update, and keep the malicious extension active.
4. Exfiltration logs show indicator `_PACOTE_NO_PREPARE_` pointing to `git+ssh://git@github.com/asyncapi/cli.git#2efa4dff59bc3d3cecdf897ccf178f99b115d63d`.
5. Private registries (Nexus, Artifactory) fail to purge revoked sha1-hulud packages and keep serving them internally.
6. CI environments using `--offline` or `--prefer-offline` keep resolving cached malicious packages.
7. Wiz reports to AsyncAPI Dec 23; AsyncAPI publishes clean v1.1.0 Dec 24, forcing IDE auto-update and collapsing new infections.
**How found:** Wiz tracked the long tail of new compromised repositories via GHArchive and their own telemetry, noticed infections plateaued at 100-200/day after containment, and traced recurring exfil log indicator `_PACOTE_NO_PREPARE_` to the malicious asyncapi fork commit, revealing the version-rollback persistence.
**Exploitation:** Worm harvested credentials from infected developer machines and exfiltrated them to attacker-controlled repositories; leaked secrets included a GitHub token with admin:enterprise over the trustwallet org and Chrome Web Store client ID/secret/refresh token, providing paths to hijack the Trust Wallet browser extension distribution pipeline (v2.68 malicious update, $7M theft).
**Escalation:** Harvested secrets from worm victims were later used for targeted exploitation, e.g. suspected hijack of Trust Wallet's extension publishing pipeline via leaked Chrome Web Store credentials and enterprise GitHub token.
**Insight:** When a malicious package version is pulled, check whether the registry rolls back to a lower version than what victims installed — IDEs and package managers only auto-update upward, so a clean higher version bump is required to evict it; also audit private mirrors and offline caches for revoked packages.

## Proof of concept (copied exactly from the original report)

**Malicious commit hash indicator in exfil logs**

```
Our analysis found a recurring indicator in victim exfiltration logs: "_PACOTE_NO_PREPARE_": "git+ssh://git@github.com/asyncapi/cli.git#2efa4dff59bc3d3cecdf897ccf178f99b115d63d"
This commit hash points directly to the malicious fork used in the original Shai-Hulud attack.
```

**npm offline flags bypassing registry validation**

```
- --offline: Forces npm to use the local cache exclusively.

- --prefer-offline: Prioritizes the local cache, only hitting the registry if a package is missing.
In high-velocity CI/CD environments where these flags are a common performance enhancement
```

**Trust Wallet leaked credentials list**

```
- GitHub Token: A valid token with admin:enterprise scope over the trustwallet GitHub organization.

- Web Store Credentials: A Chrome Web Store Client ID, Client Secret, and refresh token exfiltrated from GitHub Action secrets.
```

