Gheist GitHub Token Attack: How Stolen Tokens Steal Secrets From Private Repos and How to Stop It 7 min read

Gheist GitHub Token Attack: How Stolen Tokens Steal Secrets From Private Repos and How to Stop It

A new campaign called Gheist is turning stolen GitHub tokens into a quiet pipeline for cloud secrets. Security researchers at Wiz have linked it to more than 850 compromised GitHub accounts and over 13,500 malicious branches. The attackers do not need malware on your servers or a command server of their own. They only need one working token and GitHub Actions.

Gheist GitHub Token Attack: How Stolen Tokens Steal Secrets From Private Repos and How to Stop It

If your team stores secrets in GitHub, especially in private repositories, you should check your accounts today.

What Is the Gheist Campaign?

Gheist is an automated attack that abuses stolen GitHub tokens. The name comes from the user agent gheist, which its tool sends with every GitHub API request. For each token, the tool finds every repository the token can reach, plants a workflow, collects the secrets that workflow prints and wipes its traces. Researchers have tracked it since September 15, and it keeps changing.

Gheist Attack at a Glance

Detail What we know
Campaign name Gheist (from the user agent its tool sends)
First tracked September 15
Scale 13,500+ planted branches, 850+ accounts
Main target Private repos and GitHub Enterprise Server
Branch pattern ci-sync- plus six random characters
Workflow file .github/workflows/sync-check.yml
Commit message ci: sync config
Status Still active and changing
Companies hit Roughly a dozen, by Wiz's estimate

How Attackers Steal the GitHub Tokens

Gheist does not break into GitHub itself. It reuses tokens that already leaked elsewhere. Wiz traced them to two main sources:

  • Infostealer malware running on developer machines, which grabs saved tokens and browser sessions.
  • Earlier supply chain breaches, including the Trivy scanner compromise in March, which exposed credentials in bulk.

An old personal access token often still carries full write rights to every repo its owner can reach. That makes one stolen token far more valuable than most teams assume.

How the Gheist Attack Works Step by Step

The tool follows a short, automated routine for every repository a token can open.

  1. It creates a new branch named ci-sync- with a six-character random tail.
  2. It commits one workflow file called sync-check.yml. The commit carries no signature and shows the token owner as the author, so it looks like normal work.
  3. The push triggers the workflow automatically. The workflow dumps environment variables and repository secrets into its own run log.
  4. It encodes the secrets in Base32. GitHub hides secrets in logs only when they appear exactly as stored. Encoded text slips past that mask and prints in full.
  5. It downloads the log through the GitHub API and deletes the run seconds later.

Because the data leaves through GitHub's own API, defenders see no strange domain and no unusual outbound connection. Firewalls and egress rules have nothing to block.

If a repo uses deployment environments, the tool lists them and pulls environment-scoped secrets too. Those are often production keys.

Why Private Repositories Stay Exposed

GitHub added a safety hold on July 28. Since then, it pauses suspicious workflow runs in public repositories until a collaborator approves them. Wiz checked public Gheist runs and found every one stuck waiting for approval. The payload never ran there.

That hold does not cover private repositories or GitHub Enterprise Server. In those environments, the planted workflows ran, finished and vanished. Many teams treat "private" as safe. Gheist shows that a private label means nothing once an attacker holds a valid token with write access.

In enterprise accounts, Wiz watched the tool rotate through several user and bot tokens within minutes and clone private repos before planting the workflow. A few hours later, someone used cloud credentials from the same companies to list secrets in AWS Secrets Manager.

Gheist Variants and the Self-Hosted Runner Payload

Wiz spotted two variants running side by side. You can tell them apart by the branch suffix:

  • Tool A uses hexadecimal characters and a basic payload.
  • Tool B uses base36 characters, adds a six-minute timeout, reads deployment environments and tailors its payload to each repo.

On October 19, Tool B gained a second job aimed at self-hosted runners. It scans the host for dozens of token formats, including API keys for AI services like Anthropic, Hugging Face, Groq and NVIDIA. It also hunts for private keys, .env files, Terraform state, shell history, cloud metadata credentials and Kubernetes tokens. It even uses the Docker socket to mount the host filesystem.

The good news: the researchers found this job buggy. It looks for the wrong runner key file, its credential regex breaks, and it fails to grab GCP and Azure tokens. Still, nobody should count on attackers staying careless.

Other Malware Campaigns Using the Same Accounts

