November 12, 2026
Posted by Akeyless Security Research
TL;DR: Beginning November 10, 2026, an automated campaign leveraged 4,837 compromised GitHub accounts to open probe pull requests across 61,764 repositories. In at least 1,806 confirmed repositories, attackers injected a 1,500-line shell payload (self-designated wfx) into GitHub Actions workflows, harvesting static CI/CD secrets, cloud IAM credentials, and AI developer tokens before reverting commits and deleting run logs, typically within a minute. Here’s how the attack chain works, how to check if your pipelines were hit, and why eliminating standing secrets and enforcing strict CI governance is the structural fix.
Introduction: The Blast Radius of Standing CI Secrets
In modern cloud-native engineering, the CI/CD runner is the single highest-value aggregation point for enterprise credentials. A single compromised developer token doesn’t just grant read access to source code; it unlocks the automated pipelines holding long-lived AWS keys, Kubernetes configs, database passwords, and AI provider API keys.
On November 10, 2026, at approximately 12:45 UTC, security researchers from Akido observed a massive, automated wave of repository intrusions exploiting exactly this architectural weakness. In the first ~26 hours alone, 4,837 compromised GitHub accounts opened 61,839 probe pull requests across 61,764 repositories, including 10,431 pull requests targeting repositories not directly owned by the compromised accounts, reached via organization and collaborator permissions.
Notably, accounts previously compromised during October’s GhostAction wave were roughly 1,000 times more likely to be leveraged in this November campaign. This underscores a harsh operational reality: when stolen GitHub developer accounts and tokens are left unrevoked, they remain usable indefinitely.
Impact
Unlike traditional supply chain worms designed mostly to poison downstream package registries, this operation appears purpose-built for rapid credential harvesting. While the payload was also pushed into at least one Packagist package (digitalelvis/neuronai-studio v3.14.3) and Go modules (github.com/onuragtas/docker-env v1.0.278 / v1.0.280 / v1.0.282, also served as github.com/onuragtas/redock, and github.com/vkuznet/gocurl v0.0.0-20261111095532-8d5aad0cb0da), the Go source code itself is unchanged; the payload resides only in a workflow file, and gocurl was simply cached by the Go proxy. The attacker’s primary objective was draining CI runtime environments.
| Metric / Indicator | Confirmed Scale & Details |
|---|---|
| Initial Measurement Window | November 10, 2026 (12:45 UTC) to November 11, 2026 (15:00 UTC) (campaign remained active as of Nov 12) |
| Compromised Accounts | 4,837 GitHub accounts (50+ package publishers) |
| Targeted Repositories | 61,764 repositories probed (10,431 probe PRs in non-owned repos via org/collaborator access) |
| Confirmed Executions | 1,806+ repositories executed the payload |
| Dwell Time Before Cleanup | Typically within a minute (commits reverted + workflow run logs deleted) |
Telemetry and campaign figures sourced from Aikido Security and SafeDep research.
Attack Flow: Probe, Inject, Escalate, Erase
To understand how the intrusion unfolds and why defenders only see subtle traces after the fact, examine the four-phase execution flow of the wfx campaign.

