#Security#GitHub Actions#CI/CD#Supply Chain#DevOps

GhostAction Is Back: Hardening GitHub Actions Against Workflow Injection and Secret Theft

webhani·

GhostAction first surfaced in September 2025, when GitGuardian reported malicious workflows pushed to hundreds of repositories that sent repository secrets to an attacker-controlled server. According to security researchers, a second wave ran from late August through September 2026. GitGuardian counts 772 public repositories and 2,577 targeted secrets in that window, and other write-ups say the new variants also look for AI provider API keys such as Anthropic, OpenAI, and OpenRouter. Counts differ between sources, so treat the numbers as indicative.

The numbers matter less than the mechanism, because it is simple and it will be reused. This post explains how it works and what to change in your repositories.

How the attack works

There is no exploit in GitHub Actions itself. The attacker obtains a maintainer's credentials, then does three things:

  1. Lists the secret names referenced in existing workflow files.
  2. Pushes a new workflow with a harmless-sounding name (the 2025 campaign used a title along the lines of "Add GitHub Actions Security workflow").
  3. The workflow runs on push, reads each secret by name, and sends them to an external endpoint.

Step 1 is the part people overlook. A workflow file lists exactly which secrets exist, so anyone with write access can enumerate them without ever seeing the values. The malicious workflow then references those names, and GitHub injects the values at runtime.

The root cause is write access gained through stolen credentials: a leaked personal access token, a compromised developer machine, or a malicious package that read a token from disk. The workflow is only the exfiltration channel.

Defense 1: make workflow changes reviewable

If an account with write access can push a workflow straight to a branch that has secrets, one stolen token is enough. Close that path.

  • Protect the default branch and require pull request review, including for administrators.
  • Add a CODEOWNERS rule for the workflows directory so changes need a specific team's approval.
# .github/CODEOWNERS
/.github/workflows/  @your-org/platform-security

Note that a workflow pushed to a feature branch still runs with that branch's version of the file for push events. That is why the next control matters.

Defense 2: do not give secrets to every branch

Repository-level secrets are readable by any workflow in the repository. Environment secrets can be restricted.

  • Move production credentials into a GitHub Environment.
  • Restrict the environment to protected branches or tags.
  • Require a reviewer to approve deployments to that environment.

A workflow on an arbitrary branch then cannot read production secrets, and the attempt is blocked at an approval gate where a human sees it.

Defense 3: replace long-lived secrets with OIDC

The most effective change is to have fewer secrets worth stealing. For cloud access, use OpenID Connect so the job exchanges a short-lived token for credentials at runtime.

permissions:
  id-token: write
  contents: read
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
          aws-region: ap-northeast-1

Scope the IAM role's trust policy to the repository, the environment, and the branch. A stolen workflow from a different branch or fork then cannot assume it. We covered the trust-policy details in our post on GitHub Actions OIDC with AWS.

For third-party APIs that have no OIDC option, which currently includes most LLM providers, you still hold a static key. Treat those as the highest-risk secrets: give each project its own key, set spend limits at the provider, and keep them in environment-scoped secrets rather than repository-wide ones.

Defense 4: default to least-privilege tokens

Set the default GITHUB_TOKEN permission to read-only at the organization level, and grant write scopes per job.

permissions:
  contents: read

This does not stop a malicious workflow from sending out other secrets, but it limits what the built-in token can do if it is exfiltrated.

Defense 5: restrict egress and watch for it

The exfiltration in these campaigns reportedly used plain HTTP to a single address. Outbound network restrictions on runners turn that into a blocked request and an alert. Self-hosted runners behind an egress allowlist make this straightforward, and hosted runners can use a monitoring agent that records outbound connections. See our notes on GitHub Actions egress firewalls.

Also enable secret scanning with push protection, and audit your organization log for new workflow files created outside pull requests.

If you find a malicious workflow

Rotating the secrets is necessary but not sufficient. GitGuardian's advice is to also revoke the compromised GitHub credentials, since the attacker can simply re-run the same trick with the new secrets.

  1. Revoke the personal access tokens, SSH keys, and OAuth app grants of the compromised account. Remove the account's session.
  2. Delete the malicious workflow and check the full branch list for copies.
  3. Rotate every secret the workflow referenced, starting with cloud and package registry credentials.
  4. Review audit logs and provider-side usage for the window between the push and the cleanup.
  5. Search git history of related repositories. Some variants reportedly scan history for credentials, not only Actions secrets.

A short checklist

  • Protected default branch with required review
  • CODEOWNERS on .github/workflows/
  • Production secrets in environments with branch restrictions and approvals
  • OIDC for cloud access, scoped by repo, environment, and branch
  • Read-only default GITHUB_TOKEN
  • Per-project LLM API keys with spend limits
  • Secret scanning with push protection enabled
  • Egress monitoring on runners

None of these is new advice, which is the point. Campaigns like this succeed against repositories where the basics are only partly in place. Work through the list in order and you remove most of the value an attacker gets from a single stolen token.