MAG212 CYBER ORIGINAL ARTICLE

wfx: Hijacked GitHub Accounts Turn CI Into a Secret Stealer, and bee-js Shows Provenance Is Not Safety

November 14, 2026 · El Mostafa Ouchen

Your CI is the target: Swim-lane diagram of the wfx campaign: a hijacked GitHub account opens a probe pull request, commits and reverts a payload, steals CI secrets to exfiltration servers, and creates AWS keys and Azure federated credentials, with a

From the MAG212 CYBER Weekly, 14 November 2026. Status labels: confirmed by vendor, reported by researchers, or unverified. Every command below is copied from official documentation and was not tested by MAG212; run it only on systems you are authorised to administer.

Status: Reported by researchers (Endor Labs, published 12 Nov, updated 13 Nov) [36]. BleepingComputer’s headline corroborates the scale, but MAG212 did not fetch the article [37]. No official GitHub response was found. bee-js: maintainers’ GitHub advisory GHSA-hh7j-5m3j-23pf exists, and the details come from a secondary aggregation of Socket, OX, SafeDep and StepSecurity research [38].

What happened — wfx

From 10 November, attackers used hijacked GitHub accounts to slip a short-lived secret stealer into repositories. Endor Labs counts [36]:

Why this matters

wfx did not need a new vulnerability. It used real maintainers' accounts and normal pull-request workflows to reach CI secrets, then created cloud keys for persistence. bee-js showed that a package with genuine provenance can still ship malware. Pipeline permissions and secret scope are now front-line controls. (MAG212 analysis)

Swim-lane diagram of the wfx campaign: a hijacked GitHub account opens a probe pull request, commits and reverts a payload, steals CI secrets to exfiltration servers, and creates AWS keys and Azure federated credentials, with an inset on the bee-js npm compromise.
Diagram: MAG212 CYBER. Solid red = observed or vendor-confirmed; dashed amber = reported or unverified; green = recommended control.
  • about 61,764 repos probed
  • at least 1,806 with credentials exfiltrated
  • 8,926 with payload evidence
  • more than 829 with npm postinstall hooks
  • at least 50 hijacked accounts that publish to npm, Go, Packagist, PyPI or crates

Victims include nextcloud/docker and 20 webdriverio repositories. Malicious package versions were published to Packagist (digitalelvis/neuronai-studio 3.14.3) and Go (github.com/onuragtas/docker-env v1.0.278 and v1.0.280) [36]. BleepingComputer’s headline puts it at 4,800 GitHub accounts, matching Endor’s count of about 4,837 developers’ credentials stolen [36][37].

What happened — bee-js

On 9 November, @ethersphere/bee-js 13.3.0 sat on npm latest for about 75 minutes (00:09:50–01:24:58 UTC). The Swarm Desktop 0.56.2 auto-update was offered for about 8.5 hours, with the fix in 0.56.3 at 12:52 UTC [38]. The package carried valid npm provenance, because the attacker controlled the real release path: an unsigned commit by bee-worker, then a GitHub release that triggered publishing, through a workflow with no protected-branch check or required reviewers [38]. Reach was about 10 bee-js downloads and 65 Desktop downloads by 08:27 UTC, out of roughly 16,800 monthly downloads across the package family (a reach figure, not infections). Socket’s attribution to SleepyDuck/EtherDuck is unconfirmed [38].

Who is affected

  • wfx: any organisation whose maintainers’ GitHub accounts or tokens were hijacked, plus consumers of the affected Packagist and Go versions [36].
  • bee-js: anyone who installed @ethersphere/bee-js 13.3.0 or Swarm Desktop 0.56.2. Pin bee-js 13.2.0 or move to Desktop 0.56.3 [38].

How wfx works

Per endor labs [36]

  1. Account takeover. One victim’s leaked GitHub CLI OAuth token was found, but the overall initial-access source is unconfirmed.
  2. Probe PR. A pull request titled “docs: dependency audit notes” is opened from branch wfx-docs.
  3. Payload commit. The payload is committed to the default branch between benign commits, then reverted within seconds. It comes in three variants:
    • workflow injection: commit “ci: refresh build cache”, step “Restore cache j632”
    • install hook: commit “chore: add ci helper”, script .ci-xxxxxx.sh
    • throwaway workflow: .github/workflows/build-audit-xxxxxx.yml
  4. Theft. The stealer takes CI secrets, cloud and registry credentials, and AI-provider keys.
  5. Persistence:
    • new IAM access keys
    • trust policies set to "AWS": "*"
    • Azure federated credentials named wfx-ci-*
  6. Cleanup. Workflow runs and logs are deleted.