The stolen accounts do not belong to Gheist alone. Wiz tied them to several other operations:

  • Malware lures: On October 6, 391 accounts renamed around 1,461 repos to fake cracked-software names. That rename wiped 610 Gheist branches by accident.
  • Free compute abuse: On October 18, 25 Gheist accounts each spun up a "Windows RDP Runner" repo within nine minutes.
  • PolinRider: Some victims show signs of this DPRK-linked malware, which hides JavaScript loaders in build config files such as vite.config.js.
  • Megalodon: 57 early Gheist accounts also fell to the Megalodon campaign in May.

This overlap suggests attackers trade or resell stolen developer tokens. One leak can feed several groups at once.

Do not confuse Gheist with GhostAction. That separate campaign uses different workflow files and sends stolen data to an external server.

Gheist Indicators of Compromise

Use these markers in your repository searches and log queries.

Indicator Value
User agent gheist
Branch name ci-sync- plus six characters, matching ^ci-sync-[0-9a-z]{6}$
Workflow file .github/workflows/sync-check.yml
Commit message ci: sync config
Self-hosted runner files .gdmp_pat, .gdmp_patg, .gdmp_sweep
Job log string gdmp-pivot
Cloud log signal OIDC role assumption with a ci-sync- branch in the subject

A match on the branch name and workflow file together means the attacker workflow reached that repository. Move straight to the response steps below.

How to Check if Your Repos Were Hit

Run these checks across every org and repo you manage:

  • Search for branches matching ^ci-sync-[0-9a-z]{6}$ where the only change adds sync-check.yml.
  • Search commit history for the message ci: sync config.
  • Review GitHub audit logs for runs on ci-sync-* branches that someone deleted within a minute of finishing.
  • Look for the user agent gheist in API activity.
  • Check cloud audit logs if your workflows use OIDC. A role assumption with a ci-sync- branch in its subject proves the attacker workflow ran, even without GitHub logs.
  • Inspect self-hosted runners for the files .gdmp_pat, .gdmp_patg and .gdmp_sweep, and for the string gdmp-pivot in job logs.

What to Do if You Find a ci-sync Branch

Deleting the branch or the run does not undo anything. The secrets already left. Take these steps:

  1. Rotate every secret the affected repo could reach, at the repository, organization and environment level.
  2. Revoke the tokens that pushed the bad commits.
  3. Scan the token owners' computers for infostealer malware. Otherwise, fresh tokens will leak again.
  4. Treat any self-hosted runner that executed a ci-sync branch as fully compromised, along with its cloud instance role.
  5. Never approve a held sync-check run, and never run code from these branches.

How to Protect GitHub Tokens and Secrets

These changes shrink the damage any single stolen token can cause:

  • Replace long-lived personal access tokens with GitHub Apps or short-lived fine-grained tokens.
  • Require reviewers on any environment that holds production secrets.
  • Limit OIDC trust policies to protected branches or specific environments, so a random branch cannot assume your cloud role.
  • Run self-hosted runners as throwaway hosts with no Docker socket access and locked-down metadata endpoints.
  • Audit who and what can push to each repo, and rotate credentials after every security incident.

Gheist proves that a developer token is a key to your cloud, not just to your code. GitHub's new approval hold already stopped the attack in public repos, but private and enterprise repos remain open to anyone holding a valid token. Hunt for the ci-sync- branches now, rotate what was exposed and retire long-lived tokens before the next campaign finds them.

Frequently Asked Questions

What is Gheist?

Gheist is an automated campaign that uses stolen GitHub tokens to plant workflows that dump repository secrets into logs, then downloads and deletes those logs.

Are public GitHub repositories safe from Gheist?

Mostly yes. GitHub's approval hold, added July 28, stops these runs in public repos until a collaborator approves them. Private repos and GitHub Enterprise Server lack that hold.

Why can't firewalls stop it?

The stolen data leaves through GitHub's own API. No attacker domain or odd outbound traffic appears for a firewall to block.

Does deleting the malicious branch fix the problem?

No. Once the workflow runs, the secrets are gone. You must rotate them and revoke the tokens involved.

How did attackers get the tokens?

Mainly from infostealer malware on developer machines and from credentials exposed in earlier supply chain breaches.

Was this guide helpful?
Fdaytalk

The Fdaytalk editorial team tests games, apps and devices and writes clear, step-by-step fixes and guides.

More from Fdaytalk →