Skip to content

Back to the catalog

Introducing Secret Exfiltration Protection for GitHub Actions
stepsecurity.ioarticle

Introducing Secret Exfiltration Protection for GitHub Actions

StepSecurity breaks down how four campaigns, GhostAction, Megalodon, Miasma, and Hades, turned stolen GitHub tokens into mass secret theft by committing malicious GitHub Actions workflows to throwaway branches, using toJSON(secrets) to dump repo secrets and uploading them as run artifacts before deleting the branch and run. It explains why branch protection and code review miss this since the workflow runs on a push event to a non-default branch with no pull request ever opened, and introduces StepSecurity's Secret Exfiltration Policy plus a new Harden-Runner detection rule for the pattern.

TL;DR How GhostAction, Megalodon, Miasma and Hades exfiltrate GitHub Actions secrets via toJSON(secrets) on unreviewed throwaway branches, and StepSecurity's new policy/detection to stop it.
Document

What it is

StepSecurity's Secret Exfiltration Policy (configured under Workflow Run Policies), positioned against a shared "cash-out" step used by four GitHub Actions supply-chain campaigns: GhostAction, Megalodon, Miasma/Hades. The stolen developer token is only entry; the actual theft happens when the attacker commits a workflow that makes your own CI runner dump every secret and exfiltrate it as an artifact.

How it works (the attack)

The runner is the one place that legitimately holds all secrets at once, so the attacker turns it into the exfiltration tool. Hades:

  1. Clones the default branch, creates a throwaway branch named to blend in (e.g. main4, master2).
  2. Commits a workflow (named Run Copilot, disguised under a real path like .github/workflows/codeql.yml) triggered on push/deployment.
  3. Loads ${{ toJSON(secrets) }} into an env var, writes it to format-results.txt, uploads it as an artifact named format-results.
  4. Polls the run, downloads the artifact, then deletes the run and the branch — no residue.

Branch protection and code review both only fire on pull requests into the default branch. Because no PR is opened and the default branch is never touched, both controls are structurally bypassed. Even under review, workflow YAML gets less scrutiny and steps pinned to commit SHAs read as hardened.

Hunt indicators (from the payload)

  • Workflow/step names: Run Copilot, Github Actions Security, SysDiag, Optimize-Build; step named "Copilot Setup".
  • Artifact name format-results; file format-results.txt.
  • Short-lived branches (main4, master2) with runs and branches deleted shortly after.
  • Any workflow referencing toJSON(secrets).

What to do

Enable the Secret Exfiltration Policy under Workflow Run Policies. When on, a modified workflow in a non-default branch cannot use secrets unless explicitly approved — the run is canceled before any read, and a PR comment explains the block and approval path. Scope it per-repository or across the whole org. This directly covers the GhostAction/Megalodon/Hades scenario.

Caveats

The page describes a second detection layer that flags the exfiltration at runtime, but the fetched text is truncated before it is explained. The scale figures (GhostAction: 327 users / 817 repos / 3,325 secrets; Megalodon: 5,500+ repos in six hours; Miasma: 73 Azure-org repos disabled by GitHub) are StepSecurity's own reporting. Some dates are future-labeled (Megalodon May 2026, Miasma/Hades June 2026); treat the timeline as vendor-supplied.

Technologies named here

Techniques named here

  • Stolen developer token used to commit malicious workflow
  • Secret dump via toJSON
  • Artifact-based exfiltration
  • Branch protection bypass via non-default branch push
  • Action pinning to commit SHAs to appear legitimate
  • Evidence deletion
  • Credential harvesting via AI coding tool configuration files

Threat actors named here