Stolen GitHub tokens have enabled an automated campaign to extract secrets from private repositories and GitHub Enterprise Server environments, according to cloud security company Wiz. The operation, named Gheist, turns GitHub Actions logs into a collection channel: malicious workflows write encoded credentials into the logs, which attackers retrieve before deleting the runs.
Wiz recovered more than 13,500 attacker-created branches associated with more than 850 compromised accounts, covering personal projects and enterprise repositories. Those figures measure branches and accounts; the findings do not establish how many organizations had secrets exposed.
The researchers have followed Gheist since September 15. It remained active and continued changing as of October 24, according to The Hacker News’ report on Wiz’s analysis. The name comes from gheist, the user agent its tooling supplies in GitHub API requests.
Where the workflows ran
GitHub audit records provided evidence of execution in private repositories and on GitHub Enterprise Server: runs on the malicious branches completed, after which requests using the gheist user agent deleted them. AWS CloudTrail records also showed organizations’ legitimate workflows executing from those branches and assuming AWS roles, indicating that Actions jobs had run without approval.
Within enterprise organizations, the tooling switched among tokens belonging to users and automation accounts over a period of minutes. It cloned private repositories before introducing the malicious workflow. Cloud credentials belonging to the same organization were subsequently used, hours later, to enumerate AWS Secrets Manager. Wiz did not establish the origin of those credentials or identify whoever used them.
Public repositories showed a different outcome. Every public Gheist run Wiz examined, across all observed tool versions, remained awaiting approval and had therefore not executed its payload.
GitHub introduced that hold on July 28 for workflows it identifies as potentially malicious. A collaborator with write access must approve a flagged run. The safeguard covers only public repositories on github.com; private repositories and GitHub Enterprise Server fall outside its scope. Its protection also depends on GitHub flagging the run.
From repository access to readable credentials
Gheist starts with an already-stolen token that grants repository write access. Wiz traced some tokens to infostealer logs and earlier supply-chain compromises, including the March compromise of the Trivy vulnerability scanner.
For repositories reachable with a token, the tool pushes an unsigned commit under the token owner’s name. Its identifying combination is a branch matching ^ci-sync-[0-9a-z]{6}$, the commit message ci: sync config, and a single added file: .github/workflows/sync-check.yml.
That push launches a workflow which writes the job’s environment and accessible secrets into its logs using base32 encoding. GitHub’s documentation warns that secret masking may fail when values have been transformed. According to Wiz, the base32 representation escapes masking because it differs from the original secret. One published detection string is toJSON(secrets) }}' | base32 -w 120.
Although the workflow declares an empty permissions block, that restricts its GITHUB_TOKEN rather than preventing it from reading secrets. The attackers rely on their stolen token to retrieve the resulting logs through GitHub’s API, then erase the run seconds later. Collection therefore uses GitHub itself without requiring a separate attacker-controlled destination.
A variant also schedules a job for each deployment environment to reach secrets scoped to those environments, which Wiz says often include production credentials. Required reviewers can prevent that access: GitHub’s documentation says an environment’s secrets remain unavailable to a job until a required reviewer approves it.
Around October 19, another variant added a job aimed at self-hosted runners—the machines organizations operate to execute Actions jobs. It apparently targets repositories with a registered runner and attempts to search for 31 types of tokens and credentials, including private keys, process environments, AWS instance role credentials, runner credential files and the Kubernetes service account token. Wiz described the job as buggy and reported no successful run.
Finding and containing exposure
Wiz recommends checking GitHub audit logs for the gheist user agent and for runs on ci-sync-* branches deleted within a minute of completion. On self-hosted runners, indicators include .gdmp_pat, .gdmp_patg, .gdmp_sweep and the job-log marker ===== gdmp-pivot:.
Cloud logs offer another route to detection when workflows use OIDC, which supplies short-lived credentials instead of stored cloud keys. Role assumptions or token exchanges may identify a ci-sync- branch in their subject. There is a limitation: GitHub’s default OIDC subject includes the branch name only for jobs that do not use a deployment environment.
Removing a branch or deleting a run cannot reverse credentials already exposed. Wiz advises affected repositories to rotate every accessible repository, organization and environment secret, revoke the tokens behind the commits, and examine their owners’ devices for infostealers. Held sync-check runs should remain unapproved, and code from the planted branches should not be executed. If one of those branches ran on a self-hosted runner, Wiz recommends treating both the machine and its cloud instance role as compromised.
To limit future access, Wiz recommends required reviewers for environments holding production secrets, OIDC trust restricted to protected branches or environments, and GitHub Apps or short-lived fine-grained tokens in place of personal access tokens. It also recommends short-lived self-hosted runners without Docker socket access or unrestricted access to cloud metadata.




