~/f4n6 $ grep -r "Don't Revoke That Token Yet: Inside the keyv/cacheable npm Worm" ./investigations/ --include="*.md"

Don't Revoke That Token Yet: Inside the keyv/cacheable npm Worm

Jeff Davies 05 Aug 2026 5 min read

1. Executive summary

On August 4, 2026, an attacker compromised the maintainer account for the widely used keyv and cacheable npm namespaces and published trojanized releases containing a self-propagating worm (tracked as CHAINDROP/Shai-Hulud). The malicious packages use a preinstall hook to execute a payload that steals cloud, Vault, Kubernetes, and npm credentials, and then leverages the stolen npm tokens to automatically backdoor additional packages. The campaign has impacted over 400–868 packages (figures vary by vendor reporting) and introduces a dead-man's switch that triggers an unknown, attacker-controlled payload if compromised GitHub tokens are revoked. EMEA financial services with heavy Node.js/JavaScript dependencies are at significant risk of credential compromise and lateral movement via CI/CD environments.

2. Regulatory framing

No specific DORA/NIS2 article is directly engaged by this item.

3. Technical analysis & attack chain

This item is a threat-campaign involving a self-propagating npm supply chain worm. The attack chain operates as follows:

  1. Initial Compromise: The attacker took over the maintainer account for the keyv and cacheable npm namespaces. The first malicious release, keyv@6.0.0, was published at 09:35 UTC on August 4, 2026.
  2. Execution via preinstall: The malicious package.json files contain a preinstall hook: "scripts": { "preinstall": "node setup.mjs" }. This executes automatically upon npm install before installation completes.
  3. Secondary Execution Paths: The attacker injected autostart hooks into source repositories. A SessionStart entry in .claude/settings.json runs node .claude/setup.mjs, and a folderOpen task in .vscode/tasks.json runs node .vscode/setup.mjs. This triggers the payload when a cloned repository is merely opened in an IDE or AI agent, without requiring an installation.
  4. Dropper & Runtime: setup.mjs downloads a standalone Bun runtime (v1.3.13) directly from the official release page, extracts it, and uses it to execute the heavily obfuscated payload (Math_Symbol.js or math_init.js, ~711–728 KB). The dropper then deletes the Bun temporary directory.
  5. Credential Harvesting: The payload (collector component) scans for over 300 unique patterns across developer machines and CI/CD environments. It targets AWS instance metadata, cloud keys (AWS, GCP, Azure, Alibaba), HashiCorp Vault tokens, Kubernetes service-account tokens, GitHub Actions secrets, npm tokens, SSH private keys, and AI tooling credentials (Anthropic, Claude, Codex, Cursor, OpenAI, Gemini).
  6. Exfiltration: Stolen credentials are gzip-compressed and encrypted with a randomly generated AES-256-GCM key. The AES key is then RSA-encrypted with an attacker's hardcoded public key. Data is exfiltrated via an attacker-controlled HTTPS endpoint, with GitHub repositories serving as a fallback exfiltration channel.
  7. Worm Propagation: Using stolen npm publishing tokens, the malware enumerates accessible packages, downloads their latest tarballs, inserts the malware and setup.mjs loader, adds the preinstall hook, increments the patch version, and republishes. Worm-generated commits can be identified by the author name claude and the commit message chore: update config.
  8. Dead-Man's Switch (Persistence): The payload writes the stolen GitHub token and an attacker-supplied handler command to ~/.config/gh-token-monitor/. It persists as a macOS LaunchAgent or a Linux systemd user service (enabled with loginctl enable-linger) describing itself as a "GitHub Token Validity Monitor." A watcher script polls the GitHub API every 60 seconds. If the token returns an HTTP 4xx (triggered by revocation), it evals the remote-supplied handler string, deletes its own state, and exits. It also self-destructs after a 24-hour TTL.

Confidence caveat: Package count figures vary by source (SafeDep verified 353 versions across 79 names; Aikido reported at least 868 packages; Microsoft and Elastic report 400+). The exact total is still moving. The identity of the threat actor remains unconfirmed.

4. Mitigation & containment

P1 — Within 24 hours: Isolate and preserve (DO NOT REVOKE TOKENS YET)

  • Isolate affected hosts: Immediately isolate any developer workstation or CI/CD runner that executed the payload or opened an affected repository from the network. Network isolation is safe — without connectivity, the dead-man's switch cannot receive an HTTP 4xx response and will not fire.
  • Do not power off: Preserve volatile memory for forensic evidence.
  • Identify exposure: Check lockfiles and node_modules for the compromised name/version set (including transitive dependencies like eslint → file-entry-cache → flat-cache → keyv). Check for the presence of .claude/settings.json and .vscode/tasks.json hooks in cloned repositories.

P2 — Within 72 hours: Host cleanup & Dead-man's switch defusal

  • Extract and preserve handler: Locate and copy ~/.config/gh-token-monitor/{handler,token,started_at}, the payload files, and the plist/systemd unit. Record hashes. Do not execute the handler; treat it as inert text. Use the started_at file to bound your exposure window.
  • Disable persistence: Disable loginctl linger, remove the gh-token-monitor files, and remove the .claude/.vscode hooks. Clear package caches.
  • Revoke credentials: Once the dead-man's switch is neutralized on an isolated host, revoke and rotate all credentials exposed during the started_at window from a known-clean environment.

