LockerGnomeFor serious tech professionals with a silly side.
Subscribe

Search

Securitygithubgithub actionsci/cdsecretssupply chain

Gheist Uses Stolen GitHub Tokens to Plant a Workflow That Prints Every Repo Secret in Base32, Slipping Past GitHub's Log Masking

Wiz Research found a campaign that pushed more than 13,500 ci-sync branches from 850-plus compromised accounts, each carrying a workflow that base32-encodes the repository's secrets into its own Actions log, downloads the log and deletes the run.

Photocopy collage of Chris holding a long printout up close, a ribbon of scrambled marks leaking out between its black redaction bars
Illustration: LockerGnome

There is no server to block. Gheist’s exfiltration channel is GitHub’s own API: the build log is the loot, and the attacker downloads it like any developer checking why CI failed.

Rami McCarthy and Hila Ramati of Wiz Research published the analysis October 25. “Since September 15, 2026, Wiz Research has been tracking Gheist,” they write, and “the attack commits a workflow named sync-check to every repository to which each token has access.” Once the run finishes, “the tool downloads the log through the GitHub API and deletes the run seconds later.” The name comes from the tool’s own traffic: its API requests “carry the user agent gheist.”

The scale is the part that should get a security team’s attention. Wiz says it has “recovered more than 13,500 planted branches from more than 850 compromised accounts, ranging from personal projects to enterprise organizations’ private repositories.”

Six lines of YAML

Each repository gets a branch named ci-sync- plus six random characters, and one commit adding .github/workflows/sync-check.yml with the message ci: sync config. The commit is unsigned and authored as the token’s owner, and the push triggers it. The job runs two commands: env | base32 -w 120, then echo -n '${{ toJSON(secrets) }}' | base32 -w 120.

That second line does the damage. “GitHub masks secret values in logs only when they appear verbatim,” Wiz writes. “A base32-encoded secret doesn’t match, so it prints in full.” The workflow sets permissions: {}, but that “doesn’t restrict the secrets context.” When a repo has deployment environments, the tool runs the job across all of them in a matrix, so “environment-scoped secrets, often production credentials, are printed too.” And “because the secrets leave through GitHub’s own API, there is no attacker domain or outbound connection to block.”

A later variant adds a job for self-hosted runners. It searches the machine for “31 token formats (including keys for AI providers such as Anthropic, Hugging Face, Groq and NVIDIA),” private keys, .env files, Terraform state and shell history. Wiz notes the 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.”

Where the hold worked, and where it doesn’t exist

GitHub shipped a defense for exactly this pattern on July 28. Its changelog says a run flagged as potentially malicious “won’t execute until a repository collaborator with write access reviews and approves it,” and that “this protection currently applies to public repositories on github.com only.”

In public repos, it held. “Every public Gheist run we checked, across all variants including the self-hosted job, sits at action_required,” Wiz writes. “The control worked, and the malicious payloads never ran.”

Private repos are another story. “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.” In enterprise organizations, Wiz 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.”

As of October 24, Wiz says “the attack is ongoing and evolving,” and “roughly ⅔ of implicated accounts are no longer available on GitHub.” The same compromised accounts turn up in other operations: on October 6, 391 accounts renamed about 1,461 repositories to cracked-software lures like fl-studio-crack, and “the rename wiped 610 ci-sync branches as a side effect.” Wiz traced the credentials it could to infostealer logs and to tokens exposed in earlier supply-chain compromises. It also says “Gheist is distinct from GhostAction,” an earlier campaign that sends secrets to its own server.

The design is smart because it is boring: one commit, one run, nothing leaves through anything but the API every CI tool calls. GitHub built a hold for exactly this and pointed it at public repositories, and the attack’s real wins happened where it doesn’t reach. Wiz reported the activity to GitHub. Whether the hold now gets extended to private repositories and Enterprise Server is the question every organization running Actions on private code has a stake in.

More Security

Every Security story →