Gheist Just Turned Your GitHub Actions Logs Into a Secret Vault — 850 Accounts, 13,500 Branches, and What Every Developer Must Do Tonight

Wiz Research just exposed Gheist, a campaign that used stolen GitHub tokens to plant a workflow in over 850 accounts, print every repository secret into the build log in base32, download the log, and delete the run seconds later. If you keep deploy keys, API tokens, or cloud credentials anywhere near GitHub, this one is about you.

Gheist Just Turned Your GitHub Actions Logs Into a Secret Vault — 850 Accounts, 13,500 Branches, and What Every Developer Must Do Tonight
Gheist Just Turned Your GitHub Actions Logs Into a Secret Vault — 850 Accounts, 13,500 Branches, and What Every Developer Must Do Tonight — Technical Architecture & Overview

Table of Contents

I have spent years telling clients your secrets are safe in GitHub because GitHub masks them in logs. This week, I had to eat those words. Wiz Research published its analysis of a campaign called Gheist, and it turns the whole idea of "the log is safe" upside down. The attackers did not break into GitHub or need a zero-day. They used GitHub exactly as designed — a token, a branch, a workflow, a log — and walked away with the keys to more than 850 accounts.

What Gheist Is, in Plain Language


Gheist is a credential-harvesting campaign Wiz Research has tracked since September 15, 2026. Its name comes from the tool's own API requests, which carry the user agent string "gheist."

The numbers are what stopped me mid-scroll. Wiz recovered more than 13,500 malicious branches planted across more than 850 compromised accounts, ranging from personal side projects to private enterprise repositories. The campaign is still ongoing and still evolving as of late October, according to Wiz. About two-thirds of the implicated accounts are no longer available on GitHub, most likely because Wiz reported them and GitHub removed them — but that does not mean the stolen secrets stopped working.

The initial access did not come from some exotic exploit. Wiz traced the likely credential sources to infostealer logs and tokens exposed in earlier supply-chain compromises, including the March Trivy compromise. Somebody's laptop got infected, or a token leaked months ago, and that old leak became the front door. The attackers just logged in with a key someone left lying around.

How the Attack Works, Step by Step


I have read the Wiz write-up twice now, and the elegance of it is genuinely unsettling. There is no malware payload in the traditional sense. There is no attacker server to block. Here is the sequence:

Step 1: Take a stolen token. The attacker starts with a GitHub personal access token harvested from infostealer malware or a previous breach. With that token, they can act as the account owner.

Step 2: Plant a branch in every reachable repository. For each repository the token can reach, the tool creates a branch named ci-sync- plus six random characters, then commits a GitHub Actions workflow named sync-check to it. The commit is unsigned and made to look as if the token's owner wrote it, so at a glance it looks like ordinary activity.

Step 3: Print the secrets into the run log. When the workflow runs, it dumps the repository, organization, and environment secrets into its own execution log. This is the clever bit.

Step 4: Download the log and delete the evidence. Once the run finishes, the tool downloads the log through the GitHub API and deletes the run seconds later. The run — the thing a security team would investigate — is gone before anyone notices it existed.

Step 5: Spend the loot. Wiz observed cloud credentials from the same organization being used hours after the malicious workflows ran to enumerate AWS Secrets Manager, and CloudTrail records showed workflows on attacker-created branches assuming AWS roles. A GitHub token becomes a cloud credential, which becomes a whole infrastructure.

Notice what is missing: no phishing email to report, no malicious domain to blocklist, no binary to quarantine. Because the secrets leave through GitHub's own API, as Wiz researchers put it, there is no attacker domain or outbound connection to block. Your firewall and endpoint protection cannot help you. The exfiltration channel is a feature you use every day.

Why GitHub's Secret Masking Did Not Stop It


GitHub Actions masks secret values that appear verbatim in a workflow log, replacing them with asterisks. It works exactly as documented. The problem is the word "verbatim."

Gheist's workflow does not print the secret as-is. It encodes the secrets in base32 first, then prints the encoded version. Base32 transforms the value into a completely different string of characters, so GitHub's masking never recognizes it as a secret. The attacker downloads the log, decodes the base32, and has the original secret back, character for character.

Masking is a display filter, not encryption. Anything a workflow can read, a workflow can transform — encoding, splitting, chunking, reversing all defeat a verbatim match. If your threat model is "the log is safe because secrets are masked," Gheist just broke it with a trick a first-year student could implement.

The Self-Hosted Runner Twist


Wiz also documented an evolved variant of Gheist that targets self-hosted GitHub Actions runners. This version adds a second job, called sync-host, that searches the runner machine itself for credentials: 31 token formats including keys for AI providers such as Anthropic, Hugging Face, Groq, and NVIDIA, plus private keys, .env files, Terraform state, and shell history. If you run self-hosted runners on a machine that also holds deploy keys or a developer's shell history, that machine just became the target.

One honest caveat: Wiz noted this variant has implementation bugs — a broken URL-credential regex, and no GCP or Azure token collection — so not every targeted credential is successfully extracted. But "the attacker's code has bugs" is not a security strategy. The next version will fix them. And for WordPress developers, this hits close to home: plenty of us run deployment pipelines from a VPS that also holds SFTP credentials, wp-config salts, and payment and email API keys. A self-hosted runner on that same box is a single point of failure for everything.

The Protection That Only Covers Half the Problem


