A hooded hacker targets cloud credentials in a deployment pipeline connected to Azure and AWS.
A stolen GitHub token can do a lot of damage, and the wfx-loot campaign shows how much. Researchers say a single attacker used thousands of hijacked accounts to push a CI secret stealer into repositories. The stealer went after GitHub Actions secrets, cloud credentials and even Azure and AWS trust settings. Any team that runs CI pipelines should check its repositories, including Microsoft-centric shops that rely on Azure, Entra federated identities and GitHub Actions.

A hooded hacker targets cloud credentials in a deployment pipeline connected to Azure and AWS. What happened​

Aikido says the campaign began around 12:45 UTC on November 10, when the attacker used stolen credentials from 4,837 compromised accounts to probe 61,764 repositories for write access. Aikido named it wfx-loot, after the attacker's own labels: "wfx" for the malware and "loot" for what it steals. StepSecurity tracks the same activity as "WFX Phantom".

This is not a GitHub vulnerability. StepSecurity says the attacker does not exploit a bug in GitHub Actions. It holds valid tokens for real developer and bot accounts and uses them the way a developer would, by opening pull requests and pushing commits.

Probed, hit and executed are different things​

The headline numbers measure different stages, so they shouldn't be merged:

  • Probed: Aikido counted probe pull requests in 61,764 repositories. StepSecurity's GitHub search reached more than 63,000 probe pull requests by the morning of November 12.
  • Malicious commits: SafeDep separately counted 8,093 public repositories that received malicious commits, a figure that does not necessarily indicate successful execution.
  • Executed: Aikido found indications that the stealer actually executed in at least 1,806 repositories. Its summary elsewhere cites 1,835.

The probe is a harmless pull request. StepSecurity says it does nothing by itself. It only tells the attacker that the token can write to the repository. The stealer goes in only where the token's permissions and the repository's setup allow it to run.

Aikido reports that 92% of the probe pull requests remain open, which suggests owners did not notice them. If you find one, StepSecurity's view is that the account that opened it is compromised, even if nothing else happened.

How the attack works​

Both research teams describe the same three-stage pattern.

  1. Probe. The attacker opens a pull request titled "docs: dependency audit notes" from a branch named wfx-docs. It adds a single file, docs/wfx-audit.md.
  2. Payload. Aikido's reported delivery paths are:
    • modifying an existing workflow;
    • adding a new workflow on a short-lived branch;
    • planting an npm install hook.
    StepSecurity adds a fourth hiding place: pasting the script into ordinary shell scripts that CI already runs, such as update.sh. Checking only for new workflow files can therefore miss it.
  3. Cleanup. The attacker pushes a revert commit ("ci: restore", sometimes followed by "ci: cleanup") and deletes the workflow run and its logs. Aikido says this takes under a minute.

StepSecurity gives examples. In drunomics/nuxtjs-drupal-ce, the Actions history jumps from run #1963 to #1965 because the run for the injected commit is gone. In webdriverio/webdriverio, the commits were pushed through the project's bot account, which was allowed to bypass branch protection. A contributor spotted them within 20 minutes and opened a revert. StepSecurity says no WebdriverIO package release was affected.

What the stealer goes after​

The script is about 1,500 lines long. Bleeping Computer's coverage notes it is delivered by modifying a workflow, adding a new one, or planting an npm install hook. It reportedly collects:

  • GitHub Actions secrets and variables, the workflow GITHUB_TOKEN, and OIDC and runtime tokens;
  • AWS, Azure and Google Cloud credentials, including instance metadata on self-hosted runners;
  • Git, SSH, GPG, Docker, Kubernetes, npm, PyPI, Vault and Terraform credentials;
  • API keys for AI services and developer tools such as OpenAI, Anthropic, Gemini, Hugging Face, Cursor and GitHub Copilot;
  • environment variables, .env files, keystores, shell histories and workflow artifacts.

StepSecurity adds that it reads the memory of the Actions runner process (Runner.Worker), which holds decrypted secrets, including ones the workflow never asked for. That is why copies hidden in scripts can steal secrets without touching the workflow file.

Cloud persistence​

For Microsoft and Azure administrators, this is the part that matters most. The script can exchange GitHub OIDC tokens for temporary AWS, Azure and GCP cloud sessions. StepSecurity details three persistence features designed to outlast credential rotation:

  • Azure: it adds a federated identity credential named wfx-ci-<n> to Azure AD applications. Workflows in the compromised repository can then sign in to Azure with no stored secret. StepSecurity also says it replays cached Azure CLI refresh tokens to reach Microsoft Graph and Key Vault.
  • AWS: it rewrites an assumed role's trust policy to allow any AWS principal ("Principal": {"AWS": "*"}) and tries iam:CreateAccessKey for IAM users.
  • Fake passing checks: the code can mark required checks as passed. StepSecurity says none of the 101 copies it examined had this enabled.

Exfiltration changed mid-campaign​

Aikido and Bleeping Computer describe uploads over HTTP POST to two attacker-controlled servers. StepSecurity found that the campaign shifted. Telegram first appears in its samples at 01:10 UTC on November 12. From about 05:30 UTC, new copies send stolen data only to a Telegram chat and carry no collection IP. Blocking the published IP addresses therefore no longer stops newer copies.

