Skip to content
audiotale.net
Sign in
NewsNews

Gheist: stolen GitHub tokens plant sync-check workflow to dump secrets, per Wiz

Wiz says the Gheist campaign has planted over 13,500 branches from 850+ compromised accounts; GitHub's hold blocks it in public repos, but not private ones.

audiotale.net team

· 7 min read

Written by AI from the sources linked below. How we use AI

On this page
  1. Key points
  2. What to do
  3. What happened
  4. Background
  5. The numbers
  6. What it means
  7. What we don't know yet
  8. What to watch next
  9. Sources

Security firm Wiz says a campaign it calls Gheist has been planting a credential-stealing GitHub Actions workflow named sync-check in every repository that a stolen GitHub token can reach. Wiz Research has tracked it since 15 September 2026 and, as of 24 October, calls it ongoing and evolving. GitHub's new approval hold is blocking it in public repositories, but Wiz reports that it has worked in private repositories and in GitHub Enterprise environments, where secrets were printed into workflow logs and then fetched through GitHub's own API.

Key points

  • Wiz Research has tracked Gheist since 15 September 2026 and has recovered more than 13,500 planted branches from more than 850 compromised accounts.
  • Each branch is named ci-sync- plus six random characters and adds one file, .github/workflows/sync-check.yml, with the commit message ci: sync config; the HTTP user agent of the tool is gheist.
  • The workflow prints the repository's secrets base32-encoded, which slips past GitHub's log masking, and the tool downloads the log and deletes the run seconds later.
  • Since 28 July 2026 GitHub holds potentially malicious workflow runs in public repositories for approval; Wiz says every public Gheist run it checked sits at action_required, so the payload never ran there.
  • That hold does not cover private repositories or GitHub Enterprise Server, and Wiz found audit-log and CloudTrail evidence of Gheist runs completing there.

What to do

Who is affected: anyone whose GitHub personal access token or other token with write access to repositories has been stolen, for example by an infostealer, and any organisation that uses private repositories or GitHub Enterprise Server with Actions enabled. Public repositories on github.com have GitHub's approval hold, which Wiz says stopped the payload there.

Hunt (indicators from Wiz):

  1. Look for branches matching ^ci-sync-[0-9a-z]{6}$ whose only change is a new .github/workflows/sync-check.yml, with the commit message ci: sync config.
  2. In GitHub audit logs, look for workflow runs on ci-sync-* branches that were deleted within a minute of finishing, and for any activity from the user agent gheist.
  3. If your workflows use OIDC to reach a cloud provider, check cloud audit logs for role assumptions whose subject includes a ci-sync- branch. That shows Actions ran on an attacker's branch even if you don't collect GitHub logs.
  4. On self-hosted runners, look for the files .gdmp_pat, .gdmp_patg and .gdmp_sweep and the string gdmp-pivot in job logs.

Respond: rotate every repository, organisation and environment secret that an affected repository could read, because deleting the branch or the run does not undo the exposure. Revoke the tokens that pushed the commits and check their owners' devices for infostealers. If a ci-sync branch ran on a self-hosted runner, Wiz advises treating that host and its instance role as compromised. Do not approve held sync-check runs.

Harden: require reviewers on environments that hold production secrets, scope OIDC trust to protected branches or environments, prefer GitHub Apps or short-lived fine-grained tokens over personal access tokens, and run self-hosted runners as ephemeral hosts without access to the Docker socket.

What happened

According to Wiz, the attacker's tool takes a stolen token and, for each repository the token can reach, creates a branch and commits the sync-check workflow, authored as the token's owner and unsigned. The push event triggers the workflow, which runs env and the toJSON(secrets) context through base32. The tool then downloads the log through the GitHub API and deletes the run.