GitHub did respond: on July 28, it shipped a control that holds potentially malicious workflow runs in public repositories for collaborator approval. A flagged run will not execute until a collaborator with write access reviews it.

Wiz checked, and in public repositories the protection held: every public Gheist run they examined sits at action_required — stuck, waiting for approval that never comes.

Here is the catch, and it is a big one: that protection applies to public repositories on github.com only. It does not extend to private repositories or GitHub Enterprise Server. And in private repositories and Enterprise Server, Wiz observed the runs completing and then being deleted by the gheist user agent. The production secrets — the ones that actually matter — live in private repositories. The shield covers the window display and leaves the vault open.

The repositories most likely to hold deployable credentials are exactly the ones without the approval hold. If you run GitHub Enterprise Server or keep client work in private repos, you cannot rely on GitHub to catch this for you.

The Bizarre Side Story: 1,461 Repositories Renamed to Cracked Software


One connected detail: on October 6, 391 accounts renamed about 1,461 repositories to cracked-software lures like fl-studio-crack. Many of those accounts were also used by Gheist, and the renaming wiped out 610 ci-sync branches as a side effect.

Those renamed repositories are bait: developers searching for cracked tools clone them, run them, and get infected with malware that harvests more tokens — a flywheel feeding the next round of Gheist. A repository renamed overnight to something like fl-studio-crack is also a useful detection signal: it is a compromised account, and it should trigger an immediate incident response.

What I Checked on My Own Accounts the Same Night


Here is the checklist I actually ran, and I recommend you run it tonight, not next sprint:

1. Search for ci-sync- branches. In every organization and personal account you control, look for branches matching ci-sync- plus six random characters, a workflow named sync-check, and runs that completed and were deleted shortly afterward. If you find one, assume the secrets that repository could reach are compromised — deleting the branch does not reverse the exposure.

2. Search audit logs for the gheist user agent. If your plan gives you audit log access, search for that user agent string. It is a direct fingerprint.

3. Rotate everything the repository could reach. Repository, organization, and environment secrets, plus cloud credentials, AI provider keys, and SSH keys. Then check cloud audit logs — AWS CloudTrail, for example — for unexpected role assumptions or Secrets Manager enumeration in the hours after any suspicious run.

4. Audit your tokens like they are already public. Any token that appeared in an infostealer log, a past breach, or the Trivy compromise should be treated as burned. Replace long-lived tokens with fine-grained ones scoped to a single repository with minimum permissions and an expiry date. Better yet, move cloud access to OpenID Connect (OIDC) so there is no stored cloud credential for a workflow to steal.

5. Separate your runners from your secrets. If you use self-hosted runners, run them on ephemeral, isolated machines that hold nothing else. No developer shell history, no .env files, no Terraform state, no long-lived keys on the same box. A runner should be disposable by design.

6. Turn on secret scanning and push protection. Scanning will not catch a workflow printing base32, but it does catch the upstream problem: tokens committed to code. Every token that never reaches a repository is a token Gheist cannot steal.

Conclusion


Gheist is not a story about a brilliant new exploit. It is a simple trick — base32-encoding a secret before printing it — combined with an old failure: long-lived tokens, over-scoped access, and secrets sitting where a workflow can read them. The attackers did not defeat GitHub's security. They walked around it using GitHub's own features, at a scale no human team could match.

Most of us cannot prove we are unaffected without checking. So check: search for ci-sync- branches and the gheist user agent, and rotate anything a compromised token could have reached. Then fix the structural problem: short-lived, narrowly scoped tokens, OIDC instead of stored cloud keys, and runners that hold nothing worth stealing.

Your build log is not a vault. It never was. Gheist just proved it in public.

Frequently Asked Questions


What is the Gheist campaign?
A credential-theft campaign tracked by Wiz Research since September 15, 2026. Attackers use stolen GitHub tokens to plant a malicious sync-check workflow in every reachable repository. It prints secrets into the Actions log in base32, the attacker downloads the log, and the run is deleted seconds later.

How many accounts has Gheist affected?
Wiz recovered more than 13,500 malicious branches from more than 850 compromised accounts, from personal projects to private enterprise repositories. About two-thirds of those accounts are no longer on GitHub. The campaign was still ongoing and evolving as of late October 2026.

Why didn't GitHub's secret masking stop Gheist?
GitHub masks secret values only when they appear verbatim in logs. Gheist base32-encodes them first, so the encoded text never matches the original value and masking never triggers. The attacker simply decodes the downloaded log.

Does GitHub's workflow approval protection stop Gheist?
Only in public repositories on github.com, where flagged runs are held for approval — Wiz confirmed every public Gheist run they checked was held at action_required. It does not apply to private repositories or GitHub Enterprise Server, where Gheist runs completed successfully.

How do I check if my repositories were hit by Gheist?
Search for ci-sync- branches, a workflow named sync-check, the gheist user agent in audit logs, and runs that completed and were deleted shortly afterward. Also check cloud audit logs like AWS CloudTrail for unexpected role assumptions or Secrets Manager enumeration.

What should I do if I find a Gheist branch in my repository?
Assume every secret that repository could reach is compromised and rotate them all. Deleting the branch or run does not undo the exposure. Then replace long-lived tokens with short-lived, fine-grained ones, move cloud access to OIDC, and isolate self-hosted runners on ephemeral machines.

Looking for more WordPress & Elementor tutorials?

Explore our complete directory of 53+ web performance, theme development, and Elementor architecture guides.

View Topic Sitemap XML Feed