{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "keep-as-periodic-hunt",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "Build-time supply chain attacks bypass production SBOMs and runtime security. A negative result verifies that authorized developer systems are not executing rare, unauthorized setup hooks that harvest secrets."
      },
      "name": "Build-Time Execution and Secret Harvesting",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1195.002",
        "attack.t1059.003",
        "attack.t1105",
        "attack.t1059.007",
        "attack.t1003",
        "attack.t1528",
        "attack.t1552.004"
      ],
      "series": {
        "slug": "connecting-the-dots-securing-the-overlooked-corners-of-the-software-development-lifecycle-sdlc-s",
        "index": 1,
        "title": "Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain",
        "total": 2
      },
      "related": [
        {
          "hunt": "developer-tool-configuration-tampering",
          "reason": "This hunt focuses on build-time execution and harvesting; persistence via backdooring VS Code or Claude Code configurations belongs to a separate investigation.",
          "relation": "out-of-scope-alternative"
        }
      ],
      "targets": {
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "analysis": "A simple detection rule for 'npm install' or 'preinstall' is too noisy for developer environments. This hunt stacks child processes across the fleet to find rare hooks and correlates that rarity with credential file access, providing the context an analyst needs to judge a supply chain compromise.",
      "coverage": [
        {
          "stage": "malicious-npm-hook-execution",
          "steps": [
            "rare-build-child-processes"
          ],
          "status": "covered"
        },
        {
          "stage": "payload-delivery-bun-runtime",
          "steps": [
            "rare-build-child-processes",
            "triage-sdlc-exposure"
          ],
          "status": "covered"
        },
        {
          "stage": "cicd-secret-memory-scraping",
          "reason": "Memory-based scraping is not visible on standard process or file surfaces; partially addressed by monitoring sensitive file access instead.",
          "status": "not_visible",
          "blind_spot": "missing-memory-telemetry"
        },
        {
          "stage": "developer-tool-backdooring",
          "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "blockchain-based-c2",
          "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "automated-worm-propagation",
          "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
          "status": "out_of_scope"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "Malicious npm preinstall hook execution",
            "slug": "malicious-npm-hook-execution",
            "tactic": "execution",
            "techniques": [
              "T1195.002",
              "T1059.003"
            ],
            "observables": [
              "npm install",
              "preinstall scripts in package.json",
              "npm hooks"
            ]
          },
          {
            "name": "Payload delivery via Bun runtime",
            "slug": "payload-delivery-bun-runtime",
            "tactic": "execution",
            "techniques": [
              "T1105",
              "T1059.007"
            ],
            "observables": [
              "Bun runtime download",
              "727 KB obfuscated payload",
              "Background payload launch"
            ]
          },
          {
            "name": "CI/CD and endpoint secret harvesting",
            "slug": "cicd-secret-memory-scraping",
            "tactic": "credential-access",
            "techniques": [
              "T1003",
              "T1528",
              "T1552.004"
            ],
            "observables": [
              "Python script reading live process memory",
              "GitHub Actions runner memory scraping",
              "OpenID Connect (OIDC) tokens",
              "Local developer credentials sweep"
            ]
          },
          {
            "name": "Persistence via developer tool configuration",
            "slug": "developer-tool-backdooring",
            "tactic": "persistence",
            "techniques": [
              "T1546"
            ],
            "observables": [
              "VS Code tasks.json modification",
              "Claude Code cross-linked hooks"
            ]
          },
          {
            "name": "Blockchain command and control",
            "slug": "blockchain-based-c2",
            "tactic": "command-and-control",
            "techniques": [
              "T1102"
            ],
            "observables": [
              "Ethereum blockchain transactions",
              "Dynamic C2 infrastructure"
            ]
          },
          {
            "name": "Automated package propagation",
            "slug": "automated-worm-propagation",
            "tactic": "lateral-movement",
            "techniques": [
              "T1534",
              "T1195.002"
            ],
            "observables": [
              "Stolen npm tokens",
              "Stolen GitHub tokens",
              "Republishing infected packages (keyv, cacheable-request)",
              "Creation of rogue repositories"
            ]
          }
        ],
        "summary": "Attackers target the software supply chain by poisoning package dependencies with malicious preinstall hooks that execute during development and build processes. The ChainDrop worm specifically scrapes CI/CD runner memory for OIDC tokens and local credentials to establish persistence in developer tools like VS Code and automatically propagate by infecting and republishing additional packages."
      },
      "severity": "high",
      "rationale": "The hunt scopes using software inventory to find hosts where development tools are installed. Focus the lookback on recent dependency updates or pipeline runs.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An attacker has compromised a software dependency to execute malicious code during the build phase, subsequently harvesting cloud and developer credentials from the environment's configuration files.",
      "parameters": {
        "scope_hosts": {
          "from": {
            "ref": "analyst-scoping",
            "kind": "manual",
            "observed": "2026-08-21"
          },
          "type": "list[host]",
          "default": [],
          "description": "Optional list of hostnames to focus on (e.g. known CI/CD runners); leave empty for the whole estate."
        },
        "lookback_days": {
          "from": {
            "ref": "standard-retention",
            "kind": "manual",
            "observed": "2026-08-21"
          },
          "type": "number",
          "default": "14",
          "description": "Days of history to examine."
        },
        "sensitive_paths": {
          "from": {
            "ref": "unit42-sdlc",
            "kind": "article",
            "observed": "2026-08-21"
          },
          "type": "list[path]",
          "default": [
            "/home/runner/.npmrc",
            "/root/.npmrc",
            "/home/runner/.aws/credentials",
            "/root/.aws/credentials",
            "/home/runner/.ssh/id_rsa",
            "/root/.ssh/id_rsa",
            "tasks.json",
            ".git-credentials"
          ],
          "description": "Specific credential and configuration file paths commonly targeted by harvesters."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://unit42.paloaltonetworks.com/sdlc-supply-chain/",
          "gates": [
            "dry-run",
            "lint",
            "critic"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://unit42.paloaltonetworks.com/sdlc-supply-chain/",
          "name": "Unit 42 \u2014 Connecting the Dots: Securing the Overlooked Corners of the SDLC Supply Chain"
        }
      ],
      "blind_spots": [
        {
          "id": "missing-memory-telemetry",
          "risk": "Harvesters like ChainDrop can read OIDC tokens directly from memory without touching sensitive configuration files on disk, bypassing file-based detection.",
          "stage": "cicd-secret-memory-scraping",
          "question": "Was a script reading the memory of the GitHub runner process directly?",
          "requires": "Process memory access telemetry (CrossProcessHandle)"
        },
        {
          "id": "ephemeral-runner-logs",
          "risk": "Short-lived CI/CD runners may execute a hook and be terminated before the agent can flush its event buffer, leading to missed execution signals.",
          "stage": "malicious-npm-hook-execution",
          "question": "Did the telemetry reach the platform before the ephemeral build host was destroyed?",
          "requires": "Centralized, real-time telemetry forwarding for ephemeral CI/CD runners"
        }
      ]
    },
    "name": "Build-Time Execution and Secret Harvesting",
    "description": "Attackers are increasingly targeting the software build process rather than the application code. This hunt identifies the initial execution of malicious lifecycle hooks (like npm preinstall), the delivery of unauthorized runtimes like Bun, and the subsequent harvesting of sensitive credentials. By scoping to development environments and stacking rare child processes spawned by package managers, we identify anomalous behaviors that static bill-of-materials scans miss."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "series": {
          "slug": "connecting-the-dots-securing-the-overlooked-corners-of-the-software-development-lifecycle-sdlc-s",
          "index": 1,
          "title": "Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain",
          "total": 2
        },
        "coverage": [
          {
            "stage": "malicious-npm-hook-execution",
            "steps": [
              "rare-build-child-processes"
            ],
            "status": "covered"
          },
          {
            "stage": "payload-delivery-bun-runtime",
            "steps": [
              "rare-build-child-processes",
              "triage-sdlc-exposure"
            ],
            "status": "covered"
          },
          {
            "stage": "cicd-secret-memory-scraping",
            "reason": "Memory-based scraping is not visible on standard process or file surfaces; partially addressed by monitoring sensitive file access instead.",
            "status": "not_visible",
            "blind_spot": "missing-memory-telemetry"
          },
          {
            "stage": "developer-tool-backdooring",
            "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "blockchain-based-c2",
            "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "automated-worm-propagation",
            "reason": "Belongs to another part of the 'Connecting the Dots: Securing the Overlooked Corners of the Software Development Lifecycle (SDLC) Supply Chain' series.",
            "status": "out_of_scope"
          }
        ],
        "rationale": "An attacker has compromised a software dependency to execute malicious code during the build phase, subsequently harvesting cloud and developer credentials from the environment's configuration files.",
        "blind_spots": [
          {
            "id": "missing-memory-telemetry",
            "risk": "Harvesters like ChainDrop can read OIDC tokens directly from memory without touching sensitive configuration files on disk, bypassing file-based detection.",
            "stage": "cicd-secret-memory-scraping",
            "question": "Was a script reading the memory of the GitHub runner process directly?",
            "requires": "Process memory access telemetry (CrossProcessHandle)"
          },
          {
            "id": "ephemeral-runner-logs",
            "risk": "Short-lived CI/CD runners may execute a hook and be terminated before the agent can flush its event buffer, leading to missed execution signals.",
            "stage": "malicious-npm-hook-execution",
            "question": "Did the telemetry reach the platform before the ephemeral build host was destroyed?",
            "requires": "Centralized, real-time telemetry forwarding for ephemeral CI/CD runners"
          }
        ],
        "scoping_notes": "The hunt scopes using software inventory to find hosts where development tools are installed. Focus the lookback on recent dependency updates or pipeline runs.",
        "beyond_detection": "A simple detection rule for 'npm install' or 'preinstall' is too noisy for developer environments. This hunt stacks child processes across the fleet to find rare hooks and correlates that rarity with credential file access, providing the context an analyst needs to judge a supply chain compromise."
      }
    },
    {
      "id": "find-sdlc-environments",
      "type": "query",
      "label": "Identify development and build hosts",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "endpoint",
        "content": "SELECT DISTINCT device_hostname, package_name, package_version FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%npm%' OR LOWER(package_name) LIKE '%node%' OR LOWER(package_name) LIKE '%bun%' OR LOWER(package_name) LIKE '%python%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)",
        "surface": "hb_software_inventory",
        "description": "Scope the hunt to systems where package managers or runtimes are present, representing the SDLC attack surface.",
        "expected_signal": "A list of hostnames with development tools. Silence indicates no such software is inventoried, making the hunt non-applicable."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Identify development and build hosts",
        "reads": [
          "device_hostname",
          "package_name",
          "package_version"
        ],
        "source": "hb_software_inventory",
        "target": "endpoint",
        "content": "SELECT DISTINCT device_hostname, package_name, package_version FROM hb_software_inventory WHERE (LOWER(package_name) LIKE '%npm%' OR LOWER(package_name) LIKE '%node%' OR LOWER(package_name) LIKE '%bun%' OR LOWER(package_name) LIKE '%python%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)",
        "silence": "not_evidence_of_absence",
        "expected": "A list of hostnames with development tools. Silence indicates no such software is inventoried, making the hunt non-applicable.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "rare-build-child-processes",
      "type": "query",
      "label": "Stack-count rare build child processes",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "endpoint",
        "content": "SELECT process_cmd_line, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%npm%' OR LOWER(parent_process_name) LIKE '%node%' OR LOWER(parent_process_name) LIKE '%bun%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_cmd_line HAVING hosts <= 3 ORDER BY hosts ASC",
        "surface": "hb_process_activity",
        "description": "Identify anomalous processes spawned by package managers, which can reveal malicious lifecycle hooks or secondary payloads.",
        "expected_signal": "Command lines involving 'preinstall' scripts or rare secondary runtimes seen on very few hosts. Silence suggests no unusual hook execution occurred."
      },
      "parents": [
        {
          "id": "find-sdlc-environments"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Stack-count rare build child processes",
        "reads": [
          "process_cmd_line",
          "device_hostname",
          "parent_process_name",
          "time"
        ],
        "source": "hb_process_activity",
        "target": "endpoint",
        "content": "SELECT process_cmd_line, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%npm%' OR LOWER(parent_process_name) LIKE '%node%' OR LOWER(parent_process_name) LIKE '%bun%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY process_cmd_line HAVING hosts <= 3 ORDER BY hosts ASC",
        "silence": "not_evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "Command lines involving 'preinstall' scripts or rare secondary runtimes seen on very few hosts. Silence suggests no unusual hook execution occurred.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "process_cmd_line"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "secret-file-access",
      "type": "query",
      "label": "Credential and configuration file access",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "endpoint",
        "content": "SELECT device_hostname, process_name, file_path, time FROM hb_file_activity WHERE instr(',' || '{{sensitive_paths}}' || ',', ',' || LOWER(file_path) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_file_activity",
        "description": "Find file activity targeting sensitive paths by developer tools or their child processes.",
        "expected_signal": "Access to .npmrc, .aws/credentials, or tasks.json by non-standard processes. Silence means no direct file-level harvesting was observed."
      },
      "parents": [
        {
          "id": "rare-build-child-processes"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "Credential and configuration file access",
        "reads": [
          "device_hostname",
          "process_name",
          "file_path",
          "time"
        ],
        "source": "hb_file_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, process_name, file_path, time FROM hb_file_activity WHERE instr(',' || '{{sensitive_paths}}' || ',', ',' || LOWER(file_path) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Access to .npmrc, .aws/credentials, or tasks.json by non-standard processes. Silence means no direct file-level harvesting was observed.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "triage-sdlc-exposure",
      "type": "analytic",
      "label": "Weigh SDLC build-time evidence",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint"
        ],
        "context": [
          "find-sdlc-environments",
          "rare-build-child-processes",
          "secret-file-access"
        ],
        "objective": "Determine if the rare child processes spawned by npm, node, or bun indicate a malicious build-time execution event and whether those processes accessed sensitive credentials.",
        "description": "Correlate rare build processes with secret file access to identify supply chain compromises.",
        "max_iterations": 4,
        "expected_signal": "A per-host verdict citing specific rare hooks and the files they accessed.",
        "success_criteria": "A per-host verdict of malicious | suspicious | benign citing rows and linking process rarity to credential access."
      },
      "parents": [
        {
          "id": "secret-file-access"
        }
      ]
    },
    {
      "id": "route-exposure",
      "type": "checkpoint",
      "label": "Route on SDLC verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "The triage verdict identifies malicious lifecycle hooks or unauthorized secret harvesting on any developer host.",
        "condition": "The triage verdict identifies malicious lifecycle hooks or unauthorized secret harvesting on any developer host.",
        "blind_spot": "missing-memory-telemetry",
        "confidence": "high",
        "description": "Direct the analyst based on the findings from the agent's triage.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "triage-sdlc-exposure"
        }
      ]
    },
    {
      "id": "remediation-review",
      "type": "task",
      "label": "Credential rotation and hook review",
      "config": {
        "assignee": "analyst",
        "description": "Harden the developer environment and invalidate any potentially stolen tokens.",
        "instructions": "For any host with suspicious activity, rotate all local developer credentials (SSH, AWS, npm) and OIDC tokens. Review the package.json of the affected project to identify the malicious dependency. Consider implementing --ignore-scripts policy."
      },
      "parents": [
        {
          "id": "route-exposure",
          "branch": "on_supports"
        },
        {
          "id": "route-exposure",
          "branch": "default"
        },
        {
          "id": "route-exposure",
          "branch": "on_unavailable"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Hunt close-out",
      "config": {
        "assignee": "analyst",
        "description": "Document findings and baseline legitimate hooks.",
        "instructions": "Record the baseline of common preinstall hooks used in your business to reduce noise for future hunts. Document any coverage gaps found during the process."
      },
      "parents": [
        {
          "id": "route-exposure",
          "branch": "on_refutes"
        },
        {
          "id": "remediation-review"
        }
      ]
    }
  ]
}