Contents
ToggleExecutive Summary
wfx is a GitHub Actions credential stealer spread by an active supply chain campaign. The attacker uses stolen GitHub tokens. With them, they plant the stealer in every repository the hijacked account can write to.
The stealer runs once, in GitHub Actions or during npm install. It collects every secret and cloud credential the CI job can reach. Then it sends the loot to attacker servers or a Telegram chat.
This is not a classic dependency hijack. The attacker wants CI secrets and cloud access, not package installs. In almost every repository the malicious commit is reverted within minutes. Afterwards the branch looks clean.
SafeDep counted 8,093 repositories with malicious commits between November 10, 2026 at 12:46 UTC and November 12 at 04:36 UTC. The campaign was still active when they searched. Aikido Security reported the campaign first. They tracked 4,837 hijacked GitHub accounts and confirmed the stealer ran in at least 1,806 repositories.
The real number is higher. The search covered only default branches of public repositories. Private repos, other branches, and repos that got only the probe pull request are not counted.
The biggest danger is persistence. The payload doesn’t stop at theft. On AWS it creates long-lived access keys and rewrites role trust policies. On Azure it adds a federated identity credential named wfx-ci-* to Entra app registrations. With it, any workflow on the repo’s main branch can sign in to Azure, even after every client secret is rotated.
So rotating repository secrets alone does not remove this attacker.

TL;DR for Engineering Teams
What it is: wfx, a GitHub Actions credential stealer pushed through commits made with stolen tokens. There is no CVE. This is a campaign, not a product flaw. The payload is a shell script or workflow step of about 1,500 lines.
Where it hits: Any public or private repo a hijacked account can write to, including through collaborator or organization access. The stealer runs in GitHub Actions on push. The npm postinstall variant also runs on a developer’s laptop during npm install.
Why it matters: 8,093 repos got malicious commits. The stealer ran in at least 1,806. It takes CI secrets, cloud credentials, API keys from memory, and credential files. Then it plants AWS and Azure backdoors that survive secret rotation.
Patch status: There is no patch. This is about stolen credentials, not a code bug. The fix is GitHub token hygiene and hunting for cloud backdoors.
Do this now: If a repo you own got a wfx-docs probe pull request or a malicious commit, revoke the commit author’s tokens, OAuth grants, SSH keys and sessions. Turn on 2FA. Rotate every secret the repo can read. Then hunt for wfx-ci-* federated credentials in Entra ID and wfx-* sessions in CloudTrail.
Vulnerability Overview
| Field | Value |
|---|---|
| Vulnerability Type | Supply chain credential theft via stolen-token commits; CI/CD workflow injection |
| CWE | CWE-506 (Embedded Malicious Code); CWE-522 (Insufficiently Protected Credentials); CWE-349 (Acceptance of Extraneous Untrusted Data) |
Technical Anatomy
Root Cause
There is no memory bug or parser flaw here. The root cause is trust in write access, plus how much GitHub Actions can reach.
If an attacker has a valid GitHub token, GitHub accepts their commits as real. The commits show up as the real user, with their noreply email. They are not signed. No signature plus full write access is the gap the attacker uses.
Once a workflow runs, GitHub Actions gives it a lot: secrets, variables, the GITHUB_TOKEN, runtime tokens, and an OIDC identity. That OIDC identity can be swapped for cloud sessions. The stealer cashes all of it in one run.
The name wfx comes from the prefix the payload puts on its files, branches, and cloud objects.
Exploit Path
Entry point. The attacker pushes to a repo with a stolen token. First they open a probe pull request called docs: dependency audit notes from a branch named wfx-docs. It adds a small notes file. It only tests if the token can write.
Aikido saw these probe pull requests in 61,764 repos. SafeDep counted 61,879 of them across 61,734 repos from 4,833 accounts. 91 percent were still open when they checked. A probe pull request alone means the account is compromised, even if no payload came after it.
Planting the stealer. Once write access is confirmed, the attacker uses one of three methods:
- Code added to an existing workflow (
ci: refresh build cache): 7,309 repos. - An npm postinstall hook (
chore: add ci helper): 771 repos. This one also runs on developer machines. - A new workflow file in an empty repo (
chore: add dependency audit workflow): 16 repos. Achore: initial commitcomes first, so there is something to inject into.
The malicious workflow asks for id-token: write and statuses: write. It copies the Actions runtime tokens into the job environment. Then it passes all secrets and variables to the payload as JSON.

