GhostAction: Around 1,900 GitHub Accounts Compromised, More Than 30,000 Repositories Affected

GhostAction affected around 1,900 GitHub accounts and more than 30,000 repositories. Attackers tried to steal CI/CD secrets, cloud credentials, and API keys for AI services.

· 4 min read · 5 comments

Attackers behind the GhostAction campaign compromised around 1,900 GitHub accounts and injected malicious GitHub Actions workflows into more than 30,000 repositories. According to a Selectel article on Habr, activity peaked on October 8–9. The attackers tried to obtain CI/CD secrets, cloud credentials, and API keys for AI providers.

How the Attackers Used GitHub Actions

In this campaign, the attackers did not exploit a vulnerability in GitHub Actions itself. After gaining access to developers’ accounts, they directly modified workflow files in repositories. StepSecurity analysts believe leaked personal access tokens (PATs) may have provided the initial access, but the exact route of compromise has not been confirmed.

The malicious files were named security-audit.yml and github_actions_security.yml, making them look like routine security checks. When run, the workflows accessed secrets already used in legitimate builds, collected credentials, and sent them to an attacker-controlled server. That is why a filename alone is not enough to assess a file: its contents and who made the change both need checking.

The October variant looked beyond secrets available to the workflow. The attackers used actions/checkout@v4 with fetch-depth: 0 to retrieve the full commit history and scan it alongside the working tree. A key removed from the current code could still be present in older versions of files. Checking only the repository’s latest state is therefore incomplete.

The scripts searched for AWS keys, GitHub and GitLab PATs, Google and Firebase credentials, and Anthropic, OpenAI, and OpenRouter API keys. In later variants, the script also captured nearby lines of code when searching for AWS keys, so it could pair a key ID with its secret component. The data found was sent over HTTP to the IP address 193.32.204.199.

What We Know About the Scale and Outcome

The attack unfolded in stages: the code was first delivered to 160 previously known victims, and 20 minutes later, injection into 28,000 new repositories began. Researchers counted attempts to extract 3,174 secrets. This is not the number of confirmed thefts: the exact amount of data stolen in the October campaign is unknown, although GitGuardian researchers documented instances of leakage.

For comparison, in August and September, malicious workflows were found in 772 public repositories. Of 3,669 runs, 336 completed successfully; 26 secrets were stolen from 13 repositories during that period. Those figures relate to earlier episodes and cannot be used to estimate losses from the October wave.

GitHub’s built-in approval mechanisms block most malicious activity. However, the attackers reused 92 workflows from previous attacks, including infrastructure active since September 2025. By October 5, the malicious code had been removed from only 124 previously affected repositories—around 16% of the 772. Removing a file stops it from being used again in the repository, but it does not undo the exposure of credentials.

What Project Owners and Developers Should Check

For a website or service, the consequences may extend beyond the repository. If a workflow accessed a deployment key, cloud account, or external API, fixing the YAML file alone does not resolve the issue of that access. The first step is to distinguish confirmed account compromises from other secrets that may have been exposed during Actions runs.

Credentials for compromised accounts with write access should be revoked immediately, even if the malicious workflow has already been removed. At the same time, check GitHub audit logs and PAT activity to see which accounts changed files in .github/workflows/. The Actions run history can help establish which workflows ran and which secrets they could access.

Next, rotate and reissue potentially affected cloud keys, deployment credentials, registry tokens, and API keys for AI providers. Old passwords and keys also need checking if they were removed from the code but remain valid: scanning the full Git history allowed the attackers to find them. We have covered a similar problem with secrets in public repositories on the blog.

My Takeaway: Removing the File Does Not Close the Incident

I would judge the work not by the number of YAML files removed, but by two outcomes: access for compromised accounts has been revoked, and the workflows that may have accessed the remaining keys have been identified so a decision can be made about rotating them. That is a verifiable scope of work for a specific project. The scale of the campaign as a whole does not show which secrets were accessible in any individual repository.

Sources

Where the news comes from. The text is a retelling in the author’s own words; the facts come from the source, the opinion is the author’s.

  1. Хакеры взломали 1 900 аккаунтов GitHub и атаковали 30 000 репозиториев habr.com

About the author

Vidas Sakalauskas

Backend developer

I develop APIs and take care of service performance. I prefer small changes with clear metrics to big rewrites “for the future.”

All posts by the author

Leave a comment

Not published. We’ll send a link to confirm your comment.

No links or ads. Your first comment is published after review. By sending a comment you agree to the privacy notice.

  1. We’ve had compromised PATs used to alter workflow files, so removing the YAML was never enough: we also revoked the token, rotated every secret the job could read, and checked the audit log for other repository changes.

    1. @kubanych.mambetov that audit-log check is the part people are most likely to skip once the suspicious YAML is gone. A stolen PAT can make the workflow file just one change among several, which is a wonderfully annoying incident-response surprise. I’m still learning this stuff, but revoking the token and rotating everything the job could read sounds like the right order of operations.

      1. The order sounds sensible, though I’d treat rotation as scoped to the secrets the workflow could actually access rather than rotating every secret in the org by default. When you reviewed the audit log, did you also check for changes to workflow permissions or other files that could preserve access after the PAT was revoked?

        1. I’d check both: workflow permissions and any other file changes, especially since the PAT may have been used for more than the malicious YAML. For secrets, scoping rotation to what the job could read makes sense; the audit log can help spot anything else that needs cleanup.

        2. Yes, I’d check workflow permissions and other repository changes as well; revoking the PAT only closes that access path. Secret rotation should stay scoped to what the workflow could read, since broad rotation adds operational cost without improving coverage.