Gheist: 13,500 secret-dumping workflows planted with stolen GitHub tokens — audit yours in 5 minutes A five-week campaign dubbed Gheist used stolen GitHub tokens to plant secret-dumping workflows in 13,500 branches across more than 850 compromised accounts, including private enterprise repositories, according to an autopsy published by Wiz. The workflows dumped environment variables and secrets — including Anthropic, Hugging Face, Groq and NVIDIA API keys — to logs that were then fetched and deleted via the GitHub API, and Wiz confirmed follow-on AWS role assumption from stolen cloud credentials via CloudTrail. Public repositories were largely spared by GitHub's approval hold on first-time contributor workflow runs, a guardrail that does not apply to private repos or GitHub Enterprise Server. While everyone watched LiteLLM, a quieter campaign spent five weeks doing something worse: taking stolen GitHub tokens and committing a secret-dumping workflow into every repository they could reach — 13,500 planted branches across 850+ compromised accounts , personal projects and private enterprise repos included. Wiz published the full autopsy yesterday. The campaign is called Gheist, it dumps your org secrets, your cloud credentials, and — the line that made me write this — it specifically hunts AI provider keys: Anthropic, Hugging Face, Groq, NVIDIA . Your agents' keys, straight out of your CI. This post is the mechanics and the audit. The whole check runs in five minutes with gh , tonight, on your own account and orgs. Gheist's tooling takes a stolen token sourced from infostealer logs and prior supply-chain incidents — the same family that hit Trivy in March , enumerates every repo it can touch, and plants a branch ci-sync-XXXXXX containing one workflow, committed as ci: sync config , unsigned. The workflow is two dumb lines: dump env and toJSON secrets , base32-encoded , print to the workflow log. Then the tool fetches the log through GitHub's API and deletes the run. Four details make it clever, and all four are worth internalizing: $SECRET prints safely to the attacker. permissions: {} protects nothing here. secrets context. Half the hardening advice on the internet answers a different attack. On self-hosted runners the payload went further — searching for 31 token formats private keys, .env files, Terraform state, /proc/ /environ , Docker sockets, Kubernetes tokens and AI provider keys specifically. Wiz notes that variant is buggy — wrong runner paths, broken regex, no GCP/Azure collection — which is somehow worse: the campaign is a product under active iteration, shipping features. The single most interesting fact in the autopsy: public repos were largely saved by one quiet GitHub guardrail . Since July 28, workflow runs from first-time contributors in public repos sit in action required pending collaborator approval — and every public Gheist run Wiz checked sat there, payload never executed. That hold doesn't apply to private repos or GitHub Enterprise Server . There, the workflows ran, secrets were dumped, and Wiz proved follow-on AWS role assumption from the stolen cloud credentials via CloudTrail. The guardrail you never configured protected your open source; nothing protected the private stuff, which is where the secrets actually live. Two more findings worth the price of admission: permissions: {} on the malicious workflow was used by the attacker — cosmetically, since secrets don't care — and about two-thirds of the implicated accounts have since vanished from GitHub, suggesting response is underway while the campaign kept evolving through October 24. The radar: the commit message is searchable in public history, and it's noisy legit sync config commits everywhere , so treat it as a lead generator, not a verdict: gh api 'search/commits?q="ci: sync config"' --jq '.total count' → 321 hits right now. Most are innocent sync scripts. Your job is the branch check below. The actual self-audit — planted branches on your account loop your repos, grep the branch pattern : gh repo list --limit 200 --json nameWithOwner --jq '. .nameWithOwner' | while read r; do gh api "repos/$r/branches" --paginate --jq '. .name' 2 /dev/null | grep -E '^ci-sync- 0-9a-z {6}$' && echo " ↑ HIT in $r" done For organizations org owners only — the audit-log API isn't available on personal accounts; the web UI's security log is the personal fallback : Workflow runs deleted within a minute of completing — Gheist's cleanup signature gh api 'orgs/YOURORG/audit-log?include=all&phrase=workflow run' \ --jq '. | select .action=="workflow run.deleted" ' The tool's user agent gh api 'orgs/YOURORG/audit-log?include=all' --jq '. | select .user agent=="gheist" ' And the cloud-side hunt that works even without GitHub logs — Wiz's best trick: OIDC role assumptions whose session/subject contains ci-sync- GitHub's OIDC subject includes the ref, so the planted branch name travels into your CloudTrail : aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \ --start-time 2026-09-15 --max-results 20 | grep -i 'ci-sync' If anything hits: rotate every secret that repo could see — deleting runs doesn't un-dump them — revoke the pushing tokens, and check the token owners' machines for infostealers, because that's where the tokens came from. From Wiz's list, ranked by how little they cost: require reviewers on environment-scoped secrets kills the environment matrix ; scope OIDC trust policies to protected branches only kills ci-sync- role assumption ; prefer GitHub Apps or fine-grained, short-lived tokens over classic PATs shrinks what one stolen token reaches ; run self-hosted runners ephemerally, no Docker socket, no unrestricted metadata kills the host pivot . And never approve a held sync-check run sitting in action required — that's the trap's front door. Gheist = stolen tokens + one two-line workflow + base32 + GitHub-as-C2, at a scale of 13,500 branches. Public repos got lucky via the first-contribution hold; private repos paid. The audit is five minutes of gh and one CloudTrail query — and if you run AI agents in CI, the campaign's shopping list literally names your provider. Check tonight. Sources: Wiz — Gheist: attempted scaled abuse of stolen GitHub tokens https://www.wiz.io/blog/gheist-attempted-scaled-abuse-of-stolen-github-tokens-stumbles · CISA KEV / alerts hub https://www.cisa.gov/news-events · commit-search audit via gh api commands above, verified live