Insights

The Silent Shift: Analyzing the Evolution of Modern Supply Chain Attacks

An in-depth analysis of the transition from noisy lifecycle scripts to builder hooks, interpreter startup configurations, cross-ecosystem runtimes, and self-propagating worms.

#supply-chain#npm#pypi#automation#evasion

Introduction

Software supply chain security has graduated from a theoretical concern to a highly sophisticated cat-and-mouse game. Over the past year, threat intelligence gathered on Halting Problems has documented a profound transformation in how attackers compromise package registries, target developers, and persist in cloud and CI/CD infrastructure.

This post analyzes the telemetry, techniques, and structures of recent supply chain waves—specifically contrasting campaigns like Miasma/Phantom Gyp and the Hades Cluster—to extract actionable insights into how attacker techniques are evolving to bypass modern defensive boundaries.


1. Bypassing Script Restrictions: From Lifecycle Hooks to Builder Configurations

Historically, the majority of malicious packages found on npm or PyPI relied on explicit installer hooks—such as npm preinstall/postinstall scripts or Python setup.py executions—to run their payloads. Because defenders responded by monitoring these hooks or running package installations with flags like --ignore-scripts, attackers adapted.

The Phantom Gyp Technique

Disclosed by Snyk in June 2026, the Phantom Gyp wave demonstrates this shift. Instead of leveraging standard package-manager lifecycle scripts, the malware abuses the native addon compilation process. By placing malicious shell commands or Javascript execution commands inside a target’s binding.gyp file, the attacker triggers execution during the normal node-gyp rebuild process.

Because compiling native addons is a standard phase of Node.js package resolution, this bypasses naive --ignore-scripts configurations and evades detection rules looking for explicit hook triggers in package.json.


2. Interpreter-Level Persistence: Python *.pth Startup Hooks

In the Python ecosystem, the entry point for package execution has historically been an import statement (e.g., import compromised_pkg). However, the Hades Cluster campaign, discovered in June 2026, utilized a technique that targets the Python interpreter itself.

Abusing Site-Path Configuration Files

The attackers weaponized .pth (Path Configuration) files. By including a custom *-setup.pth file inside the PyPI wheel directory, the malware triggers auto-execution. When Python initializes, it parses every .pth file in its search path (site-packages) and executes any line starting with import.

# Conceptual example of a malicious .pth auto-execution hook
import sys, subprocess; subprocess.Popen([sys.executable, "-c", "import os; ..."])

As a result:

  • The malware executes automatically when the interpreter starts (e.g., when launching a development server, running a script, or opening an interactive REPL).
  • No import or invocation of the compromised package is required.
  • Standard static analysis checking for unused imports or checking package entry points is completely bypassed.

3. Cross-Ecosystem Payloads: Bootstrapping Alternative Runtimes

Traditional malware payloads are usually written in the host language of the compromised package (e.g., Python for PyPI, JavaScript for npm). However, recent campaigns have introduced cross-ecosystem execution runtimes to enhance evasion and cross-platform compatibility.

In the Hades Cluster and Mini Shai-Hulud PyPI campaigns, the malicious Python wheels carried an embedded executable or downloaded the Bun JavaScript runtime. The Python startup hook then launched Bun to run a heavily obfuscated JavaScript payload (_index.js).

Why Attackers Blend Runtimes:

  1. Obfuscation: JavaScript obfuscators (like those utilizing AST transformations or custom packing) are highly mature and can render payload logic nearly unreadable to standard Python static analysis tools.
  2. Feature Parity: Using a single JavaScript codebase allows attackers to share the same stealer logic across both npm and PyPI campaigns, reducing development overhead.
  3. Telemetry Evasion: Many endpoint detection tools monitor standard python processes for outbound network calls; wrapping execution in a dynamically fetched runtime changes the process tree and evasion signature.

4. Self-Propagating Registry Worms (The Miasma Model)

Perhaps the most alarming evolution is the shift from passive, target-specific stealers to active registry worms. The Miasma campaign (which compromised packages under namespaces like @redhat-cloud-services and @vapi-ai) functions as an automated propagation engine.

graph TD
    A[Infected Developer Installs Compromised Package] --> B[Worm Steals npm/PyPI Publish Tokens from Host]
    B --> C[Worm Scrapes Developer's Local Repositories]
    C --> D[Worm Modifies Local Package Files & binding.gyp]
    D --> E[Worm Auto-Publishes Infected Version to Registry]
    E --> F[New Victim Installs Updated Package]
    F --> A

The Infection Loop:

  1. The developer installs a compromised dependency.
  2. The installation-time hook harvests registry publishing tokens (e.g., ~/.npmrc tokens or PyPI API keys) and local Git SSH keys.
  3. The malware uses the harvested tokens to scan the developer’s accessible repositories and package list.
  4. It injects its malicious hooks into the developer’s own packages and immediately publishes a new minor/patch version back to the public registry.

This creates an exponential propagation loop that bypasses standard registry review mechanisms since the updates are published using legitimate, compromised maintainer credentials.


5. Targeting Federated Identity and CI/CD Memory

In modern cloud environments, developers have shifted from static API keys to federated identity providers (such as GitHub Actions OIDC federation to AWS/GCP/Azure). In response, attackers have shifted their post-compromise target areas.

In the actions-cool tag-hijack campaign, the malicious commits did not just look for files containing static environment keys. Instead, they targeted running processes:

  • The payload monitored the GitHub Actions runner Runner.Worker memory.
  • By scraping process memory dynamically, it extracted temporary OIDC authentication tokens and repository scoped secrets (GITHUB_TOKEN) before they expired or were cleared.
  • Attackers then leveraged these temporary tokens to push commits to other repositories or assume IAM roles on downstream cloud resources.

Conclusion: Defensive Implications

The evolution of these attacks shows that software supply chain defense can no longer focus solely on static manifest verification. Defenders must implement:

  1. Strict Lifecycle Control: Disable native build execution during resolution where native addons are not required (e.g., using --ignore-scripts combined with targeted allowances for verified native packages).
  2. Behavioral Endpoint Monitoring: Alert on interpreter processes (like Python or Node) spawning compiler processes (node-gyp), downloading runtime binaries (Bun), or reading runner memory.
  3. Immutable Tag Enforcement: Never resolve dependencies to mutable tags (like GitHub Actions @v2). Always pin actions to full SHA-1 hashes to prevent tag-hijacking and imposter commit injection.
  4. Token Isolation: Limit the scope of publishing tokens to single packages and enforce Multi-Factor Authentication (MFA) for publishing operations where supported.