Skip to content

Back to the catalog

StepSecurity + Endor Labs: Mini-Shai-Hulud Wave 2
stepsecurity.iowriteup

StepSecurity + Endor Labs: Mini-Shai-Hulud Wave 2

StepSecurity and Endor Labs document a second wave of the Mini Shai-Hulud npm worm spreading through the compromised 'atool' account, which publishes timeago.js and maintains packages in Alibaba's @antv namespace, hitting echarts-for-react, jest-canvas-mock and dozens of other packages. The payload scrapes GitHub Actions Runner.Worker process memory to steal masked CI/CD secrets, harvests 130+ credential file paths (cloud, SSH, Kubernetes, Vault, crypto wallets, Claude Code/VS Code configs), exfiltrates via a GitHub API dead-drop in antvis/G2 or a fake-OpenTelemetry C2 at t.m-kosche.com, drops persistent backdoors in Claude Code/VS Code, and has already used stolen GitHub tokens to spin up 2,500+ dead-drop repositories. The writeup includes full process/network telemetry from Harden-Runner showing the attack blocked, deobfuscated payload internals, IOCs, and detection/remediation guidance.

TL;DR Second wave of the Mini Shai-Hulud npm worm compromises the atool/AntV/timeago.js publishing account, scraping GitHub Actions runner memory for secrets and self-propagating via 2,500+ dead-drop GitHub repos.
Document

What it is

A StepSecurity + Endor Labs runtime analysis of the "Mini Shai-Hulud Wave 2" npm worm, observed by installing echarts-for-react@3.0.7 inside a GitHub Actions workflow protected by Harden-Runner. The compromised atool npm account publishes timeago.js (1.5M weekly downloads) and @antv packages; the affected list is still growing while the attack is ongoing.

How it works

The malicious version ships as an optionalDependencies git reference. npm install clones a poisoned antvis/G2 commit (codeload.github.com/antvis/G2/tar.gz/7cb42f57...), which runs a prepare hook that downloads the Bun runtime and executes index.js. The payload then:

  • runs gh for GitHub CLI reconnaissance;
  • uses sudo to spawn python3 reading /proc/2175/mem — the Runner.Worker process — and pipes it through tr | grep isSecret:true | sort to pull masked CI/CD secrets out in plaintext;
  • harvests credentials from 130+ file paths (AWS, GCP, Azure, Kubernetes, Vault, crypto wallets);
  • exfiltrates via a GitHub API dead-drop in antvis/G2 and a fallback C2 at t.m-kosche.com, POSTing to a fake OpenTelemetry endpoint api/public/otel/v1/traces.

Detail not in the summary: the payload also drops persistent backdoors into Claude Code and VS Code configurations that survive across sessions. For echarts-for-react@3.0.7 the clone/scrape pattern repeated four times (four python3 PIDs all reading /proc/2175/mem).

flowchart TD
    A[npm install echarts-for-react@3.0.7] --> B[node: resolve github:antvis/G2, install bun]
    B --> C[dash: prepare hook]
    C --> D[bun.exe: index.js payload]
    D --> E[gh: recon]
    D --> F[sudo python3: read /proc/PID/mem Runner.Worker]
    F --> G[tr | grep isSecret:true | sort]
    D --> H[Backdoor Claude Code + VS Code]
    G --> I{Exfil}
    I --> J[GitHub dead-drop antvis/G2]
    I --> K[C2 t.m-kosche.com fake OTel]

Preconditions / Affected

CI/CD environments (GitHub Actions) that install any compromised atool/@antv package. Masked secrets are exposed because they live in Runner.Worker memory the payload can read.

What to do

  • If you installed any affected package, assume all secrets in that environment are compromised and rotate all credentials immediately.
  • Check Claude Code and VS Code configs for persistent backdoors — these survive session restarts.
  • Two Harden-Runner default protections each independently stopped the attack: the Global Block List blocked t.m-kosche.com, and memory-access detection on /proc/PID/mem triggered a lockdown that terminated the run. Either alone was sufficient.
  • Hunt indicators: outbound t.m-kosche.com, GitHub repos named with Dune terminology and the reversed description niagA oG eW ereH :duluH-iahS.

Caveats

The full telemetry was only captured by deliberately disabling the Block List and lockdown mode in a second run; with defaults on, the attack was blocked before completion. The compromised-package list was incomplete and growing at publication.

Organisations named here

  • antvis/G2
  • t.m-kosche.com

Technologies named here

  • Harden-Runner
  • echarts-for-react
  • StepSecurity Secure Registry
  • timeago.js
  • @antv/graphlib
  • StepSecurity Dev Machine Guard
  • @antv/* namespace packages
  • npm Package Cooldown Check
  • NPM Package Compromised Updates Check
  • jest-canvas-mock
  • jest-date-mock
  • npm Package Search
  • size-sensor
  • AI Package Analyst
  • lint-md
  • StepSecurity Threat Center
  • antvis/G2 GitHub repository
  • npm
  • GitHub Actions
  • Bun
  • VS Code
  • Claude Code
  • StepSecurity
  • AntV

Techniques named here

  • Runner.Worker Memory Scraping
  • CI/CD environment variable theft
  • Credential file harvesting
  • Dual-channel exfiltration
  • Direct install hook
  • optionalDependencies git reference
  • Double-publish version bump
  • Daemonization / background execution
  • Persistence via IDE/tool config backdoors
  • Privilege escalation via sudoers rule
  • Payload obfuscation
  • GitHub token reuse for infrastructure creation

Threat actors named here

  • Mini Shai-Hulud
  • atool