P3 — Within 7 days: Rebuild & Supply Chain Hardening

  • Rebuild systems: Rebuild CI/CD runners and any host with confirmed execution from trusted images. Arbitrary code ran; the list of known artifacts is not a completeness guarantee.
  • Harden npm configuration: Disable lifecycle scripts globally where possible (npm config set ignore-scripts true) and enforce this in CI/CD pipelines.
  • Audit IDE configurations: Treat .claude/, .cursor/, and .vscode/ directories as part of the execution surface. Implement pre-commit hooks or repository scanning to detect unauthorized SessionStart or folderOpen tasks.

5. Indicators of compromise

type value confidence source
file_path ~/.config/gh-token-monitor/ High SANS ISC
file_name Math_Symbol.js High Elastic Security Labs
file_name math_init.js High Elastic Security Labs
file_name setup.mjs High SANS ISC / Elastic
file_path .claude/settings.json High SANS ISC / Elastic
file_path .vscode/tasks.json High SANS ISC / Elastic
service_name GitHub Token Validity Monitor High SANS ISC
commit_author claude High Elastic Security Labs
commit_message chore: update config High Elastic Security Labs
package keyv@6.0.0 High SANS ISC / Socket
file_path  ~/.config/gh-token-monitor/
file_name  Math_Symbol.js
file_name  math_init.js
file_name  setup.mjs
file_path  .claude/settings.json
file_path  .vscode/tasks.json
service_name  GitHub Token Validity Monitor
commit_author  claude
commit_message  chore: update config
package  keyv@6.0.0

6. Detection

rule CHAINDROP_npm_worm_payload {
    meta:
        author = "Adverse Trace"
        date = "2026-08-05"
        reference = "https://isc.sans.edu/diary/rss/33218"
        description = "Detects CHAINDROP/Shai-Hulud npm worm payload and dropper components"
    strings:
        $preinstall = "\"preinstall\": \"node setup.mjs\"" ascii
        $claude_hook = "SessionStart" ascii
        $vscode_hook = "folderOpen" ascii
        $gh_monitor = "GitHub Token Validity Monitor" ascii
        $dune1 = "shai-hulud" ascii
        $dune2 = "sandworm" ascii
        $dune3 = "fedaykin" ascii
        $dune4 = "tleilaxu" ascii
        $dune5 = "sardaukar" ascii
        $dune6 = "ornithopter" ascii
        $dune7 = "navigator" ascii
        $dune8 = "mentat" ascii
        $dune9 = "ghola" ascii
        $dune10 = "kanly" ascii
        $dune11 = "laza" ascii
        $dune12 = "lasgun" ascii
        $dune13 = "sietch" ascii
    condition:
        $preinstall or $claude_hook or $vscode_hook or $gh_monitor or 5 of ($dune*)
}
title: Detect CHAINDROP npm Worm Persistence Mechanism
id: 7a9b3c2d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
status: experimental
description: Detects the creation of the gh-token-monitor directory or the systemd/LaunchAgent persistence used by the CHAINDROP npm worm.
author: Adverse Trace
date: 2026/08/05
references:

    - https://isc.sans.edu/diary/rss/33218
logsource:
    product: linux
    service: file_create
detection:
    selection_dir:
        TargetFilename|contains: '.config/gh-token-monitor/'
    selection_service:
        TargetFilename|endswith:

            - '.plist'
            - '.service'
        TargetFilename|contains: 'gh-token-monitor'
    condition: selection_dir or selection_service
falsepositives:

    - Unknown
level: high

7. Sources

  • SANS Internet Storm Center, "Don't Revoke That Token Yet: Inside the keyv/cacheable npm Worm," https://isc.sans.edu/diary/rss/33218, August 5, 2026.
  • Elastic Security Labs, "Shai-Hulud strikes again: CHAINDROP worm hits 400+ npm packages," https://www.elastic.co/security-labs/shai-hulud-chaindrop-npm-supply-chain, August 2026.
  • Microsoft Threat Intelligence, "ChainDrop supply chain compromise: Anatomy of a self-propagating worm," https://www.microsoft.com/en-us/security/blog/2026/08/04/chaindrop-supply-chain-compromise-anatomy-self-propagating-worm/, August 4, 2026.
  • Snyk Vulnerability Blog, "Inside the keyv npm Compromise: preinstall Malware, Trusted Provenance, and IDE Hooks," https://snyk.io/blog/inside-keyv-npm-compromise-preinstall-malware-trusted-provenance-ide-hooks/, August 2026.
  • The Hacker News, "Keyv-Linked npm Worm Poisons Hundreds of Packages, Plants Claude Code and VS Code Hooks," https://thehackernews.com/2026/08/keyv-linked-npm-worm-poisons-hundreds.html, August 2026.

8. Adverse Trace position

This campaign represents a high-severity supply chain compromise with novel execution and persistence mechanisms that directly threaten EMEA financial services development pipelines. The combination of IDE/AI-agent execution vectors and a dead-man's switch that punishes standard incident response (token revocation) requires a paradigm shift in how teams handle npm compromise triage. The rapid worm propagation and broad credential harvesting scope mean that any organization with a modern JavaScript stack must assume potential exposure. Adverse Trace is actively monitoring the expanding IOC list and will update client detection packages as new package hashes and versions are verified. We strongly advise clients to prioritize host isolation and dead-man's switch defusal before any credential rotation.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies