Has the Gheist campaign stolen secrets from my GitHub repositories, and what should I do?
Gheist uses stolen GitHub tokens with write access to push a ci-sync branch whose workflow prints every Actions secret into the run log, then downloads the log and deletes the run within a minute of it finishing. It works on private repositories and GitHub Enterprise Server. There is nothing to patch: look for ci-sync branches, rotate every secret those repositories could read and revoke the tokens.
By Simon Phillips, CTO. Reviewed by Oliver Legg, Head of SOC and IR. Last updated 26 October 2026, 10:40 GMT.
At a Glance
Based on security researchers' analysis of the campaign as reported on 26 October 2026, and on GitHub's own changelog and documentation. Last updated 26 October 2026, 10:40 GMT.
Active and changing
Security researchers have tracked it since 15 September 2026. It was still running and changing as of 24 October.
13,500+ branches
Planted across more than 850 compromised accounts, from personal projects to private repositories of enterprise organisations.
Any repo a token can write to
Private repositories on github.com and GitHub Enterprise Server. GitHub's hold on suspicious runs covers public github.com repositories only.
Rotate and revoke
There is no flaw to patch. Rotate every secret an affected repository could read, revoke the tokens and check their owners' devices.
GitHub starts holding potentially malicious workflow runs for approval, on public github.com repositories only.
Security researchers begin tracking ci-sync branches that dump secrets into workflow logs.
Around this date a new job appears that hunts for credentials on self-hosted runners. It is buggy and no successful run has been reported.
Still active as of 24 October. Search for ci-sync branches, rotate secrets and revoke the tokens behind them.
How the attack works
- The attacker uses a stolen GitHub token with write access. Researchers traced tokens to infostealer malware logs and to earlier supply chain leaks such as the March compromise of the Trivy vulnerability scanner.
- It pushes a branch named
ci-sync-plus six characters, with a single unsigned commit,ci: sync config, that adds.github/workflows/sync-check.ymlunder the token owner's name. - The workflow prints the job environment and every secret the repository can read, encoded in base32 so log masking does not catch it. One version runs a job per deployment environment, so environment secrets, often production ones, are printed too.
- The workflow sets an empty permissions block. That limits the built-in GITHUB_TOKEN but does not stop it reading secrets.
- The tool downloads the log through the GitHub API and deletes the run within a minute of it finishing. Its API requests carry a user agent containing
gheist. There is no attacker server and no outbound connection to block. - In enterprise organisations the tool cycled through tokens from several users and automation accounts within minutes and cloned private repositories.
What to Do in the Next 24 Hours
Proactive on the left. Reactive on the right. Tick both tracks.
Find and contain
- Search every GitHub organisation you or your clients run for ci-sync branches.
- Do not approve held sync-check runs, and do not run code from ci-sync branches.
- Add required reviewers to environments that hold production secrets.
- List every person, app and token with write access.
Rotate and revoke
- Rotate every repository, organisation and environment secret an affected repository could read.
- Revoke the tokens that pushed ci-sync commits and check their owners' devices for infostealers.
- Treat any self-hosted runner that ran a ci-sync job, and its cloud instance role, as compromised.
- Remember that deleting the branch or the run does not undo the exposure.
Proactive: Make a Stolen Token Less Useful
There is nothing to patch. The goal is to find exposure fast and limit what any one token can reach.
Look for ci-sync branches
Check every repository you or your clients run, including private repositories and GitHub Enterprise Server. With the GitHub CLI, for one repository:
gh api --paginate "repos/OWNER/REPO/branches?per_page=100" --jq '.[].name' | grep -E '^ci-sync-[0-9a-z]{6}$'Or across a whole organisation:
gh repo list ORG --limit 1000 --json nameWithOwner --jq '.[].nameWithOwner' |
while read -r r; do
gh api --paginate "repos/$r/branches?per_page=100" --jq '.[].name' |
grep -E '^ci-sync-[0-9a-z]{6}$' | sed "s|^|$r |"
doneOn GitHub Enterprise Server, set GH_HOST to your server first. Any match needs the reactive steps below, even if the run has gone.
Put a reviewer in front of production secrets
Add required reviewers to every deployment environment that holds production secrets. GitHub documents that a job cannot access environment secrets until a reviewer approves it. Never approve a held or pending sync-check run.
Tie cloud OIDC trust to protected branches or environments
If workflows sign in to your cloud with OpenID Connect, make the trust policy accept only protected branches or named environments, never any branch. For example, an AWS trust policy condition such as:
"token.actions.githubusercontent.com:sub": "repo:ORG/REPO:environment:production"Combined with required reviewers, a pushed ci-sync branch can then no longer assume the role.
Cut down write access and long-lived tokens
Anyone with write access to a repository can read every secret configured in it, so audit write access across people, automation accounts and integrations, and remove what is not needed. Prefer GitHub Apps or short-lived fine-grained tokens over classic personal access tokens.
Harden runners and developer machines
Run self-hosted runners as short-lived machines, without access to the Docker socket and without open access to cloud metadata. Check developer machines for infostealer malware, because stolen tokens are the entry point for this campaign.
Free Detection Pack
Built by the CybaVerse threat hunt team. Check repositories and self-hosted runners you or your clients run for signs of this campaign. Everything is read-only.
check-gheist-repos.sh / Check-GheistRepos.ps1
Read-only checkers that use the GitHub API to look across an organisation, a user or a list of repositories for ci-sync branches, sync-check.yml or the known workflow hashes, and for runs on ci-sync branches. Runs held for approval are flagged so nobody approves them. They only send GET requests and never download run logs or print secrets.
Use a fine-grained token or GitHub App token with read-only Metadata, Contents and Actions access. On GitHub Enterprise Server, set GITHUB_API_URL to your server's API address.
export GITHUB_TOKEN=... # read-only token
./check-gheist-repos.sh --org my-org
./check-gheist-repos.sh --repo owner/repo
pwsh ./Check-GheistRepos.ps1 -Org my-orgcheck-gheist-runner.sh / Check-GheistRunner.ps1
Run locally on each self-hosted runner, as the runner's service user or as root for full coverage. They report any .gdmp_* helper files (path, size, owner and time, never contents), files such as runner logs containing the gdmp-pivot header or the payload marker, and workflow files matching a known hash.
sudo ./check-gheist-runner.sh
./check-gheist-runner.sh --path /opt/runners
pwsh ./Check-GheistRunner.ps1 -Path D:\runnersKQL hunts and Sigma rules
Seven KQL hunts covering ci-sync workflow runs, runs deleted within a minute of finishing, the gheist user agent, token pivots, OIDC role assumptions from ci-sync branches, Secrets Manager access afterwards and runner artefacts. Eight Sigma files cover the same ground for other platforms.
Verdicts
- CLEANNo indicators found in what the checker could see.
- INDICATORS FOUNDA ci-sync branch, sync-check workflow, known hash, held run or runner artefact was found. Start the reactive steps.
- UNKNOWNThe check was incomplete, for example no access to a repository, rate limiting or an unreadable folder.
- ERRORBad usage, a missing or rejected token, a network failure or a missing tool.
Exit codes
| Code | Meaning |
|---|---|
| 0 | CLEAN |
| 1 | INDICATORS FOUND |
| 2 | UNKNOWN, nothing found |
| 3 | ERROR |
MSPs and internal IT teams can use the exit code in RMM, CI or scheduled jobs to flag organisations and runners at scale. A finding outranks an unknown, and an unknown outranks clean.
Know the limits
- The KQL has not been run against a live tenant, and the GitHub audit log field names in the KQL and Sigma are unverified. Validate table and field names in your own environment before relying on any result.
- A deleted branch or run is invisible to the GitHub API, so CLEAN does not prove a repository was never hit. Use the audit log hunts as well.
- The cloud hunts only see ci-sync branches when your OIDC trust allows branch-based subjects. Environment-based subjects need a wider review.
- The pack contains the indicator strings, so it will match itself if you scan it.
Reactive: How to Hunt
Gheist leaves consistent traces in branches, audit logs, cloud logs and runners. CybaOps can run these hunts across your estate or your clients'.
Confirm the commit
For each ci-sync branch, check the commit. A match has the message ci: sync config, is unsigned and only adds .github/workflows/sync-check.yml.
gh api "repos/OWNER/REPO/commits/ci-sync-xxxxxx" \
--jq '.commit.message, .commit.verification.verified, .files[].filename'Then compare the workflow file's blob SHA with the known values in the IOC table below:
gh api "repos/OWNER/REPO/contents/.github/workflows/sync-check.yml?ref=ci-sync-xxxxxx" --jq .shaThe list is not exhaustive: versions that list deployment environments differ per repository, so a different SHA does not clear the branch.
Search the GitHub audit log
Look for activity whose user agent contains gheist (the full value has not been published), and for workflow runs on ci-sync branches that were deleted within a minute of finishing. Runs can last several minutes, so measure from when the run finished, not when it started. In private repositories and on GitHub Enterprise Server, this pattern of a run completing and then being deleted is how the campaign shows up.
Check cloud sign-ins from workflows
In your cloud logs, such as AWS CloudTrail, look for OIDC role assumptions whose subject contains ci-sync-. The default subject only includes the branch when the job does not use a deployment environment, so also review environment-based sign-ins around the same times. Then look for unusual use of any credentials those roles or secrets could reach, including listing or reading secrets stores.
Check self-hosted runners
On any self-hosted runner that could have run a ci-sync job, look for the helper files .gdmp_pat, .gdmp_patg and .gdmp_sweep. gdmp-pivot is not a file: it is a header the job writes to its log, so search job logs and runner diagnostic logs for it. The runner job searches for 31 types of credential, including private keys, process environments, cloud instance role credentials, runner credential files and Kubernetes service account tokens.
find / -xdev \( -name .gdmp_pat -o -name .gdmp_patg -o -name .gdmp_sweep \) 2>/dev/null
grep -rl "gdmp-pivot" /path/to/actions-runner/_diag 2>/dev/nullReplace the runner path with your own install folder. Exported job logs from GitHub are worth searching too, if you still have them.
Indicators of compromise
Three of the four workflow hashes were independently reproduced by rebuilding the published workflow text. The hash for the self-hosted runner version is as reported and has not been independently verified. The list is not exhaustive, because versions that list deployment environments differ per repository, so rely on the branch name, file, commit message and user agent first.
| Type | Indicator |
|---|---|
| Branch name | ^ci-sync-[0-9a-z]{6}$, with a single commit that only adds .github/workflows/sync-check.yml |
| Commit | Message ci: sync config, unsigned, carrying the token owner's name |
| Workflow content | toJSON(secrets) }}' | base32 -w 120 |
| GitHub audit log | User agent containing gheist (the full value has not been published); runs on ci-sync branches deleted within a minute of finishing |
| Cloud logs | OIDC role assumptions whose subject contains ci-sync- |
| Self-hosted runners | Files .gdmp_pat, .gdmp_patg and .gdmp_sweep on the runner |
| Job logs | Header ===== gdmp-pivot: written to the job log by the self-hosted runner job (a log line, not a file) |
| Workflow blob SHA | f3da2b8b357dcda02fd455d314be2a95007516a6 (Plain workflow, reproduced) |
| Workflow blob SHA | d09535857a741bbb976bdc084a9a021713133291 (Timeout version, reproduced) |
| Workflow blob SHA | 1dc5af9cce950aad1eb1546ecce8565b117540c4 (Separator version, reproduced) |
| Workflow blob SHA | c665b778b2d822453293003e7ca0bbb2cb08e39d (Version with the self-hosted runner job, reported but not independently verified) |
If you find a ci-sync branch
- Rotate every repository, organisation and environment secret the repository could read. Rotation is the only thing that ends the exposure.
- Revoke the tokens that pushed the ci-sync commits and check their owners' devices for infostealer malware.
- Treat any self-hosted runner that ran a ci-sync job as compromised, along with its cloud instance role. Rebuild it rather than cleaning it.
- Do not approve held sync-check runs and do not run code from ci-sync branches.
- Review cloud activity in the hours after the run. In affected enterprise organisations, cloud credentials from the same organisation were used to enumerate AWS Secrets Manager hours later. The research does not say where those credentials came from or who used them, so check every secrets store the exposed credentials could reach.
- Keep the evidence. Deleting the branch or the run does not undo the exposure, so export audit log entries first.
Gheist FAQ
Short answers based on public reporting and GitHub guidance as of 26 October 2026.
Is Gheist a GitHub vulnerability?
No. There is no software flaw and nothing to patch. Gheist abuses stolen tokens that already have write access, and GitHub documents that any user with write access to a repository can read all of the secrets configured in it. The fix is to rotate exposed secrets, revoke the stolen tokens and reduce what a single token can reach.
Are private repositories and GitHub Enterprise Server affected?
Yes. Since 28 July 2026 GitHub holds potentially malicious workflow runs for approval, but only on public repositories on github.com. GitHub Enterprise Server does not add this protection, and private repositories are not covered, so in those environments the workflow runs and the secrets are exposed.
Does deleting the ci-sync branch or the workflow run fix it?
No. The secrets were exposed the moment the workflow printed them, and the attacker downloads the log as soon as the run finishes. Deleting the branch or the run removes evidence but not the exposure. Rotate every secret the repository could read and revoke the token that pushed the branch.
Why does GitHub log masking not hide the secrets?
The workflow encodes every secret in base32 before printing it. GitHub's own guidance says that because a secret can be transformed in many ways, automatic redaction is not guaranteed, so encoded values can appear in the log in readable form.
Where do the stolen tokens come from?
Security researchers traced them to infostealer malware logs and to tokens exposed in earlier software supply chain attacks, such as the March compromise of the Trivy vulnerability scanner. Check the devices of every developer whose token pushed a ci-sync branch.
What should MSPs and internal IT teams do first?
Search every GitHub organisation you or your clients run for branches matching ci-sync- followed by six characters. Where you find one, rotate the secrets that repository could read, revoke the token that pushed it and check its owner's device. Give priority to people and automation accounts with write access across many repositories or clients.
Detect and Remediate GitHub Token Abuse Across Your Estate
CybaOps monitors your endpoints, cloud accounts and developer platforms, whether that's your own business or your clients'. UK-based analysts monitor alerts 24/7, run these threat hunts and act on pre-approved response actions, and CybaOps drives remediation through to a verified fix, so proactive checks and reactive response happen in one place.
Sources
- The Hacker News: Gheist uses stolen GitHub tokens to steal secrets (26 October 2026)
- GitHub changelog: GitHub Actions holds potentially malicious workflows for approval (28 July 2026)
- GitHub Docs: Secure use reference for GitHub Actions
Related threat guides: Railway environment variable breach and LiteLLM supply chain attack. All threat guides.