How bee-js works [38]:

  1. The prepare script is renamed to postinstall.
  2. scripts/generate-version.cjs becomes a loader that fetches JavaScript from frameworkmetrics[.]xyz, including the paths /v1/analytics, /v1/electron, /v1/assets/ and /v1/swarm/report, with the header X-Ethersphere: 1.
  3. It steals the node’s config.yaml and swarm.key.
  4. It installs a backdoor:
    • Linux/macOS: a Go backdoor under ~/.solidity/ (systemd user unit solidity-langsupport-runner.service, LaunchAgent com.solidity.langsupport.runner) talking to a hub at 193[.]57[.]9[.]155:4912, with the hub address resolved from an Ethereum contract.
    • Windows: a Defender exclusion plus a ScreenConnect client (ScreenConnect Client (5cbe79c9c25524df)) connecting to 185[.]232[.]84[.]169:8041.

What defenders should look for

  • wfx IOCs [36]:
    • exfiltration endpoints: 194.150.166[.]244:8080/exfil, 18.143.168[.]96:8080/exfil, 153[.]75[.]82[.]159:8080, with a Telegram bot as fallback
    • stealer SHA-256: 89695abb392d5cfd4972672961e644bb46baa98aa1ed7173c2a2816c762735cf
    • branches: wfx-docs, chore/[a-z0-9]{8}, ci-check-[0-9]{4}
    • gaps in workflow run numbers, which show deleted runs
  • wfx cloud events:
    • AWS CloudTrail: CreateAccessKey, UpdateAssumeRolePolicy, AssumeRoleWithWebIdentity
    • Entra ID: federated-credential additions named wfx-ci-*
  • bee-js:
    • loader SHA-256 ee2f762e9ed3fd89f106e0b80c2e923bf1aed9a280277d3d998a5902c4d9ec0f
    • DNS lookups for frameworkmetrics[.]xyz
    • the systemd/LaunchAgent names above

False positives: legitimate key rotation also produces CreateAccessKey, so check the identity making the call and when it happened.

How to investigate

  • Pull requests: search your organisation’s PRs for the title “docs: dependency audit notes” from 10 November onward. Endor supplies a gh search prs query, but its syntax was not verified against GitHub CLI docs.
  • Branches: check repos for attacker branches.
  • Commit history: review default-branch history for add-then-revert pairs.
  • Cloud logs: pull the CloudTrail and Entra events listed above for the same period.
  • bee-js: use NPM-1 to find the package anywhere in your dependency tree.

Contain and remediate (in order) [36][38]:

  1. Revoke GitHub sessions, tokens, SSH keys and OAuth grants for affected accounts.
  2. Require phishing-resistant MFA.
  3. Rotate every secret reachable from workflows.
  4. Delete attacker IAM keys and "AWS": "" trust policies, and remove wfx-ci- federated credentials.
  5. For bee-js:
    • pin 13.2.0 or Desktop 0.56.3
    • rotate secrets from a clean machine
    • move node funds to a new key
    • rebuild affected hosts

Verify recovery

Rerun the PR and branch searches and get no hits. Confirm IAM and Entra show no unknown keys or federated credentials. Confirm npm ls shows no 13.3.0.

Prevention and awareness [36]:

  • Put workflow files under CODEOWNERS.
  • Make GITHUB_TOKEN read-only by default.
  • Use npm ci --ignore-scripts in CI where possible.
  • Protect release branches and require reviewers on release workflows.

Lesson: provenance attestations prove where a package was built, not that the builder was honest [38].

Command panel – npm (source: npm docs, npm-ls [39])

NPM-1 — Find bee-js anywhere in the dependency tree

  • OS / shell / tool: any OS; the project directory; npm CLI
  • Privileges: a normal user with read access to the project
  • Purpose: show every installed copy of @ethersphere/bee-js, including transitive ones. The docs confirm npm ls <package-spec>, and that --all shows the full tree rather than direct dependencies only [39].
  • Read-only or change: Read-only
  • Command:
  • How to interpret: any 13.3.0 in the output means the malicious version was installed. Treat that machine as compromised and follow the remediation steps. Run the command in each project and each CI cache.
  • Not tested by MAG212.
npm ls @ethersphere/bee-js --all

Related on MAG212 CYBER

Sources

  1. [36] Endor Labs, “GitHub Actions supply chain attack targeting CI secrets at scale”, 12 Nov, updated 13 Nov 2026. link
  2. [37] BleepingComputer, “Hackers compromise 4,800 GitHub accounts to steal cloud credentials (not fetched)”, Nov 2026. link
  3. [38] Safeguard.sh, “@ethersphere/bee-js and Swarm Desktop compromise: npm loaders without install scripts”, 13 Nov 2026. link
  4. [39] npm Docs, “npm-ls (CLI v12)”, Current. link

Written and edited by El Mostafa Ouchen for MAG212 CYBER. Facts checked against the sources above as of 14 November 2026, 10:05 AM PST. Diagrams are original MAG212 CYBER illustrations.