{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "keep-as-periodic-hunt",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "The CHAINDROP worm automates the compromise of secondary npm packages using stolen credentials; early detection of C2 discovery prevents exfiltration of sensitive secrets and stops the spread of supply-chain infections."
      },
      "name": "CHAINDROP: C2 Discovery and Worm Propagation",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1041",
        "attack.t1195.002",
        "attack.t1555"
      ],
      "series": {
        "slug": "shai-hulud-strikes-again-chaindrop-worm-hits-400-npm-packages",
        "index": 2,
        "title": "Shai-Hulud strikes again: CHAINDROP worm hits 400+ npm packages",
        "total": 2
      },
      "related": [
        {
          "hunt": "chaindrop-dropper-execution-and-harvesting",
          "reason": "The initial dropper execution and credential harvesting happen before C2 discovery and propagation.",
          "relation": "precedes"
        },
        {
          "hunt": "chaindrop-host-worm-execution",
          "relation": "follows"
        }
      ],
      "targets": {
        "github": {
          "name": "github",
          "category": "siem",
          "huntbase": {
            "product": "github"
          }
        },
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "network": {
          "name": "Network telemetry",
          "category": "network",
          "telemetry": [
            "network"
          ]
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "analysis": "A static rule can catch known C2 domains, but this hunt pivots between suspicious software inventory, rare blockchain-related DNS queries, and unauthorized source control activity from new contributors. It identifies dynamic C2 domains resolved via smart contracts which a single rule cannot anticipate.",
      "coverage": [
        {
          "stage": "c2-discovery-and-exfiltration",
          "steps": [
            "rpc-discovery-baseline",
            "c2-exfiltration-leads"
          ],
          "status": "covered"
        },
        {
          "stage": "worm-propagation",
          "steps": [
            "github-worm-commits"
          ],
          "status": "covered"
        },
        {
          "stage": "initial-access-npm-package",
          "reason": "Handled by the first hunt in this series focusing on the initial install hook.",
          "status": "out_of_scope"
        },
        {
          "stage": "execution-via-dropper",
          "reason": "Handled by the first hunt in this series focusing on the dropper and bun execution.",
          "status": "out_of_scope"
        },
        {
          "stage": "credential-harvesting",
          "reason": "Handled by the first hunt in this series focusing on the collector component.",
          "status": "out_of_scope"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "Trojanized npm Package Installation",
            "slug": "initial-access-npm-package",
            "tactic": "initial-access",
            "techniques": [
              "T1195.002"
            ],
            "observables": [
              "keyv",
              "flat-cache",
              "cacheable-request",
              "cacheable",
              "cache-manager",
              "preinstall hook in package.json",
              "setup.mjs"
            ]
          },
          {
            "name": "Multi-Path Dropper Execution",
            "slug": "execution-via-dropper",
            "tactic": "execution",
            "techniques": [
              "T1059.001"
            ],
            "observables": [
              "node setup.mjs",
              "bun v1.3.13",
              "Math_Symbol.js",
              "math_init.js",
              ".claude/settings.json SessionStart hook",
              ".vscode/tasks.json folderOpen task",
              "9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc",
              "fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb",
              "54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668"
            ]
          },
          {
            "name": "Developer and AI Credential Harvesting",
            "slug": "credential-harvesting",
            "tactic": "credential-access",
            "techniques": [
              "T1555"
            ],
            "observables": [
              "Anthropic",
              "Claude",
              "Codex",
              "Cursor",
              "OpenAI",
              "Gemini",
              "npm tokens",
              "GitHub PATs",
              "Kubernetes service account tokens",
              "HashiCorp Vault tokens"
            ]
          },
          {
            "name": "Dynamic C2 Discovery and Data Exfiltration",
            "slug": "c2-discovery-and-exfiltration",
            "tactic": "command-and-control",
            "techniques": [
              "T1041"
            ],
            "observables": [
              "0xE1f2395ee43e45A1556EC6438a88c31B83493103",
              "go.getblock.io",
              "eth.llamarpc.com",
              "awqhnjewqjkl.icu",
              "npm-cache.com",
              "thebeautifulmarchoftime",
              "Shai-Hulud: Here We Go Again"
            ]
          },
          {
            "name": "Self-Propagating Supply Chain Infection",
            "slug": "worm-propagation",
            "tactic": "persistence",
            "techniques": [
              "T1195"
            ],
            "observables": [
              "claude@users.noreply.github.com",
              "chore: update config",
              "IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients",
              "bypass_2fa"
            ]
          }
        ],
        "summary": "The Shai-Hulud campaign, also known as CHAINDROP, targets JavaScript developers via compromised npm maintainer accounts to deploy a self-propagating worm. The malware executes via npm hooks to steal cloud, AI, and developer credentials, then uses stolen npm tokens to automatically backdoor and republish all packages owned by the victim."
      },
      "severity": "high",
      "rationale": "Focus on developer workstations and build servers that have npm installed. Use the software inventory to prioritize hosts with the known-affected dependencies.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An attacker has infected local development environments via trojanized npm packages and is using Ethereum smart contracts to discover C2 infrastructure before propagating the worm using stolen GitHub credentials.",
      "parameters": {
        "c2_domains": {
          "from": {
            "ref": "elastic-security-labs",
            "kind": "article",
            "observed": "2026-08-06"
          },
          "type": "list[domain]",
          "default": [
            "awqhnjewqjkl.icu",
            "npm-cache.com"
          ],
          "description": "Known exfiltration and C2 domains."
        },
        "rpc_domains": {
          "from": {
            "ref": "elastic-security-labs",
            "kind": "article",
            "observed": "2026-08-06"
          },
          "type": "list[domain]",
          "default": [
            "go.getblock.io",
            "eth.llamarpc.com",
            "eth-mainnet.nodereal.io",
            "mainnet.infura.io",
            "eth-mainnet.g.alchemy.com",
            "rpc.ankr.com",
            "nodes.lala.xyz",
            "cloudflare-eth.com"
          ],
          "description": "Ethereum RPC providers used by the worm for C2 discovery."
        },
        "scope_hosts": {
          "from": {
            "ref": "analyst-scoping",
            "kind": "manual",
            "observed": "2026-08-06"
          },
          "type": "list[host]",
          "default": [],
          "description": "Narrow the hunt to specific hosts; leave empty to hunt across the whole estate."
        },
        "npm_packages": {
          "from": {
            "ref": "elastic-security-labs",
            "kind": "article",
            "observed": "2026-08-06"
          },
          "type": "list[string]",
          "default": [
            "keyv",
            "flat-cache",
            "cacheable-request",
            "cacheable",
            "cache-manager"
          ],
          "description": "NPM packages known to be initially compromised."
        },
        "lookback_days": {
          "from": {
            "ref": "hunt-config",
            "kind": "manual",
            "observed": "2026-08-06"
          },
          "type": "number",
          "default": "14",
          "description": "Days of history to examine."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://www.elastic.co/security-labs/threat-command/shai-hulud-chaindrop-npm-supply-chain",
          "gates": [
            "dry-run",
            "lint"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://www.elastic.co/security-labs/threat-command/shai-hulud-chaindrop-npm-supply-chain",
          "name": "Elastic Security Labs \u2014 Shai-Hulud strikes again: CHAINDROP worm hits 400+ npm packages"
        }
      ],
      "blind_spots": [
        {
          "id": "rpc-visibility-gap",
          "risk": "Without visibility into DNS queries for RPC providers, the primary C2 discovery mechanism remains invisible.",
          "owner": "Infrastructure Team",
          "stage": "c2-discovery-and-exfiltration",
          "question": "whether a host is querying Ethereum nodes to resolve C2 infrastructure",
          "requires": "Endpoint DNS monitoring or network-level DNS logging (hb_dns_activity)",
          "remediation": "Enable DNS logging on endpoint agents and forward resolver logs to the central surface."
        },
        {
          "id": "github-token-audit-delay",
          "risk": "Delays in GitHub API ingestion mean the worm could propagate before the hunt identifies the commit.",
          "owner": "AppSec Team",
          "stage": "worm-propagation",
          "question": "how quickly a malicious package update is detected after publication",
          "requires": "Real-time GitHub package audit events (github_package_version)",
          "remediation": "Configure GitHub Webhooks to alert on package publication events."
        }
      ]
    },
    "name": "CHAINDROP: C2 Discovery and Worm Propagation",
    "description": "This hunt targets the post-exploitation phase of the CHAINDROP worm. It focuses on identifying hosts that perform dynamic C2 discovery by querying Ethereum RPC nodes and detecting unauthorized GitHub package updates from accounts with no prior history for those packages. By correlating host-level network behavior with identity-level source control activity, we identify compromised developer workstations and secondary supply-chain infection attempts."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "series": {
          "slug": "shai-hulud-strikes-again-chaindrop-worm-hits-400-npm-packages",
          "index": 2,
          "title": "Shai-Hulud strikes again: CHAINDROP worm hits 400+ npm packages",
          "total": 2
        },
        "coverage": [
          {
            "stage": "c2-discovery-and-exfiltration",
            "steps": [
              "rpc-discovery-baseline",
              "c2-exfiltration-leads"
            ],
            "status": "covered"
          },
          {
            "stage": "worm-propagation",
            "steps": [
              "github-worm-commits"
            ],
            "status": "covered"
          },
          {
            "stage": "initial-access-npm-package",
            "reason": "Handled by the first hunt in this series focusing on the initial install hook.",
            "status": "out_of_scope"
          },
          {
            "stage": "execution-via-dropper",
            "reason": "Handled by the first hunt in this series focusing on the dropper and bun execution.",
            "status": "out_of_scope"
          },
          {
            "stage": "credential-harvesting",
            "reason": "Handled by the first hunt in this series focusing on the collector component.",
            "status": "out_of_scope"
          }
        ],
        "rationale": "An attacker has infected local development environments via trojanized npm packages and is using Ethereum smart contracts to discover C2 infrastructure before propagating the worm using stolen GitHub credentials.",
        "blind_spots": [
          {
            "id": "rpc-visibility-gap",
            "risk": "Without visibility into DNS queries for RPC providers, the primary C2 discovery mechanism remains invisible.",
            "owner": "Infrastructure Team",
            "stage": "c2-discovery-and-exfiltration",
            "question": "whether a host is querying Ethereum nodes to resolve C2 infrastructure",
            "requires": "Endpoint DNS monitoring or network-level DNS logging (hb_dns_activity)",
            "remediation": "Enable DNS logging on endpoint agents and forward resolver logs to the central surface."
          },
          {
            "id": "github-token-audit-delay",
            "risk": "Delays in GitHub API ingestion mean the worm could propagate before the hunt identifies the commit.",
            "owner": "AppSec Team",
            "stage": "worm-propagation",
            "question": "how quickly a malicious package update is detected after publication",
            "requires": "Real-time GitHub package audit events (github_package_version)",
            "remediation": "Configure GitHub Webhooks to alert on package publication events."
          }
        ],
        "scoping_notes": "Focus on developer workstations and build servers that have npm installed. Use the software inventory to prioritize hosts with the known-affected dependencies.",
        "beyond_detection": "A static rule can catch known C2 domains, but this hunt pivots between suspicious software inventory, rare blockchain-related DNS queries, and unauthorized source control activity from new contributors. It identifies dynamic C2 domains resolved via smart contracts which a single rule cannot anticipate."
      }
    },
    {
      "id": "affected-npm-inventory",
      "type": "query",
      "label": "Inventory of affected npm packages",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "endpoint",
        "content": "SELECT DISTINCT device_hostname, package_name, package_version FROM hb_software_inventory WHERE package_type = 'npm' AND (instr(',' || '{{npm_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0)",
        "surface": "hb_software_inventory",
        "description": "Identify hosts that have the compromised versions of key packages installed to narrow the scope of network monitoring.",
        "expected_signal": "A list of hosts running the targeted packages. Silence suggests no direct use of the known-compromised libraries."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Inventory of affected npm packages",
        "reads": [
          "device_hostname",
          "package_name",
          "package_version",
          "package_type"
        ],
        "source": "hb_software_inventory",
        "target": "endpoint",
        "content": "SELECT DISTINCT device_hostname, package_name, package_version FROM hb_software_inventory WHERE package_type = 'npm' AND (instr(',' || '{{npm_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0)",
        "silence": "not_evidence_of_absence",
        "expected": "A list of hosts running the targeted packages. Silence suggests no direct use of the known-compromised libraries.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "rpc-discovery-baseline",
      "type": "query",
      "label": "Baseline Ethereum RPC DNS lookups",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "endpoint",
        "content": "SELECT LOWER(query_hostname) AS rpc_domain, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_dns_activity WHERE (instr(',' || '{{rpc_domains}}' || ',', ',' || LOWER(query_hostname) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY rpc_domain HAVING hosts <= 5",
        "surface": "hb_dns_activity",
        "description": "Find hosts querying blockchain RPC providers to resolve C2 domains from the Ethereum smart contract.",
        "expected_signal": "Rare connections to blockchain RPC nodes from developer machines indicate discovery phase. High counts on many hosts may signify legitimate development."
      },
      "parents": [
        {
          "id": "affected-npm-inventory"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Baseline Ethereum RPC DNS lookups",
        "reads": [
          "query_hostname",
          "device_hostname",
          "time"
        ],
        "source": "hb_dns_activity",
        "target": "endpoint",
        "content": "SELECT LOWER(query_hostname) AS rpc_domain, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_dns_activity WHERE (instr(',' || '{{rpc_domains}}' || ',', ',' || LOWER(query_hostname) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY rpc_domain HAVING hosts <= 5",
        "silence": "not_evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "Rare connections to blockchain RPC nodes from developer machines indicate discovery phase. High counts on many hosts may signify legitimate development.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "rpc_domain"
          ],
          "rare_below": 5
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "c2-exfiltration-leads",
      "type": "query",
      "label": "Direct network connections to rare C2 domains",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "network",
        "content": "SELECT LOWER(dst_endpoint_hostname) AS c2_domain, COUNT(DISTINCT device_hostname) AS hosts, GROUP_CONCAT(DISTINCT device_hostname) AS device_list FROM hb_network_connection WHERE (instr(',' || '{{c2_domains}}' || ',', ',' || LOWER(dst_endpoint_hostname) || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY c2_domain HAVING hosts <= 3",
        "surface": "hb_network_connection",
        "description": "Identify active exfiltration by monitoring for rare connections to known C2 and dead-drop domains unique to scoped hosts.",
        "expected_signal": "Direct hits to rare infrastructure confirming successful discovery and exfiltration. Higher host counts indicate unrelated noise."
      },
      "parents": [
        {
          "id": "affected-npm-inventory"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "Direct network connections to rare C2 domains",
        "reads": [
          "device_hostname",
          "process_name",
          "dst_endpoint_hostname",
          "dst_endpoint_ip",
          "time"
        ],
        "source": "hb_network_connection",
        "target": "network",
        "content": "SELECT LOWER(dst_endpoint_hostname) AS c2_domain, COUNT(DISTINCT device_hostname) AS hosts, GROUP_CONCAT(DISTINCT device_hostname) AS device_list FROM hb_network_connection WHERE (instr(',' || '{{c2_domains}}' || ',', ',' || LOWER(dst_endpoint_hostname) || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY c2_domain HAVING hosts <= 3",
        "silence": "not_evidence_of_absence",
        "expected": "Direct hits to rare infrastructure confirming successful discovery and exfiltration. Higher host counts indicate unrelated noise.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "c2_domain"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "github-worm-commits",
      "type": "query",
      "label": "New GitHub package contributor activity",
      "config": {
        "dsl": "sqlite",
        "role": "triage",
        "source": "github",
        "content": "SELECT package_name, author, MIN(created_at) AS first_published_at, html_url FROM github_package_version GROUP BY package_name, author HAVING first_published_at >= datetime('now', '-{{lookback_days}} days')",
        "product": "github",
        "surface": "github_package_version",
        "extension": "github",
        "description": "Detect worm propagation by identifying package versions published by accounts with no historical presence for that specific package.",
        "expected_signal": "Package versions published by first-time authors for that repository indicate potential account takeover or automated worm propagation."
      },
      "parents": [
        {
          "id": "affected-npm-inventory"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "triage",
        "label": "New GitHub package contributor activity",
        "reads": [
          "author",
          "package_name",
          "created_at",
          "html_url"
        ],
        "source": "github_package_version",
        "target": "github",
        "content": "SELECT package_name, author, MIN(created_at) AS first_published_at, html_url FROM github_package_version GROUP BY package_name, author HAVING first_published_at >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Package versions published by first-time authors for that repository indicate potential account takeover or automated worm propagation.",
        "verified": "dry-run",
        "verified_at": "2026-09-20",
        "target_extension": "github"
      }
    },
    {
      "id": "triage-signals",
      "type": "analytic",
      "label": "Triage discovery and propagation",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint",
          "github",
          "network"
        ],
        "context": [
          "affected-npm-inventory",
          "rpc-discovery-baseline",
          "c2-exfiltration-leads",
          "github-worm-commits"
        ],
        "objective": "Determine if any host with the affected npm packages is engaging in Ethereum-based C2 discovery or if GitHub accounts associated with the organization are publishing malicious package versions via new contributor accounts.",
        "description": "Synthesize network and supply-chain evidence into a per-host verdict.",
        "max_iterations": 4,
        "expected_signal": "A detailed assessment citing RPC connections and unauthorized commits.",
        "success_criteria": "A detailed verdict citing host-to-RPC connections or unauthorized GitHub commits."
      },
      "parents": [
        {
          "id": "rpc-discovery-baseline",
          "kind": "merge"
        },
        {
          "id": "c2-exfiltration-leads",
          "kind": "merge"
        },
        {
          "id": "github-worm-commits",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-verdict",
      "type": "checkpoint",
      "label": "Route based on infection evidence",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the agent finds evidence of rare C2 discovery (RPC DNS) or GitHub propagation (new contributor package versions) for any host or account",
        "condition": "the agent finds evidence of rare C2 discovery (RPC DNS) or GitHub propagation (new contributor package versions) for any host or account",
        "blind_spot": "rpc-visibility-gap",
        "confidence": "high",
        "description": "Act on confirmed infection or escalate to analyst review.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "triage-signals"
        }
      ]
    },
    {
      "id": "contain-and-revoke",
      "type": "action",
      "label": "Isolate host and revoke credentials",
      "config": {
        "target": "endpoint",
        "description": "Contain the breach.",
        "instructions": "Isolate the compromised host from the network and revoke all GitHub and npm tokens associated with the user. Check the GitHub repository for unauthorized commits.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "manual-audit",
      "type": "task",
      "label": "Manual remediation and audit",
      "config": {
        "assignee": "analyst",
        "description": "Clean up source control and verify extent of propagation.",
        "instructions": "Audit GitHub package logs for versions published by identified accounts. Verify if other internal developer accounts were used to propagate the worm. Review metadata of suspicious versions for the intimidation string."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "default"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_unavailable"
        },
        {
          "id": "contain-and-revoke"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Hunt close-out",
      "config": {
        "assignee": "analyst",
        "description": "Document and finalize hunt results.",
        "instructions": "Document findings, update list of malicious C2 domains if new ones were resolved, and verify all affected hosts have been cleaned."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_refutes"
        },
        {
          "id": "manual-audit"
        }
      ]
    }
  ]
}