What it does. The payload runs in CI and takes everything it can reach:
- GitHub secrets and variables, the
GITHUB_TOKENand runtime tokens - API keys found in the runner’s process memory and environment
- Credential files: cloud CLI configs,
~/.ssh,~/.gnupg, registry tokens, kubeconfig, shell history, and AI tool configs
Then it asks for GitHub OIDC tokens for AWS, Azure, and GCP and tries to use them. It also replays the OIDC token against HashiCorp Vault and Kubernetes APIs.
The data goes out by HTTP POST to attacker servers or to a Telegram chat. After the run, the attacker pushes ci: restore and ci: cleanup to remove the payload. Where the stealer ran, its logs are deleted, and often the run itself too.
Campaign Phases
Phase 1, foothold. A stolen GitHub token opens a probe pull request from wfx-docs to check write access. SafeDep notes a big overlap with the October GhostAction wave. Accounts hit in October were about 1,000 times more likely than other active accounts to be reused this week.
Phase 2, expansion. The stealer goes in through workflow injection, a new workflow, or an npm postinstall hook. All three run in CI. The postinstall variant also runs on developer laptops.
Phase 3, packages. Automated publishing pushed the payload into three packages: the Packagist package digitalelvis/neuronai-studio at v3.14.3, and two Go modules, github.com/vkuznet/gocurl and github.com/onuragtas/redock (also served as docker-env). SafeDep thinks the attacker wants CI secrets, not package spread. The packages got hit as a side effect. Release automation fired on the attacker’s push.
Phase 4, persistence. On AWS the payload creates new access keys. It rewrites role trust policies so any account can assume the role. Session names look like wfx-<pid>. On Azure it adds a federated identity credential named wfx-ci-<n> to Entra app registrations. After that, workflows can sign in without a stored secret, even after rotation.
Indicators of Compromise
| Type | Indicator | Notes |
|---|---|---|
| IPv4 | 194.150.166[.]244:8080/exfil | Main HTTP exfil server; about half of sampled commits |
| IPv4 | 18.143.168[.]96:8080/exfil | HTTP exfil server (AWS ap-southeast-1 range; may be reassigned) |
| IPv4 | 153.75.82[.]159:8080 | Extra exfil server reported by SafeDep; about 1% of sample |
| Telegram chat id | -1004340163187 | Attacker collection chat; do not contact |
| SHA-256 | 89695abb392d5cfd4972672961e644bb46baa98aa1ed7173c2a2816c762735cf | Reported stealer sample; not the same across all variants |
| Branch | wfx-docs | Probe pull request branch |
| Branch | ci-check-[0-9]{4} | Reported pattern linked to earlier activity |
| PR title | docs: dependency audit notes | Probe pull request |
| Commit msg | ci: refresh build cache | Workflow injection; adds about 1,500 lines |
| Commit msg | chore: add ci helper | Adds .ci-xxxxxx.sh plus postinstall hook |
| File | .github/workflows/build-audit-[a-z0-9]{6}.yml | New malicious workflow (about 1,519 lines) |
| File | .ci-[a-z0-9]{6}.sh | Root shell payload (about 1,440 to 1,493 lines) |
| Azure | wfx-ci-* federated credential | Backdoor on Entra app registrations |
| AWS | wfx-* role session name | Followed by CreateAccessKey or UpdateAssumeRolePolicy |
| Runner path | $RUNNER_TEMP/.wfx-<pid>/ | Payload staging directory |
A repo can show none of the wfx-loot- artifacts, wfx- cache entries, or WFX-BEGIN step summaries and still have lost data. Those channels were turned off in the analyzed samples. A missing run number on the attacker’s commit is a better sign that the stealer ran.
Affected Versions
| Package | Vulnerable Versions | Fixed Version | Notes |
|---|---|---|---|
digitalelvis/neuronai-studio (Packagist) | v3.14.3 | v3.14.2 or earlier (pin back) | composer install does not run the payload. npm install inside the vendor dir does. Packagist still serves v3.14.3 as latest. |
github.com/vkuznet/gocurl (Go) | v0.0.0-20261111095532-8d5aad0cb0da | None (block the pseudo-version) | The payload sits in a workflow that has never run. Go tooling does not run workflow files. The proxy caches the pseudo-version for good. |
github.com/onuragtas/redock / docker-env (Go) | v1.0.278, v1.0.280, v1.0.282 | v1.0.277 (.279/.281/.283 are clean reverts) | Payload only in beta.yml, which has not run since the attack. Block the poisoned versions. |
SafeDep also scanned 218 npm, Go, and Packagist packages whose source repos got malicious commits. None of the published versions had the payload. PyPI and other registries were not checked.
The payload also reached 28 WebdriverIO repos through the wdio-bot account. No package release was affected, but treat the wdio-bot token as stolen.
Exposure Analysis
| Environment | Risk Level | Reason |
|---|---|---|
| CI/CD pipelines | Critical | Direct target. Secrets, variables, GITHUB_TOKEN, OIDC and runtime tokens are all exposed in one run. |
| Cloud workloads | Critical | OIDC is swapped for AWS, Azure, and GCP sessions. AWS key creation, trust-policy rewrites, and Azure federated-credential backdoors survive rotation. |
| Developer environments | High | The npm postinstall variant runs on any machine that runs npm install on an affected commit. Local keys, SSH, and shell history are exposed. |
| Internet-exposed services | Medium | Not the entry point. But stolen cloud credentials open a path into exposed infrastructure. |
Scale: 8,093 repos with malicious commits (SafeDep). The stealer ran in at least 1,806 (Aikido). 61,734+ repos got a probe. 4,837 accounts were hijacked. At least 50 of the compromised users publish packages for npm, Go, Packagist, PyPI, or crates.
Real-World Impact
Think about what a typical CI secret set holds. Registry publish tokens. Cloud deployment credentials. Third-party API keys. So account takeover and cloud credential theft happen right away.
In the 1,806+ repos where the stealer ran, the pipeline is compromised. Because it reads the runner’s process memory, it also gets short-lived tokens that never touch a file. On self-hosted runners it goes further. It reads instance metadata, Docker container state, and systemd units. That opens up the host and its network.
The persistence is what makes this more than a one-time theft.