Phase 1: Write-Access Probe
For every repository accessible to a stolen GitHub token, the attacker’s automation creates a branch named wfx-docs and opens a pull request titled docs: dependency audit notes, which acts as an automated permissions check. Even if no follow-up payload ran on your repository, seeing this PR means the authoring account’s token is compromised.
Phase 2: Three-Variant Payload Delivery
Once write access is verified, the automation pushes a ~1,500-line un-obfuscated shell script (self-labeled wfx) using one of three methods:
- Workflow Injection (
ci: refresh build cache): Appends roughly 1,500 lines directly into an existing GitHub Actions YAML file. - Package Install Hook (
chore: add ci helper): Drops a hidden.ci-xxxxxx.shscript and registers a postinstall hook inpackage.jsonto trigger onnpm install. - Throwaway Branch (
chore: add dependency audit workflow): Creates a short-lived branch (chore/<8-random-chars>) containing.github/workflows/build-audit-xxxxxx.yml.
Phase 3: Four-Stage wfx Payload
Inside the injected workflow step (a step named like Restore cache j632), the attacker dumps the entire context (${{ toJSON(secrets) }} and ${{ toJSON(vars) }}) into environment variables and runs the shell script across four modules:
- Collect: Scrapes
GITHUB_TOKEN, Actions runtime tokens, environment variables matching keywords (aws, azure, gcp, openai, anthropic, stripe, token, secret), credential files (~/.aws,~/.kube,~/.ssh,.env), shell histories, and memory fromRunner.Workerprocesses. On self-hosted runners, it also queries cloud instance metadata (IMDS) and inspects Docker/systemd states. - Escalate: Mints GitHub OIDC tokens to exchange for AWS, GCP, and Azure cloud sessions, pivots using cached Azure CLI FOCI refresh tokens, and replays tokens against HashiCorp Vault and Kubernetes API servers.
- Persist: Forges passing status checks against commit SHAs to bypass branch protection. To survive reactive secret rotation, it also rewrites AWS IAM trust policies to “Principal”: {“AWS”: “*”} and injects rogue federated identity credentials (
wfx-ci-…) into Entra ID applications. - Exfiltrate: Compresses loot into a gzipped tarball and transmits it via HTTP POST to 194.150.166[.]244:8080/exfil, 18.143.168[.]96:8080/exfil, or 153.75.82[.]159:8080/exfil, or exfiltrates via Telegram, which accounts for roughly a fifth of observed samples (with dormant capabilities for GitHub Actions Cache, Step Summaries, and Artifacts).
Phase 4: Anti-Forensics Cleanup
Typically within a minute of execution, the automation reverts the malicious commit and deletes the workflow run along with its execution logs. For defenders, a gap in sequential workflow run numbers (e.g., Run #412 jumping directly to Run #414) is often the only primary forensic artifact left behind.
Why Static Secrets Turn a Single Token Leak into a Cloud Breach
The wfx campaign succeeds because most CI/CD architectures still rely on standing, static secrets stored inside repository settings or .env files, combined with unprotected workflow execution paths.

When long-lived cloud keys or database credentials sit in repository or organization secrets, any workflow pushed to any branch by an account with write access can read them. Environment secrets guarded by GitHub deployment protection rules (such as required reviewers) are the main native exception, because jobs pause for approval before receiving environment-scoped credentials. Even worse, when organizations rotate leaked secrets manually after an incident, cloud persistence mechanisms, such as modified IAM trust policies or rogue Entra ID federated credentials, allow attackers to maintain access.
How to Check If You Were Affected
If your organization uses GitHub Actions, execute these five immediate containment checks:
- Revoke Compromised Account Credentials Immediately:
- Immediately revoke the personal access tokens (PATs), OAuth grants, and SSH keys of any GitHub account that opened a
wfx-docsprobe PR or committed malicious workflow changes.
- Immediately revoke the personal access tokens (PATs), OAuth grants, and SSH keys of any GitHub account that opened a
- Hunt for GitHub Indicators of Compromise (IOCs):
- Search across all organization repositories for open or closed PRs titled
docs: dependency audit notesor branches namedwfx-docs,chore/[a-z0-9]{8}, orci-check-[0-9]{4}. - Audit commit logs for messages matching
ci: refresh build cache,ci: restore,chore: add ci helper,ci: cleanup, orchore: add dependency audit workflow. - Check GitHub Actions workflow histories for missing sequential run numbers starting November 10, 2026.
- Search across all organization repositories for open or closed PRs titled
- Block Network & Package IOCs:
- On self-hosted runners or with an egress-control tool, block outbound traffic from build runners and corporate networks to the known exfiltration addresses as of Nov 12 (194.150.166[.]244, 18.143.168[.]96, and 153.75.82[.]159), the October GhostAction C2 (193.32.204[.]199), and unauthorized Telegram API traffic (api.telegram.org) from CI runners.
- Pin away from or remove Packagist
digitalelvis/neuronai-studio3.14.3 and Go modules github.com/onuragtas/docker-env (v1.0.278, v1.0.280, and v1.0.282, also served as github.com/onuragtas/redock) and github.com/vkuznet/gocurl (v0.0.0-20261111095532-8d5aad0cb0da).
- Assume Full Secret Compromise on Hit Repos:
- If a malicious workflow was executed, rotate every GitHub secret, cloud access key, AI API key (OpenAI, Anthropic, Gemini, Hugging Face), and deployment token accessible to that repository.
- Audit Cloud Persistence Paths (Critical):
- AWS: Audit CloudTrail for
CreateAccessKeyandUpdateAssumeRolePolicyevents introducing “Principal”: {“AWS”: “*”}, as well asAssumeRoleWithWebIdentitysessions namedwfx-*. - GCP: Audit Cloud Audit Logs for
GenerateAccessTokenandSignJwtcalls originating from CI service accounts. - Azure / Entra ID: Check App Registrations for newly added federated identity credentials prefixed with
wfx-ci-.
- AWS: Audit CloudTrail for
Architectural Remediation: Moving to Secretless CI/CD with Akeyless
Rotating your leaked secrets puts out today’s fire, but leaves the underlying fuel intact and ready to ignite again. Because the wfx payload itself mints OIDC tokens and attempts to escalate privileges within the same run, OIDC alone is not a silver bullet. It must be paired with strict claim binding, branch protections, and least-privilege ephemeral credentials. Akeyless Identity Security Platform is built to enforce these controls across your pipelines.

1. Authenticate via GitHub OIDC, Bound to a Protected Branch or Environment
Because attackers can mint OIDC tokens from unverified branches or inject into existing workflows, OIDC authentication must be tightly coupled with branch protection, required pull request reviews, and strict claim validation.
- How it works: Configure the Akeyless GitHub Actions Plugin (
akeyless-authentication-and-fetching-secrets) using GitHub OIDC (OAuth 2.0 / JWT Authentication) (https://token.actions.githubusercontent.com/.well-known/jwks), and enforce sub-claim constraints (--sub-claimsfor repository, ref, workflow_ref, and environment) on your Akeyless role association (note that the environment claim is present only when the workflow job declares an environment). - Block Side Branches & Unauthorized Merges: Restricting Akeyless role association strictly to protected refs (such as
ref: refs/heads/main) and protected GitHub Environments ensures that rogue workflows on side branches (chore/xxxxxxxx) get zero access. Pairing that binding with GitHub branch protection rules and required pull request reviews prevents a single compromised token from pushing workflow injections directly into main (whereas status checks alone can be forged by the payload).
2. Replace Static Secrets with Least-Privilege Dynamic Secrets
Because wfx abuses credentials inside the same workflow run, creating IAM keys and rewriting trust policies before the job ends, short-lived TTLs alone shrink the exposure window rather than stopping in-run abuse.
- Least-Privilege Ephemeral Access: Akeyless Dynamic Secrets generate just-in-time, tightly scoped credentials for AWS, Azure, GCP, Kubernetes, and databases on the fly, which automatically expire at the end of a short TTL. Enforcing strict least-privilege permissions on those dynamic roles (for example, denying
iam:CreateAccessKeyandiam:UpdateAssumeRolePolicyon CI roles) ensures that even if an attacker compromises a runner mid-job, they cannot use those credentials to create access keys or rewrite trust policies.
3. Reduce Local Credential Exposure with Secretless Developer Access
The campaign relied on 4,837 compromised developer GitHub accounts, many reused from prior credential-harvesting waves. Reducing what a developer-machine stealer can find on an endpoint limits the blast radius of workstation compromises.
- With Akeyless Modern PAM & Secretless Developer Access, engineers do not store long-lived SSH keys, cloud CLI profiles (
~/.aws/credentials), or static infrastructure tokens on local laptops. Instead, access to cloud consoles, servers, and databases is brokered just-in-time via identity federation with short session TTLs, shrinking the inventory of static credentials exposed on developer machines.
4. Rapidly Rotate & Govern Residual GitHub Secrets via Universal Secrets Connector (USC)
Where legacy workflows still require a native GitHub secret, use the Akeyless GitHub Universal Secrets Connector (USC).
- Instead of manually hunting through hundreds of repositories during an incident, Akeyless USC gives security teams a single control plane to audit, rotate, update, or purge secrets across repository, organization, and repository-environment scopes via CLI or API (
akeyless usc update/akeyless usc delete).
Next Steps
This latest campaign once again proves that attackers have industrialized CI/CD reconnaissance: probing 61,000+ repositories, dumping runner memory, forging status checks, and erasing run logs typically within a minute. No enterprise can out-rotate an automated 60-second attack loop.
By combining GitHub OIDC bound to protected branches and environments, strict JWT sub-claim policies, and least-privilege Dynamic Secrets with Akeyless, you can strip standing credentials out of your repositories and contain the blast radius of a compromised developer token.
Ready to remove static secrets from your GitHub Actions pipelines? Explore the Akeyless GitHub Actions Integration Docs or start a free trial at akeyless.io.