If your CI/CD pipeline holds AWS keys, npm tokens or AI service API keys, one hijacked GitHub account is enough to send all of them out of your organisation within minutes. That is what happened in an attack disclosed this week, and it matters both to companies that build software in-house and to those who outsource it.
What happened to the repositories
According to The Hacker News (published 12 November 2026), citing analysis by Aikido Security, the attack began on Tuesday. The attacker used stolen credentials for 4,837 GitHub accounts to plant a secret stealer in repositories those accounts could write to. It ran in at least 1,806 of them.
Each attempt started with a test pull request titled docs: dependency audit notes, opened from a branch named wfx-docs, to check whether the account could write to the repository. These test pull requests appeared in 61,764 repositories. SafeDep counted 8,093 repositories that received malicious commits, and said the real total is higher because it only searched default branches of public repositories.
The worrying part: after pushing the code, the attacker usually reverted the change within minutes and deleted the run logs. A glance at your commit history may show nothing unusual.
What the stealer takes from a runner
It collects everything a workflow can reach: GitHub Actions secrets and variables, GITHUB_TOKEN, OIDC tokens, AI service keys, and secrets held in the memory of the runner's processes. It also reads credential files for AWS, Azure, GCP, Kubernetes, npm, PyPI and SSH from their usual locations.
Then it uses what it finds to go deeper into cloud accounts. It exchanges GitHub OIDC tokens for AWS, GCP and Azure sessions, and on AWS it can create long-lived access keys for IAM users. Some steps are built to survive key rotation: rewriting an AWS role's trust policy so any AWS account can assume it, or adding federated credentials prefixed wfx-ci- to Azure AD applications so workflows can sign in without a stored secret.
In 7,309 repositories the stealer was added to an existing workflow. In 771 it arrived as a postinstall hook in package.json, which also runs on a developer's machine during npm install.
Check your repositories from Tuesday onward
For every repository your team or contractors can write to, look for these signs (taken from The Hacker News list):
- Branches named
wfx-docs,ci-check-NNNN, or short-livedchore/branches with eight random characters. - A pull request titled
docs: dependency audit notes. - Commits with messages such as
ci: refresh build cache(adds roughly 1,500 lines to a workflow),chore: add ci helper(adds a.ci-xxxxxx.shscript and apostinstallhook),chore: add dependency audit workflow,ci: restoreorci: cleanup. - Unfamiliar workflow files such as
.github/workflows/build-audit-xxxxxx.yml.
One useful tip: a missing run number on the attacker's commit is a more reliable sign that the stealer ran than searching for wfx- artifacts, because a repository can lose data without leaving any artifact.
You can start with this on a local clone to inspect history, including branches no longer visible in the UI:
git fetch --all --prune
git log --all --since="2026-11-10" --oneline \
--grep="ci: refresh build cache" \
--grep="chore: add ci helper" \
--grep="chore: add dependency audit workflow"
grep -R "postinstall" package.jsonThis only matches the published commit messages. An empty result is not proof of safety, so also review your organisation's GitHub audit log.
If you were hit: the rotation order
Treat every secret and cloud role the workflows could read as compromised. SafeDep recommends:
- Revoke the commit author's tokens, OAuth app grants, SSH keys and sessions, and enable two-factor authentication.
- Rotate every secret and variable the repository can read.
- Treat any machine that ran
npm installon an affected commit as compromised and rotate its keys.
Because the malware may leave backdoors that outlive rotation, also review the cloud side:
- Entra ID: federated credentials named
wfx-ci-*that you did not create. - AWS CloudTrail:
AssumeRoleorAssumeRoleWithWebIdentitysessions namedwfx-*, followed byCreateAccessKeyorUpdateAssumeRolePolicy. - GCP:
GenerateAccessTokenandSignJwtcalls from your GitHub workload identity pool. - Vault: JWT logins from the GitHub OIDC issuer.
Reducing the blast radius next time
This wave shows that protecting accounts is not enough; assume a workflow will one day run code that is not yours. A few things to do this week:
- Prefer OIDC with least-privilege, short-lived cloud roles scoped to repository and branch, instead of long-lived keys stored as secrets.
- Set the default
GITHUB_TOKENpermission to read-only and raise it per job only where needed. - Enable branch protection and required reviews for changes under
.github/workflows/. - Separate secrets by environment (staging, production) and use environments with approvers for deployment steps.
- Audit contractors and collaborators: who still has write access, and who has 2FA enabled.
We have written before about how GitHub handles leaked keys in GitHub secret scanning, and about the consequences of hardcoded secrets in the Wyndham data exposure (both in Vietnamese). The lesson is the same: attackers go wherever secrets live. If you outsource development, see how we work on the Besoft projects page.
A good first step this morning: pick your five most important repositories, run the check above, compare against the audit log, and write down which secrets each workflow reads. If that list is longer than you expected, it is time to cut it down.
Sources
- The Hacker News, "Attacker Used 4,837 Hijacked GitHub Accounts to Run a CI Secret Stealer in 1,806+ Repositories", 12 Nov 2026: https://thehackernews.com/2026/11/4837-hijacked-github-accounts-ran-ci.html
- SafeDep technical analysis: https://safedep.io/blog/github-actions-wfx-credential-stealer
- Aikido Security technical analysis: https://www.aikido.dev/blog/compromised-github-accounts-push-ci-secret-stealer-to-tens-of-thousands-of-repositories






