Article

    Cyber News / Article / Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles

    Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles
    Hi
    Hila Ramati-11 days ago

    Gheist: Attempted Scaled Abuse of Stolen GitHub Tokens Stumbles

    An automated campaign is planting secret-dumping workflows in every repository that stolen GitHub tokens can reach. It uses no C2 server, is actively evolving, and has been significantly disrupted by recent GitHub security improvements.

    Since September 15, 2026, Wiz Research has been tracking Gheist: a new campaign performing mass abuse of compromised GitHub tokens. The attack commits a workflow namedsync-checkto every repository to which each token has access. The workflow seeks to print the repository's secrets into its own workflow log. Then, the tool downloads the log through the GitHub API and deletes the run seconds later. The tool's API requests carry the user agentgheist, which gives the campaign its name.

    We have recovered more than 13,500 planted branches from more than 850 compromised accounts, ranging from personal projects to enterprise organizations' private repositories.

    A recent GitHub control has been blocking the exploitation in public repositories, but we've observed successful exploitation and follow-on activity in private repositories and GitHub enterprise environments.

    As of October 24:

    the attack is ongoing and evolving

    roughly ⅔ of implicated accounts are no longer available on GitHub. This likely indicates response activity, as we have reported this activity to GitHub.

    For each repository, the tool creates a branch namedci-sync-plus six random characters, then commits.github/workflows/sync-check.ymlwith the messageci: sync config. The commit is unsigned and authored as the token's owner. The push event triggers the workflow:

    Three details make it work:

    GitHub masks secret values in logs only when they appear verbatim. A base32-encoded secret doesn't match, so it prints in full.

    Whilepermissions: {}restricts the job'sGITHUB_TOKEN, which the attacker doesn't need, it doesn't restrict the secrets context.

    When a repository has deployment environments, the tool lists them in a job matrix withenvironment: ${{ matrix.environment }}, so environment-scoped secrets, often production credentials, are printed too.

    Because the secrets leave through GitHub's own API, there is no attacker domain or outbound connection to block.

    In enterprise organizations, we 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.

    Two variants of this attack have been observed in parallel. They can be distinguished via the branch names.

    Tool Auses a hexadecimal suffix and the plain payload.

    Tool Buses a base36 suffix, addedtimeout-minutes: 6and the environment matrix within hours of its first run on September 28, and modifies the selected payload per repository.

    On October 19, Tool B addedecho X | base32 -w 120between the dumps, which printsLAFA====as a separator, likely for the operator's log parser. Hours later it pushed a second job,sync-host, withruns-on: self-hosted, apparently only to repositories with a registered self-hosted runner. It writes three helper files (.gdmp_pat,.gdmp_patg,.gdmp_sweep) and logs each stage under a===== gdmp-pivot: <stage> ===== header:

    Host details and crontab.

    A recursive search of the home directory, workspace and system paths for 31 token formats (including keys for AI providers such as Anthropic, Hugging Face, Groq and NVIDIA), private keys,.envfiles, Terraform state and shell history.

    The environment of every process, from/proc/*/environ.

    Through the Docker socket, a container with the host's root filesystem mounted, to repeat the search on the host.

    AWS instance-role credentials, plus GCP and Azure metadata.

    GitHub Actions and GitLab runner credential files.

    The Kubernetes service account token.

    However, this 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.

    Since July 28, 2026, GitHub hasheld potentially malicious workflow runsin public repositories until a collaborator approves them. Every public Gheist run we checked, across all variants including the self-hosted job, sits at action_required.The control worked, and the malicious payloads never ran.

    Unfortunately, that hold doesn't apply to private repositories or GitHub Enterprise Server. There, GitHub audit logs show runs onci-sync-*branches completing and then being deleted by thegheistuser agent. CloudTrail shows the organizations' own workflows running on those branches and assuming AWS roles, which proves Actions executed without approval.

    For defenders, that last signal finds Gheist without any GitHub logs, as shown below.

    This campaign is leveraging compromised GitHub access. The same accounts are used by other actors and campaigns, which demonstrates how compromised credentials can move throughout the threat actor ecosystem. We've observed overlaps with the following campaigns:

    Lure repositories (malware distribution).On October 6, 391 accounts renamed about 1,461 repositories to cracked-software lures like fl-studio-crack and solana-sniper-lite. The rename wiped 610ci-syncbranches as a side effect. 166 of these accounts never appear in Gheist activity, so the pool is larger than Gheist's footprint.

    Free compute.On October 18, 25 Gheist accounts each created a "Windows RDP Runner" repository within nine minutes, attempting to abuse GitHub Actions for free compute cycles.

    PolinRider.Some victims appear to be infected withPolinRider, the DPRK-linked malware that hides JavaScript in build config files and force-pushes back-dated commits. On October 19 and 20, three victims re-pushed their Gheist commits with a loader appended tovite.config.jsbehind hundreds of whitespace characters, and in two cases an api.js that package.json runs before every dev and build.

    Megalodon.57 accounts from Gheist's first phase were also victims ofMegalodonin May.

    The credential sources we could trace fit this picture: infostealer logs, and tokens exposed in earlier supply-chain compromises such as the Trivy compromise in March.

    Note: Gheist is distinct fromGhostAction, which uses different workflow files and exfiltrates secrets to 193.32.204[.]199.

    Hunt

    Look for branches namedci-sync-followed by six random characters, where the only change is a new file at.github/workflows/sync-check.ymlwith the commit messageci: sync config.

    In GitHub audit logs, look for workflow runs onci-sync-*branches that were deleted within a minute of completing, and for any activity from the user agentgheist.

    If your workflows authenticate to the cloud with OIDC, check cloud audit logs for role assumptions or token exchanges whose subject includes aci-sync-branch. That shows GitHub Actions ran on an attacker branch, even if you don't collect GitHub logs.

    On self-hosted runners, look for the files.gdmp_pat,.gdmp_patgand.gdmp_sweep, and the stringgdmp-pivotin job logs.

    Respond

    Rotate every repository, organization and environment secret available to an affected repository. Deleting the run or the branch doesn't undo the exposure.

    Revoke the tokens that pushed the commits, and check their owners' devices for infostealers.

    If aci-syncbranch ran on a self-hosted runner, treat the host and its instance role as compromised.

    Don't approve heldsync-checkruns, and don't run code fromci-syncbranches.

    Harden.Require reviewers on environments with production secrets, scope OIDC trust policies to protected branches or environments, use GitHub Apps or short-lived fine-grained tokens instead of personal access tokens, and run self-hosted runners as ephemeral hosts without Docker socket or unrestricted metadata access.

    User agentgheist

    Branchci-sync-followed by six characters, matching^ci-sync-[0-9a-z]{6}$

    Commit messageci: sync configadding only.github/workflows/sync-check.yml

    MarkerstoJSON(secrets) }}' | base32 -w 120and===== gdmp-pivot:; files.gdmp_pat,.gdmp_patg,.gdmp_sweep

    Workflow blob SHAs:f3da2b8b357dcda02fd455d314be2a95007516a6(plain),d09535857a741bbb976bdc084a9a021713133291(timeout),1dc5af9cce950aad1eb1546ecce8565b117540c4(separator),c665b778b2d822453293003e7ca0bbb2cb08e39d(self-hosted job). Environment-matrix variants differ per repository.

    TheThreat Center advisoryincludes an Event Explorer query covering planted commits, Gheist activity in GitHub audit logs and cloud role assumptions from attacker branches. Wiz Defend detects the activity with rules including "New Workflow Completed And Deleted In Short Time Span", "Workflow Pushed With Secret Dump" and "Commit Created With Known Malicious Commit Message".

    Detect and mitigate CVE-2026-111497, a critical Brotli decoder vulnerability enables remote code execution.

    Reduce triage times and focus on the risks that matter most by combining context across code, infrastructure, and runtime

    Wiz Research is tracking an ARTEX-using actor, overlapping with Storm-0940, across multiple targeted software vendors. Learn how the AI agent attacks Okta, Entra, and Cognito, and what it left behind in the logs.

    Get a personalized demo

    ©2026Wiz, Inc.

    StatusPrivacy PolicyTerms of UseModern Slavery StatementCookie Settings

    Original source