
Hijacked GitHub accounts let wfx-loot steal CI secrets from 1,806 repos - Pasquale Pillitteri
An attacker used stolen GitHub credentials to open probe pull requests in 61,764 repositories, with the wfx-loot script successfully shipping secrets and cloud keys from at least 1,806 of them, according to research from Aikido Security.
The wfx-loot campaign began on November 10, 2026, around 12:45 UTC, when an attacker used stolen GitHub credentials from 4,837 accounts to open probe pull requests in 61,764 repositories. The probe was a simple check to confirm the stolen token had write access, but in at least 1,806 repositories, the attacker's script actually ran and exfiltrated secrets to a server under their control.
The attack exploited the nature of GitHub Actions, the automated CI/CD pipeline that holds sensitive cloud provider keys, tokens, and credentials. Anyone with write access to a repository can run their own code inside the pipeline, allowing them to steal all secrets stored within it. Aikido Security, a firm focused on software supply chain security, named the campaign wfx-loot, with 'wfx' being the malware's self-label and 'loot' referring to the stolen data.
The attack window was narrow, with probe pull requests appearing between November 10 at 12:45 UTC and November 11 at around 15:00 UTC. A GitHub search returned 61,839 of these requests, and in 10,431 cases, the request landed in a repository the hijacked account did not own, reached through collaborator or organization membership access. At the time of the search, 92% of those requests were still open, meaning the repository owners may not have noticed anything.
The first victim to speak up was a user who flagged a GitHub CLI OAuth token that had ended up in the wrong hands at 06:33 UTC on November 11. Shortly after, the web testing framework webdriverio reported two commits pushed under its bot account that bypassed protection rules on the main branch. A contributor flagged these within about twenty minutes, as Endor Labs reported.
The impact extended beyond repositories to package registries, with at least 50 hijacked users publishing packages on npm, Go, Packagist, PyPI, or crates. Aikido confirmed versions to avoid only on Packagist (digitalelvis/neuronai-studio 3.14.3) and on Go (github.com/onuragtas/docker-env v1.0.278 and v1.0.280). On Packagist, every machine that installed that version should be treated as compromised.
The wfx-loot script moves in three steps: a probe, a payload, and cleanup. The probe was a pull request titled 'docs: dependency audit notes' from a branch called wfx-docs, which existed only to confirm the token's write permissions. The payload was a 1,500-line shell script that entered the repository in one of three forms, and the cleanup involved a revert commit and deletion of the workflow run and logs.
The script harvests secrets in four stages, beginning with a sweeping inventory of Actions secrets, variables, the GITHUB_TOKEN, OIDC tokens, credential files for various cloud providers, SSH keys, shell history, and .env files. It also harvested keys for AI services like OpenAI, Anthropic, and Hugging Face, and even the memory of the runner's processes.
The second stage is escalation, where the script uses OIDC tokens to obtain real cloud sessions on AWS, Google Cloud, and Azure, and on AWS it creates long-lived access keys. The third stage, persistence, involves forging passing results for branch protection checks and rewriting AWS role trust policies to accept any AWS account, as well as adding Azure federated credentials with the wfx-ci- prefix. These doors remain open even after secrets are changed.
The final stage is exfiltration, where the harvested data is compressed into an archive and sent over HTTP to one of two attacker-controlled servers on port 8080. The attack has similarities to the GhostAction campaign from October, which used about 1,900 GitHub accounts to hit more than 30,000 repositories. Aikido believes the two attacks are the work of different actors, as their infrastructure and tooling do not match.
To spot the intrusion, researchers recommend looking for pull requests titled 'docs: dependency audit notes' or branches named wfx-docs, chore/ followed by eight random characters, or ci-check- followed by four digits. Commits with messages like 'ci: refresh build cache' or 'chore: add ci helper' are also signs. A .ci-xxxxxx.sh file or a build-audit-xxxxxx.yml workflow, even if reverts have removed them, is another indicator.
If a repository was hit, the affected account should be considered compromised, and every secret and cloud role reachable from its workflows should be treated as lost. The recommended order of operations is to revoke sessions, personal tokens, SSH keys, and OAuth authorizations, then force a new password with phishing-resistant multi-factor authentication. Repository and organization secrets, cloud keys, package registry tokens, and AI service keys must be rotated.
Cloud persistence must be removed by checking AWS role trust policies, IAM keys created since November 10, and Azure wfx-ci-* federated credentials, as rotating secrets does not touch them. Probe pull requests should be closed, and branches like wfx-docs, chore/, and ci-check- should be deleted. Finally, cloud logs and Entra audit logs should be reviewed.
For the medium term, repositories should require code owner approval for .github/workflows/**, block direct pushes to the main branch even for admins, and keep the GITHUB_TOKEN read-only by default. OIDC trust should be tied to specific repositories, branches, and environments, and npm ci --ignore-scripts should be used wherever possible.