Take an AWS role whose trust policy now accepts any account. Or an Entra app with a wfx-ci-* federated credential. Both stay open to the attacker after you rotate every secret in the repo.
Teams that rotate secrets and close the pull request will think they’re done. If they skip the cloud hunt, the attacker still has access.
Detection Guidance
Log Indicators
Search all branches, not just the default one, from November 10 onward. Look for:
- the
wfx-docsbranch - the pull request title
docs: dependency audit notes - the commit messages
ci: refresh build cache,chore: add ci helper,chore: add dependency audit workflow,ci: restore, andci: cleanup
In workflow diffs, look for a step that re-exports ACTIONS_RUNTIME_TOKEN and ACTIONS_RESULTS_URL into GITHUB_ENV. Also look for env variables with the prefixes WF_CALLBACK, WF_CTOKEN, WF_TG_BOTS, WF_TG_CHAT, WF_MEMSCAN, and WF_EXFIL_.
In network logs, hunt for POSTs to the three exfil IPs. Also look for unexpected CI traffic to api.telegram.org using sendDocument. Don’t block api.telegram.org outright. It’s a shared, legitimate domain. Match it with the campaign context instead.
Scanner References
Phoenix Security scanners match the poisoned package coordinates and campaign IOCs against your repos and runtime inventory. Combine that with:
- SCA and SBOM analysis, to find the three poisoned package versions on direct and transitive paths
- GitHub dependency scanning for the affected coordinates
- external attack surface scanning, to find exposed services running code from a compromised pipeline
A low heuristic score on any of these findings doesn’t mean low risk. This payload is about credentials. It won’t look like a normal vulnerable dependency.
Verification Steps for Teams
- Search lockfiles and manifests for
digitalelvis/neuronai-studio@3.14.3,github.com/onuragtas/redock/docker-envat v1.0.278, v1.0.280 or v1.0.282, and the gocurl pseudo-version. - Run dependency scans across all repos, including transitive paths. Check the SBOM for the affected coordinates.
- Query GitHub audit logs for the campaign branches, pull request titles, and commit messages across every branch.
- Check your external attack surface for services deployed from any affected pipeline.
- In your cloud accounts, hunt for
wfx-ci-*Entra federated credentials,wfx-*CloudTrail sessions followed by CreateAccessKey or UpdateAssumeRolePolicy, GCP GenerateAccessToken and SignJwt calls from the GitHub workload identity pool, and Vault JWT logins from the GitHub OIDC issuer.
Remediation Guidance
Immediate Actions
- For any repo with a probe pull request or malicious commit, revoke the commit author’s GitHub tokens, OAuth app grants, SSH keys, and sessions. Turn on 2FA. That account is the likely way in.
- Rotate every secret and variable the repo can read. Include environment and organization secrets, and any key the runner could reach.
- Delete the
wfx-docsbranch and close the probe pull request. - Pull any package version built between the malicious commit and the
ci: cleanupcommit. Publish again from a clean commit. - If a developer machine ran
npm installon an affected commit, treat it as compromised. Rotate its keys. - Remove the cloud backdoors. Delete unknown
wfx-ci-*Entra federated credentials. Revert rewritten AWS role trust policies. Delete IAM access keys the attacker created.
Block and pin the three poisoned packages to the versions in the Affected Versions table. For neuronai-studio, pin v3.14.2 or earlier. Packagist still lists v3.14.3 as latest.
Temporary Mitigations
If full account and secret rotation will take time, shrink what the attacker can reach:
- Set repo and organization Actions permissions to the minimum.
- Remove
id-token: writefrom workflows that don’t need OIDC. - Require signed commits and branch protection on default branches. Unsigned pushes with stolen tokens will be rejected.
- Scope cloud OIDC trust policies to specific repos, branches, and workflows, not a broad
repo:*subject. This blocks the OIDC swap even if a token is stolen.
One note on --ignore-scripts. It stops the npm postinstall variant. It does nothing for the workflow-injection and new-workflow variants. Those run fully in CI.
Phoenix Security Recommendations
A campaign like wfx leaves you with thousands of scattered signals. Phoenix Security turns them into one backlog with owners.
Threat intelligence. Phoenix Blue tracks supply chain campaigns like wfx, including poisoned package versions and IOCs. You can check if you’re in scope at phxintel.security.
Contextual deduplication. The poisoned package coordinates and IOC hits from all your scanners land in one prioritized list. No duplicate findings per tool.
Reachability analysis. It shows which poisoned versions and compromised pipelines actually reach your runtime. Your team fixes what matters, not every transitive match.
Remediation campaigns. Create a wfx response campaign. Assign owners per repo and per cloud account. Track secret rotation and backdoor removal. Then check runtime exposure once the fixes land.
Ownership attribution. Each affected repo and pipeline maps to the right team automatically.
Attack surface management. Find internet-exposed services deployed from a compromised pipeline.
The result: thousands of probe pull requests and malicious commits become a scoped backlog. Every item has an owner and can be checked.
Protect yourself with the latest threat intelligence, get free access to Phoenix Blue Today
External References
- SafeDep, wfx: GitHub Actions Credential Stealer Hits 8,000 Repos (Kunal Singh, November 12, 2026). Main technical analysis used in this article.
- 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).
- GitHub Docs, Audit log events for your organization. Reference for workflow run events, including deleted runs.
















