Attackers with stolen GitHub tokens have been planting secret-dumping Actions workflows across the repositories those tokens can reach. Wiz Research calls the campaign Gheist: it recovered more than 13,500 planted branches associated with more than 850 compromised accounts. For organizations with private GitHub repositories, Wiz observed completed malicious runs and follow-on use of cloud credentials.
The tool creates a ci-sync- branch and adds .github/workflows/sync-check.yml. A push starts the workflow, which encodes environment variables and available Actions secrets into the run log. The attacker retrieves that log using GitHub’s API, then deletes the run seconds later. A quick look at the current branches or Actions run list can miss the event. Wiz describes the campaign as “ongoing and evolving” as of October 24.
A branch becomes a credential drain
Wiz says the attacker-controlled tool cycles through compromised user and automation tokens, committing the workflow as the token owner with the message ci: sync config. The six-character suffix on the ci-sync- branch varies. The run uses Base32 encoding so the printed values differ from the specific secret strings that GitHub normally masks. GitHub’s own Actions guidance warns that transformed secrets may evade automatic redaction and calls for rotation when an unredacted secret reaches a run log.
The workflow declares permissions: {}, which limits its GITHUB_TOKEN permissions. It still reads the secrets context made available to the job. Where a repository has deployment environments, Wiz observed a version that selects environments in a job matrix, seeking environment-scoped secrets too. GitHub’s approval rules for protected environments can stop jobs before those secrets become available; examine the actual environment protection rules and the run’s approval history for each affected repository.
On October 19, a variant also began adding a sync-host job, apparently to repositories with a registered self-hosted runner. Wiz observed it searching local files and process environments for credentials, inspecting cloud metadata and using the Docker socket to search the host filesystem. Its helper-file markers are .gdmp_pat, .gdmp_patg and .gdmp_sweep; stage logs contain gdmp-pivot. A runner that executed this job needs host-level incident handling alongside secret rotation. Wiz found that the job did not collect GCP or Azure tokens; its runner-key lookup and URL-credential matching were also broken. Investigate the local files, process environments, Docker socket and AWS instance role the runner could reach.
Public approval holds, private runs complete
Wiz checked public Gheist runs across the variants and found them held at action_required under GitHub’s July 28 approval control. In private repositories and GitHub Enterprise Server environments, Wiz saw runs complete, followed by deletion through API requests carrying the user agent gheist. It also saw organizations’ own workflows on attacker branches assume AWS roles. The public hold is useful, but private-repository responders need the branch and cloud evidence.
In enterprise organizations, Wiz watched the tool use several users’ and automation accounts’ tokens within minutes, clone private repositories and later reach AWS Secrets Manager with credentials from the same organization. The compromised token is the entry point; the workflow log is the credential handoff. Because the attacker collects that log through GitHub’s API, an outbound-domain block will miss this route.
Find the run after deletion
Search repository branch and commit history for ^ci-sync-[0-9a-z]{6}$, especially a commit adding only .github/workflows/sync-check.yml with message ci: sync config. In retained GitHub organization audit events, correlate the push, workflow completion and run deletion, and look for user agent gheist where that field is available. GitHub documents owner access, filtered export and a 180-day organization audit-log window; collect records promptly. The branch may already be gone, so include historical events and repository commit records.
If Actions uses AWS OIDC, inspect CloudTrail role-assumption events for a subject containing ci-sync-. Wiz used this trail to find execution even where GitHub run records were gone. Compare the role and subject with the intended branch or environment constraints. GitHub’s AWS OIDC guide shows how an IAM trust policy can restrict token.actions.githubusercontent.com:sub to a branch; an environment-based subject needs its own environment protection rules. Match the subject format your repository uses, including immutable IDs where enabled.
Revoke tokens and contain runners
For a matching repository, revoke the token that pushed the workflow and rotate the repository, organization and environment secrets available to the run. Check the token owner’s device for an infostealer, then review connected cloud credentials for follow-on access. Treat a self-hosted runner that executed sync-host and its instance role as compromised: isolate it, replace it from a clean image and investigate its cloud activity. Keep held sync-check runs unapproved.
Require reviewers for production-secret environments, scope AWS OIDC trust to protected branches or protected environments, and use short-lived, narrowly scoped GitHub App credentials where possible. Keep self-hosted runners ephemeral and remove unnecessary Docker-socket and metadata access. To verify containment, compare retained GitHub events and cloud role assumptions with your authorized branches and environment approvals, then test that the intended production role rejects a workflow from an unprotected branch. Deleting a run erases the easiest evidence, so preserve the audit and cloud records before they age out.