DEV Community

Cover image for GhostAction Has Hit GitHub Repos Three Times Since 2025, and It Never Actually Stopped
Rohit Bhadani
Rohit Bhadani Subscriber

Posted on

GhostAction Has Hit GitHub Repos Three Times Since 2025, and It Never Actually Stopped

Between August 31 and September 30, 2026, a supply chain campaign called GhostAction pushed a credential-stealing GitHub Actions workflow to 772 public repositories belonging to 373 accounts, targeting 2,577 secrets by name. That's per GitGuardian's October 7 writeup, and the number that matters more than the repo count is this one: in 92 of those cases, the attacker didn't add a new malicious workflow. They updated one already sitting in the repository from an earlier wave. GhostAction isn't a campaign that happened three times. It's a campaign that never actually stopped running since GitGuardian first named it in September 2025.

How it actually works

The attack doesn't exploit a GitHub Actions flaw. It starts from a stolen GitHub maintainer credential, a personal access token pulled from an infostealer log or credential dump, per an October 9 analysis from Aviatrix, and uses that account's own legitimate write access. The workflow it commits doesn't dump every environment variable available to the runner. It first reads the repository's existing workflow files to find which secrets are actually referenced, in the form ${{ secrets.NAME }}, then builds a new workflow that hardcodes just those exact names into a single curl -X POST call to attacker infrastructure. The file is usually named github_actions_security.yml or security-audit.yml, committed with a message like "Add Github Actions Security workflow," designed to read as routine automation hygiene during a quick code review rather than anything suspicious.

What changed in the October wave

A related burst analyzed by Socket on October 8 hit 346 repositories through two compromised maintainer accounts, including Uber's athenadriver and the 18,000-star kitao/pyxel game engine. The payload in that wave kept the original secret-harvesting behavior and added something new: a regex sweep across the full working tree and complete git history, enabled by fetching the repository with fetch-depth: 0 before searching, for cloud provider and AI service credential patterns. Earlier GhostAction waves only took what was sitting in configured GitHub Actions secrets. This version also finds the AWS key a developer committed and then deleted eighteen months ago, because deleting a file doesn't remove it from git history, and the workflow now reads that history directly.

Why removing the workflow file doesn't end it

GitHub started blocking the known malicious workflow from executing on October 9. New waves using the same payload still appeared on October 10 and October 12, per GitGuardian's tracking, because the thing that persists across waves was never the workflow file. It's the underlying stolen credential. As long as a compromised maintainer's token is still valid, the attacker can commit the workflow again to any repository that account can write to, under a new filename if the old one gets flagged, the moment the old one stops running. Deleting the malicious file and calling the incident closed addresses the symptom GitGuardian's own report calls out directly: the GitHub credential that allowed the injection in the first place has to be found and revoked, or someone, not even necessarily the same actor, uses it again.

This is close to the same root shape as the fake Terraform provider that hid malware inside .woff font files: a trusted-looking artifact executing inside a pipeline that already has more standing access than the task in front of it needs, where the actual fix is reducing what that access can do, not just finding this specific instance of it.

What I'd actually check

Require review on any change to .github/workflows/, specifically, even for accounts with otherwise broad write access. A workflow file is code that runs with repository secrets attached, and it deserves the scrutiny of a permission change, not a routine commit.

Pin third-party actions to a full commit SHA, and audit workflow files for anything that sends data to a raw IP address or an unfamiliar endpoint. curl -X POST to a bare numeric address in a step named "security audit" is not what a real security scan looks like.

If a malicious workflow is found, treat secret rotation as the second step, not the whole response. Find and revoke the specific credential, API token, OAuth grant, or SSH key, that allowed the commit to happen in the first place. Rotating only the secrets the workflow exfiltrated leaves the door it walked through wide open for the next wave.

Give CI/CD runners short-lived, narrowly scoped credentials instead of long-lived ones with broad reach, so that even a successful injection has less sitting in the runner's environment worth stealing. A runner that requests a scoped, minutes-long token for exactly the job in front of it is a much smaller payout than one holding a standing key to everything the pipeline has ever touched.

The rule

A campaign that keeps reusing the same workflow filename for over a year isn't persisting because defenders are slow to notice it. It's persisting because removing the file is treated as the fix, when the file was never the actual foothold, the stolen credential was. Any response to this class of incident that stops at "we deleted the workflow" hasn't closed anything.


I'm Rohit, founder of Krova Cloud: disposable sandboxes for CI jobs with short-lived, narrowly scoped credentials by default, so a hijacked token or a malicious workflow has as little standing access to reach for as possible. More debugging stories at debugly.dev.

Top comments (0)