Threat actors used thousands of hijacked GitHub accounts to inject a self-erasing CI secret stealer into tens of thousands of repositories, stealing cloud, package registry and AI credentials
Overview
OX Security researchers are tracking a large-scale supply chain campaign, dubbed wfx-loot, in which threat actors used 4,837 hijacked GitHub accounts to push a CI secret stealer into repositories those accounts own or have write access to.
The injected code runs inside GitHub Actions workflows and on developer machines through npm install hooks. It collects every secret and credential it can reach, sends them to attacker-controlled servers, then reverts the malicious commit and deletes the workflow run and its logs. Combined, the attackers opened over 66,000 probe pull requests, pushed malicious commits into more than 8,000 public repositories, and successfully exfiltrated secrets from at least 1,806 repositories.
The numbers we’re seeing are constantly changing, the campaign is ongoing, meaning IP addresses, C2 servers and Telegram bot groups will continue to change as the threat is evolving through GitHub.
Key Findings
The attacker first opens a harmless looking “docs: dependency audit notes” pull request to confirm write access, then pushes a malicious commit disguised as routine CI maintenance, and reverts it within seconds to minutes. Because the workflow run and its logs are deleted, the most reliable trace left behind is a missing workflow run number.
The malware isn’t obfuscated – it’s a single shell script of roughly 1,500 lines that steals GitHub tokens, AWS, Azure and GCP credentials, npm, PyPI, Packagist and crates.io publishing tokens, Kubernetes and Vault secrets, and AI provider API keys. Similar to our previous research documenting Ethersphere crypto currency hijack, threat actors rely on speed and volume instead of complexity and obfuscation.
Mid-campaign, the attackers switched exfiltration channels. Starting November 12 at approximately 05:30 UTC, new copies send stolen data through Telegram bots instead of HTTP, which means blocking the known IP addresses alone is no longer enough.
According to our research, the campaign is still live: over 4,100 new probe pull requests were opened on November 12 alone, roughly 89% of all probe pull requests are still open, and malicious payloads are still present on the default branches of dozens of public repositories.
Recommended Actions
Check for affected repositories. Search your organization for pull requests titled “docs: dependency audit notes”, branches named wfx-docs, and commits titled “ci: refresh build cache”, “chore: add ci helper”, “chore: add dependency audit workflow”, “ci: restore” or “ci: cleanup” since November 10. A probe pull request alone means the account that opened it is compromised.
Rotate secrets and revoke tokens. If a malicious commit landed in a repository with GitHub Actions enabled, treat every repository and organization secret, variable and cloud role reachable from that workflow as compromised – even if no workflow run is visible. Deleted runs leave a gap in the run numbers.
Hunt for persistence. Review Azure and Entra ID federated credentials named wfx-ci-*, AWS role trust policies that allow any principal, and AWS sessions named wfx-*. Rotating secrets doesn’t remove backdoors that were already planted.
Impact Analysis
- Hijacked Accounts: 4,837 GitHub accounts opened probe pull requests, including at least 50 accounts that publish packages to npm, Go, Packagist, PyPI and crates.io.
- Probe Pull Requests: Over 66,200 “docs: dependency audit notes” pull requests were opened across tens of thousands of repositories, up from 61,764 repositories reported on November 11. Roughly 58,900 are still open.
- Malicious Commits: 8,093 public repositories received a malicious commit on their default branch – 7,309 through workflow injection, 771 through npm postinstall hooks and 16 through new workflow files. Private repositories and non-default branches aren’t visible, so the real number is higher.
- Confirmed Exfiltration: At least 1,806 repositories ran the payload and sent their secrets to the attackers.
- Third-Party Access: 10,431 probe pull requests targeted repositories the hijacked account doesn’t own, through collaborator, organization or bot access – including 28 WebdriverIO repositories reached through the project’s bot account.
Telegram Bots
While threat actors changed their IP addresses and exfiltration techniques multiple times during the attack, we found that exfiltration shifted from C2 servers to Telegram, using five different telegram API keys, each connected to a different Telegram bot by the same threat actor.
Bot names are split from Worker1 to Worker5, and a “creator” account, with “Worker2” being kicked from managing the exfiltration group.
Telegram IDs
- @worker1qwevqqweqweqwevqw_bot
- @Worker2qweqwcvqweqe_bot
- @worker3qweqcwecqwecqwecQW_bot
- @worker4qwecqwecqwec_bot
- @Worker5qweqwceqwecqweQWce_bot
We were also able to extract the group’s creator account with his ID, display name and profile picture, alongside the group itself and its image:
- 7349133718
- NG PANEL 😜
- 0xK
- -1004340163187
- EGO


Compromised Packages
| Package | Versions | Fix Versions | Ecosystem | Status |
| digitalelvis/neuronai-studio | 3.14.3 | 3.14.4 | Packagist | Down |
| github.com/vkuznet/gocurl | v0.0.0-20261111095532-8d5aad0cb0da | Previous release | Go | Cached in Go proxy |
| github.com/onuragtas/redock (docker-env) | v1.0.278, v1.0.280, v1.0.282 | v1.0.287 | Go | Cached in Go proxy |
Infection Analysis
The malicious commits are authored as real users of each repository, using their GitHub noreply email and no commit signature – which points to stolen access tokens rather than a GitHub platform vulnerability.
The attack follows the same flow in every repository:
- Probe – a “docs: dependency audit notes” pull request from a wfx-docs branch confirms write access.
- Inject – a commit disguised as CI maintenance adds the stealer to an existing workflow, a new workflow file, an existing shell script, or an npm postinstall hook.
- Execute – the next workflow run or npm install runs the stealer.
- Erase – the commit is reverted with “ci: restore” or “ci: cleanup”, and the workflow run and its logs are deleted.
In the WebdriverIO case, two “chore: add ci helper” commits were pushed through the project’s bot account on November 11 at 12:11 UTC, bypassing branch protection, and were spotted by a contributor and reverted within about 20 minutes.
Conclusions
Coming just days after the Ethersphere hijack and the October GhostAction wave, wfx-loot shows that stolen developer tokens have become a renewable resource for threat actors. This campaign can teach us a lot about how attackers approach CI/CD compromises at scale:
Hiding the evidence beats hiding the code: Just like Ethersphere, the malware isn’t obfuscated – any human or AI could flag it in seconds. The attackers don’t care, because the code lives on the main branch for seconds to minutes and the logs are gone right after. Any detection that relies on reviewing the current state of a repository or reading workflow logs after the fact will simply miss it.
Defensive systems are still too slow: Nearly 48 hours after the first probe pull request, new ones are still being opened every few minutes, tens of thousands remain open, and malicious versions are still available on Packagist and the Go module proxy. The same pattern from Ethersphere repeats itself – one platform reacts while the other keeps serving the malicious version. A pull request titled “docs: dependency audit notes” opened by thousands of accounts within hours is not a subtle signal, and platforms should be able to block it automatically.
The more CI/CD pipelines hold the keys to production, the more attractive they become. Until GitHub tokens are short-lived by default and bot accounts stop bypassing branch protection, every stolen token is a potential entry point for the next campaign.
IOCs
Network Addresses
hxxp://194[.]150[.]166[.]244:8080/exfil
hxxp://18[.]143[.]168[.]96:8080/exfil
Telegram: api[.]telegram[.]org sendDocument to chat ID (five bot tokens)
Payload Hashes
89695abb392d5cfd4972672961e644bb46baa98aa1ed7173c2a2816c762735cf
Strings
b42d70a7aab9140ca19e8249
Telegram group ID
- -1004340163187