Hijacked GitHub Accounts Pushed a CI Secret Stealer Into Thousands of Repos, Then Deleted the Runs. Check for a Gap in Your Run Numbers, Then Rotate. Three Exfil IPs Are in Our Feed
If you maintain anything on GitHub, check three things today. First, search every repository you can write to for a pull request titled "docs: dependency audit notes" from a branch named wfx-docs. If one exists, the account that opened it is compromised, whether or not anything else happened. Second, look at your Actions history since November 10 for a missing run number, and at your commit history for "ci: refresh build cache", "chore: add ci helper" or "chore: add dependency audit workflow" followed within minutes by "ci: restore" or "ci: cleanup". Third, if you find either, rotate every secret the repository could read and go looking for the cloud backdoors described below, because rotating the secrets alone does not remove them. The three exfiltration IPs the researchers published are now in our feed.
What the researchers reported
Two primary sources, both published November 12, 2026, both read in full: Aikido Security ("wfx-loot campaign pushes CI secret stealer to tens of thousands of repositories", by Oliver Smith), which reported it first, and SafeDep ("wfx GitHub Actions credential stealer", by Kunal Singh). The Hacker News covered both the same day. These are two independent analyses that agree on the mechanism and differ on counts, because they counted different things.
The counts, each with its population:
Accounts. Aikido: 4,837 compromised accounts opened the probe pull requests. SafeDep: 4,833 accounts.
Repositories probed. Aikido: 61,764 repositories received probe pull requests. SafeDep: 61,879 probe pull requests across 61,734 repositories, about 91% still open. Aikido counted 10,431 probes in repositories the account does not own, reached through collaborator or organization access, 92% still open.
Repositories where the stealer ran. Aikido: at least 1,806 repositories with signs the payload ran, and at least 1,835 where it ran and credentials were sent out. Aikido says these figures "may understate impact" because the attacker deleted the run logs.
Repositories that received malicious commits. SafeDep: 8,093, between 12:46 UTC on Tuesday, November 10 and 04:36 UTC on Thursday, November 12, on default branches of public repositories only, so the real number is higher. Of those, 7,309 had the stealer added to an existing workflow, 771 got it as an npm postinstall hook, and 16 got a new workflow in an empty repository. 1,343 had a malicious commit authored by someone other than the owner, such as a collaborator or a bot.
So 8,093 is commits pushed (SafeDep), 1,806 is runs that executed (Aikido), and about 61,700 is repositories probed (both). They are not competing numbers.
How it works. A stolen GitHub token pushes as the victim. The attacker first opens the probe pull request to confirm write access. Then it pushes the stealer one of three ways: about 1,500 lines added to an existing workflow ("ci: refresh build cache"), a .ci-xxxxxx.sh script plus a postinstall entry in package.json ("chore: add ci helper"), or a new .github/workflows/build-audit-xxxxxx.yml on a throwaway chore/ branch. The stealer runs once, and the attacker reverts within about a minute and deletes the workflow run and its logs. In Aikido's words, a gap in the run numbers "may be the only trace."
What it takes. Both analyses list the same scope: Actions secrets and variables, the workflow's GITHUB_TOKEN, OIDC and runtime tokens, secrets read out of the memory of the runner's own processes, credential files for AWS, Azure, GCP, Kubernetes, Docker, npm, PyPI, SSH, GPG, Vault and Terraform, AI provider keys, shell history, .env and .tfstate files, and on self-hosted runners the cloud instance metadata, Docker state and systemd units. It sends a gzipped bundle by HTTP POST to port 8080, path /exfil, or to a Telegram chat.
What it leaves behind. This is the part that rotation does not fix. The stealer uses the workflow's OIDC token to get into the victim's clouds and plants persistence there. On AWS it creates new access keys, opens role trust policies to any AWS account ("Principal": {"AWS": "*"}, per Aikido), and uses sessions named wfx-something. On Azure it adds a federated identity credential named wfx-ci-something to Entra app registrations, which Aikido says survives token rotation. It also forges "success" status checks to get past branch protection.
Packages touched. Both name the Packagist package digitalelvis/neuronai-studio version 3.14.3 (running npm install inside the vendored package triggers it; composer alone does not) and the Go module github.com/onuragtas/docker-env versions v1.0.278 and v1.0.280 (payload only in a workflow file; Go code unchanged). SafeDep adds github.com/onuragtas/redock v1.0.278, v1.0.280 and v1.0.282, the docker-env v1.0.282 build, and github.com/vkuznet/gocurl pseudo-version v0.0.0-20261111095532-8d5aad0cb0da. SafeDep scanned 218 npm, Go and Packagist packages and found no other published version carrying the payload; it did not check PyPI. Aikido counts at least 50 compromised users who publish packages. SafeDep found 28 WebdriverIO repositories with commits from the wdio-bot account and says no package release was affected.
What it means
The workflow file is not the attack surface here. The maintainer's token is. Every repository a stolen token can push to becomes a place to run code next to your secrets, and a one-minute revert plus a deleted run means the repository looks clean by the time anyone looks. Anything that inspects only the current state of your workflows will miss this. The history, the run numbers and your cloud audit logs are where the evidence sits.
What to do
Find the probe. Search the repositories and organizations you can write to for pull requests titled "docs: dependency audit notes" or from a wfx-docs branch, opened on or after November 10. If you find one, revoke that account's personal access tokens, OAuth app grants, SSH keys and sessions, and turn on two-factor authentication. Close the pull request and delete the branch. Both sources treat that account as the entry point.
Find the payload. Search the commit history on every branch since November 10 for the commit messages "ci: refresh build cache", "chore: add ci helper", "chore: add dependency audit workflow", "chore: initial commit", "ci: restore" and "ci: cleanup"; for files named .github/workflows/build-audit-something.yml or .ci-something.sh; for a new postinstall entry in package.json; and for branches named chore/ followed by eight characters or ci-check- followed by four digits. Aikido also lists the workflow step names "Pass runtime" and "Restore cache j632" and a hardcoded token value, b42d70a7aab9140ca19e8249, in a field called WF_CTOKEN.
Read the Actions history, not the logs. The logs are gone. Look for skipped run numbers since November 10 and for runs triggered by a push you do not recognize. Aikido notes that wfx-loot artifacts, wfx- cache entries and step summaries were switched off in this campaign, so do not treat their absence as a clean bill.
If the payload ran, rotate everything the repository could read: repository, environment and organization secrets, variables, cloud keys, registry tokens, AI provider keys, and anything on a self-hosted runner. Aikido: "If the payload ran in your repository, assume every secret and cloud role available to its workflows is compromised."
Then hunt the cloud backdoors, because rotation does not remove them. In AWS CloudTrail, look for AssumeRoleWithWebIdentity or AssumeRole sessions named wfx-something followed by CreateAccessKey or UpdateAssumeRolePolicy, and check every role trust policy for a wildcard principal. In Entra ID, look for federated identity credentials named wfx-ci-something that you did not create. In GCP, look for GenerateAccessToken and SignJwt calls from your GitHub workload identity pool. In Vault, look for JWT logins from the GitHub OIDC issuer.
If anyone ran npm install on an affected commit, treat that machine as compromised and rotate its keys. The same goes for anyone who installed or opened neuronai-studio 3.14.3. Pin 3.14.2 or earlier and block the Go versions listed above.
Rebuild anything published from a poisoned commit. SafeDep: rebuild and republish any package version built between the malicious commit and the cleanup commit, from a clean commit.
Block the exfil servers on any egress you control, including self-hosted runners: 194.150.166.244, 18.143.168.96 and 153.75.82.159, TCP 8080. The second is a cloud address and may be reassigned, so treat a hit as a reason to look.
Make the next one harder. Require two-factor authentication for every member and outside collaborator, prefer fine-grained tokens with short expiry, require review on default branches, scope OIDC trust in your clouds to specific repositories and branches rather than an organization, and give workflows the least GITHUB_TOKEN permission they need.
What is not known
How the attacker got the GitHub credentials; neither source says. Who the attacker is: neither names one. Aikido found that accounts hit by the October GhostAction wave were "significantly more likely than baseline" to be hit again, but concludes: "Despite the overlap in accounts, we assess that these are distinct campaigns likely carried out by different threat actors." It cites different infrastructure, tooling and tradecraft, and Indonesian-language comments in newer versions of the script. How many organizations lost cloud access, as opposed to secrets. Whether the campaign has stopped: SafeDep says it was still active when its search ran, and The Hacker News found new probe pull requests after Aikido's search ended.
How we know, and how we checked
We read both primaries in full and The Hacker News report. We ran no stealer, tested no repository, and did not check any victim repository on GitHub.
Our feed: before this post, none of the three IPs and not the stealer hash were in our corpus. We added all four, credited to Aikido and SafeDep, at confidence 80 to 90 under the source tag manual-batch-github-ci-stealer-2026-11, and read them back after writing. They are the researchers' indicators, not our observations. We did not add the package names, branch names, commit messages or the callback token, because those describe the stealer inside a victim's repository, not infrastructure a defender can block, and the package names belong to legitimate projects with one poisoned version. We also did not add github.com or any victim repository.
The October GhostAction exfil server, 193.32.204.199, has been in our feed since October 11 from our earlier post on that wave. Aikido treats November as a separate campaign, and so do we.
Our edge: this attack happens inside GitHub's runners and the victim's clouds, where our edge cannot see. We checked our own edge records since August 1 for the three exfil IPs as a source of traffic to us (368,702 blocked requests, 121,907 decoy hits and 5,594 requests that reached our origin) and found none from any of them. That is expected: they are collection servers that receive stolen data, not scanners. A zero here says nothing about whether the campaign is active, and nothing about your repositories.
Sources
Aikido Security, "wfx-loot campaign pushes CI secret stealer to tens of thousands of repositories," Oliver Smith, November 12, 2026, aikido.dev/blog/compromised-github-accounts-push-ci-secret-stealer-to-tens-of-thousands-of-repositories. SafeDep, "wfx GitHub Actions credential stealer," Kunal Singh, November 12, 2026, safedep.io/blog/github-actions-wfx-credential-stealer. The Hacker News, "Attacker Used 4,837 Hijacked GitHub Accounts to Run a CI Secret Stealer in 1,806+ Repositories," Swati Khandelwal, November 12, 2026.
Every indicator in this post is in the feed. Free.
1.58M+ IOCs, STIX 2.1 / TAXII, 88% novel vs ThreatFox, exploited-CVE leads ahead of CISA. No credit card — a free API key in 30 seconds, and you can audit every claim above against the live endpoints.



Comments