Wiz describes three details that make this work. GitHub masks a secret in logs only when it appears verbatim, and a base32-encoded copy doesn't match. The workflow sets permissions: {}, which restricts the job's GITHUB_TOKEN (the attacker doesn't need it) but does not restrict the secrets context. And where a repository has deployment environments, the tool lists them in a job matrix, so environment-scoped secrets, often production credentials, are printed too.

Because the secrets leave through GitHub's own API, Wiz notes, there is no attacker domain or outbound connection for a defender to block.

Wiz saw two variants running side by side. "Tool A" uses a hexadecimal branch suffix and the plain payload. "Tool B" uses a base36 suffix and, within hours of its first run on 28 September, added a six-minute timeout and the environment matrix. On 19 October Tool B began printing a separator line, LAFA====, between the dumps, which Wiz thinks helps the operator's log parser. Hours later it pushed a second job, sync-host, set to run on self-hosted runners, apparently only in repositories that have one registered.

That job is far more aggressive. Wiz lists what it tries to collect: host details and crontab; a recursive search for 31 token formats (including keys for AI providers such as Anthropic, Hugging Face, Groq and NVIDIA), private keys, .env files, Terraform state and shell history; the environment of every process; a container started through the Docker socket with the host's root filesystem mounted; AWS instance-role credentials; GitHub Actions and GitLab runner credential files; and the Kubernetes service account token. Wiz says the job is buggy: it looks for the wrong runner key file, its URL-credential pattern is broken and it doesn't collect GCP or Azure tokens.

In enterprise organisations, Wiz saw the tool cycle through the tokens of several users and automation accounts within minutes, clone private repositories before planting the workflow, and, hours later, cloud credentials from the same organisation used to enumerate AWS Secrets Manager.

Background

The defence that worked in public repositories is new. GitHub's changelog of 28 July 2026 says recent supply-chain attacks use compromised GitHub credentials to push malicious workflows that steal CI/CD credentials, so Actions now holds certain runs "for approval before they start". A collaborator with write access must approve a held run through an authenticated web session. It needs no configuration, and GitHub says it applies to public repositories on github.com only; GitHub Enterprise Server doesn't get it "at this time".

The attack also leans on a known weakness. GitHub's own secure-use documentation says that because secrets can be transformed in several ways, "automatic redaction is not guaranteed", and that any user with write access to a repository has read access to all of its secrets. It recommends required reviewers for environment secrets, so that a job cannot read them until a reviewer approves, and OIDC instead of long-lived cloud credentials.

Wiz says the stolen tokens trace back to infostealer logs and to tokens exposed in earlier supply-chain compromises, such as the Trivy compromise in March. It stresses that Gheist is distinct from GhostAction, a different campaign that uses other workflow files and sends secrets to a fixed server address.

The numbers

Wiz's counts show how widely the same pool of compromised accounts is being used. The figures below are the ones Wiz reports for itself and for overlapping activity, all measured in accounts.

type: bar
title: Compromised GitHub accounts in Wiz's Gheist findings
unit: accounts
source: Wiz Research, Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles
highlight: Gheist accounts recovered
Gheist accounts recovered (more than) | 850
Accounts that renamed repos to lure names, 6 Oct | 391
Gheist accounts also hit by Megalodon in May | 57
Accounts that made RDP runner repos, 18 Oct | 25

Besides the 850 accounts, Wiz recovered more than 13,500 planted branches. About two thirds of the implicated accounts were no longer available on GitHub as of 24 October, which Wiz says likely indicates response activity after it reported the campaign to GitHub.

Wiz also found the same accounts used by other actors. On 6 October, 391 accounts renamed about 1,461 repositories to cracked-software lures such as fl-studio-crack and solana-sniper-lite, which wiped 610 ci-sync branches as a side effect; 166 of those accounts never appear in Gheist activity. On 18 October, 25 Gheist accounts each created a "Windows RDP Runner" repository within nine minutes, apparently to get free compute. Some victims appear to be infected with PolinRider, malware that Wiz attributes to North Korea-linked actors and that hides JavaScript in build configuration files; three victims re-pushed their Gheist commits with a loader appended to vite.config.js.

What it means

For defenders, the lesson is that a stolen developer token is a path to production secrets even when no code is vulnerable. The workflow needs no exploit: it relies on write access plus an Actions secret store that any writer can read. GitHub's approval hold removes that path for public repositories, but private repositories, where the most valuable credentials tend to sit, remain exposed, and Wiz says audit logs and CloudTrail show runs completing on ci-sync-* branches there.

Teams that rely on long-lived personal access tokens look most exposed, in our reading. The mitigations GitHub already documents (environment reviewers, OIDC with branch or environment conditions, least-privilege tokens) are the same ones Wiz recommends, and they work against this attack because the attacker's branch would not satisfy the policy.

What we don't know yet

  • Who is behind Gheist. Wiz does not name an actor, and notes that the same compromised accounts are also used by other actors and campaigns.
  • How many private repositories and enterprise organisations were affected, and how many secrets were exposed. Wiz gives counts of planted branches and accounts, not of victims whose secrets were taken.
  • Whether GitHub will extend the approval hold to private repositories or GitHub Enterprise Server. Its changelog says only that Enterprise Server isn't covered "at this time".
  • This research comes from a single vendor. We found no independent write-up of Gheist at the time of writing, so the figures above are Wiz's own.

What to watch next

Watch for GitHub widening the approval hold beyond public repositories, for further variants of the self-hosted job once its bugs are fixed, and for indicator updates in Wiz's Threat Center advisory. Wiz says the attack is ongoing, so rotating exposed secrets sooner rather than later matters.

Our related coverage: how AGENTS.md files in a Downloads folder can hijack coding agents shows another route by which developer machines become the entry point.

Sources

Written by

audiotale.net team

The official account of the people who run audiotale.net: announcements, conversation starters and the newsroom's drafts. Posts written with AI say so. Every other account here is a real person.