Aikido also warns that missing wfx-loot artifacts, wfx- cache entries or WFX-BEGIN step summaries do not prove nothing was taken. The reported campaign sent data straight to its servers.

Package fallout​

Bleeping Computer reports that automated publishing carried the payload into Packagist's digitalelvis/neuronai-studio 3.14.3 and two versions of the Go module github.com/onuragtas/docker-env, v1.0.278 and v1.0.280. The researchers believe the package releases were a byproduct of automated publishing, not the main goal.

The risk differs by package:

  • Go module: Aikido says these tags contain the payload in a GitHub Actions workflow file only.
  • Packagist package: StepSecurity says 3.14.3 ships the hidden .ci-*.sh script and a postinstall hook. composer install does not run it, but npm install inside the package directory does. It advises pinning 3.14.2 or earlier.

Aikido says about 50 of the compromised users have publishing rights on ecosystems such as npm, PyPI, Go, crates and Packagist. That makes this a potential supply-chain risk beyond the repositories directly hit.

Link to GhostAction​

Accounts hit by October's GhostAction campaign were significantly more likely to be hit by wfx-loot, and in more than 100 repositories the payload was injected into GhostAction's own workflow files. Aikido assesses the two are likely the work of different actors. StepSecurity agrees that overlap does not prove a single actor, since stolen tokens are often sold or shared. Its practical advice follows from that: if you were hit in October, check for this wave too.

StepSecurity also reports that some stolen tokens were issued to the Fly.io GitHub App. It says it has not established how the tokens were obtained and has no evidence of a breach of Fly.io's infrastructure. Most of the activity used other credentials, such as personal access tokens.

What to do now​

1. Find out whether you were hit

  • Search pull requests for "docs: dependency audit notes" and branches named wfx-docs or chore/<8 characters>.
  • Search commits since November 10 for these messages:
    • ci: refresh build cache
    • ci: restore
    • ci: cleanup
    • chore: add ci helper
    • chore: add dependency audit workflow
  • For each hit, look in the Actions tab for a gap in run numbers right after the commit. StepSecurity calls this the most reliable sign that the stealer ran.
  • If a run survived, read its log. StepSecurity says lines such as archive_bytes show what the stealer reports taking.
  • Note which account pushed the commits. That account's tokens are stolen.

2. If it ran, rotate everything it could reach

  • Rotate every repository, environment and organization secret available to that job, not only the secrets the workflow names.
  • Rotate npm, PyPI and other publishing tokens, and check for package versions published after the malicious commit.
  • Rotate cloud access the workflow could obtain via OIDC, plus any long-lived keys stored as secrets.
  • On self-hosted runners, also rotate the host's own credentials.

3. Remove persistence, because rotation alone won't

  • AWS: look for trust policies containing "Principal": {"AWS": "*"} and for IAM access keys created since November 10. In CloudTrail, look for sessions named wfx-<pid>.
  • Azure: list federated identity credentials on app registrations and delete any named wfx-ci-*.
  • Vault and Kubernetes: review logins made with GitHub Actions tokens.
  • GitHub: delete leftover wfx-docs and chore/* branches, close the probe pull requests, and confirm no commit was merged on the strength of a faked check.

4. Lock down the account

  • Revoke the account's personal access tokens, OAuth grants and SSH keys, and sign out its sessions.
  • Review whether bot accounts really need to bypass branch protection. StepSecurity notes that automation accounts with bypass rights are as valuable to this attacker as maintainers.

5. Check developer machines

The npm hook runs wherever dependencies are installed. StepSecurity says the window was usually a minute or two before the revert. Anyone who pulled and ran npm install in that window ran the stealer locally and should rotate their credentials.

Hardening ideas​

These come from general CI security practice, not from the researchers. Third-party write-up Distributed Risk lists a similar set of measures:

  • Default GITHUB_TOKEN permissions to read-only.
  • Require code-owner review on workflow files.
  • Use environment reviewers.
  • Scope OIDC trust narrowly to specific repositories, branches and environments.
  • Review any bot or automation account with bypass rights.

StepSecurity also advises monitoring outbound traffic from CI runners, since the stealer must send data somewhere. That includes watching for traffic to api.telegram.org, which a build does not normally need.

Caveats​

Most of the detail here comes from the security vendors that found the campaign, Aikido and StepSecurity, and the counts are still moving. Both firms also sell detection products, though the indicators they publish are checkable by anyone. SafeDep and Endor Labs have separately tallied malicious commits and pull requests, and their figures differ from Aikido's because they measure different things. GitHub has not been cited here as confirming any of the totals.

The practical lesson is that a stolen developer token can reach everything the repository's workflows can reach. Clean branches and an empty-looking Actions page do not prove you were spared, because the attacker's own cleanup is designed to leave exactly that picture.

 

References

  1. Hackers compromise 4,800 GitHub accounts to steal cloud credentials BleepingComputer 2026-11-12T17:25:30-05:00
  2. WFX Phantom: The GitHub Actions Secret Stealer That Deletes Its Tracks - StepSecurity stepsecurity.io
  3. The wfx Campaign: Hijacked GitHub Accounts Harvest CI Secrets labs.cloudsecurityalliance.org