The bee-js and Swarm Desktop Backdoor Reads Its Orders From Ethereum. On Nov 10 the Operator Pointed It at a New Server
BlockintAI · 2026-11-12T15:00:00.000Z
On November 9, someone with access to Ethersphere's GitHub accounts published two poisoned releases. One was @ethersphere/bee-js 13.3.0 on npm. The other was a trojanized Swarm Desktop 0.56.2, delivered through the desktop app's auto-updater. Socket, StepSecurity, Corgea and The Hacker News have all written it up. GitHub issued advisory GHSA-hh7j-5m3j-23pf.
Both releases carried a small loader. It pulls JavaScript from api[.]frameworkmetrics[.]xyz and runs it with full Node.js privileges. On machines running a Swarm node, the payload posts two files to the attacker: the node's config.yaml and its swarm.key. The key file is an encrypted Ethereum keystore, but config.yaml stores its password in plain text. Together they give the attacker the node's wallet.
The later stages are where crypto users should pay attention. They don't hard-code a command server. They ask an Ethereum smart contract where to connect. That means the operator can move the whole implant fleet to a new server with one transaction, and anyone can watch them do it. That happened again on November 10.
How fast it moved
| Time | Event |
|---|---|
| Nov 6, 9:08 AM CST | frameworkmetrics[.]xyz registered (per Socket) |
| Nov 8, 5:30:47 PM CST | Contract hub changed to 193[.]57[.]9[.]155:4912, 29 minutes before the malicious bee-js commit (per Socket) |
| Nov 8, 6:09:50 PM CST | bee-js 13.3.0 published to npm (00:09:50 UTC Nov 9) |
| Nov 8, 7:24:58 PM CST | npm removes 13.3.0, about 75 minutes later |
| Nov 8, 7:25:27 PM CST | GHSA-hh7j-5m3j-23pf published |
| Nov 9, 6:52:07 AM CST | Swarm Desktop v0.56.3 released. Socket says the trojanized 0.56.2 stayed up about eight and a half hours. |
| Nov 10, 3:21:59 PM CST | Hub changed again, to 216[.]126[.]231[.]122:4912 (BlockintAI research) |
npm's latest tag for bee-js now points back to 13.2.0. The clean versions are bee-js 13.2.0 and Swarm Desktop 0.56.3.
The dead drop
The contract is 0xf8a900Db50b3331be6B768bA460BB59f3E40C344 on Ethereum. Socket describes it as a minimal configuration store:
param1()returns the hub address.param2()returns a payload URL.- Only the owner wallet, 0xFd3fc58bcbd8ccc77b6000201438eDfc636E7cA7, can change them.
Socket and Pluto Security tie the contract to the operator behind the SleepyDuck/EtherDuck Open VSX extension campaigns. Socket reports that six of the seven payload hashes Pluto published match files pulled from the bee-js C2 on November 9. The full addresses and hashes are in Socket's IOC list, linked below.
The values are plain strings in the setter transactions' calldata, so the whole history decodes from Etherscan. Here is every write, in BlockintAI research's decoding:
| Time | Value written | Tx |
|---|---|---|
| Mar 14, 8:03:47 AM CDT | Contract deployed | 0x2ee8cd7492d8771630a3022401a08501e8ee40b7d64c16d569ec9e398cd46120 |
| Mar 14, 8:24:23 AM CDT | hub hxxp://localhost:4912 | 0x972f6e632284b36bde259c8a8f3fec3095655512b5b2f2890da7b00006d3248a |
| Mar 14, 9:04:23 AM CDT | hub 107[.]189[.]16[.]215:4912 | 0xe324855aaaeeed519b0b3afb1fa39e71a7fd03111da198cb274e0a77377fc4e2 |
| Mar 28, 4:55:11 PM CDT | hub 91[.]108[.]240[.]156:4912 | 0xf0ea0a5be8ca520d0696de6b75f81c6d751e46de53745a16f347a96bd51b8d85 |
| Mar 29, 2:07:47 PM CDT | payload hxxp://91[.]108[.]240[.]156:3000/app | 0x50ca51ccfb662de6c66249950f3296fdee70770fc451042066c7b93a6a00c625 |
| Mar 29, 2:16:59 PM CDT | payload hxxp://91[.]108[.]240[.]156:3000/app[.]js | 0x153d51a7dc03131e80ba2dcd191eb0e1bf42f7af048ade5d980b9fe91309d2d0 |
| May 3, 11:55:11 AM CDT | hub 107[.]189[.]27[.]46:4912 | 0xadea1dfe0b2aa2417510a803601ce7b036dcd673111ba53aa6be0ff366e6f679 |
| May 16, 7:08:23 AM CDT | payload hxxp://107[.]189[.]27[.]46:3000/app[.]js | 0x8e5dec4488f71fef76481c502db745d5deeb98088d6f45e6f8fd3993214cd32d |
| Jul 31, 5:10:11 PM CDT | hub 107[.]189[.]27[.]223:4912 | 0xdb69bed02b097961f04d43e4a7a616067a685088055bad73020d3697b4301738 |
| Sep 23, 5:47:35 PM CDT | hub 178[.]16[.]54[.]222:4912 | 0x27ad3ea4f3203dedf9262200df8b1a08d6eb3a5ce54cfff779682f9211cef7d2 |
| Sep 24, 12:30:23 PM CDT | hub 193[.]57[.]9[.]17:4912 | 0x58682d6af76c0b305a3b4c374094016976263eed4626d897aeb3cb64c770c35d |
| Nov 8, 5:30:47 PM CST | hub 193[.]57[.]9[.]155:4912 | 0x53131590acd75a9d2bd2ceb9d55d94e45174945c966fb76bf3a6e8439946ea15 |
| Nov 10, 3:21:59 PM CST | hub 216[.]126[.]231[.]122:4912 | 0x3d62efab689251a4f06283edd6293bbdb64fb196487698ab4219e9e48b73f530 |
Our decoding matches Socket's history up to November 8. One note: we count a payload-URL write at 2:07:47 PM CDT on March 29 (…:3000/app, no extension), corrected nine minutes later. Socket groups those as a single "March 28 to 29" change.
The November 10 write
The last write in that table postdates the public reports. At 3:21:59 PM CST on November 10, the owner wallet called the hub setter with 216[.]126[.]231[.]122:4912. Tx: 0x3d62efab689251a4f06283edd6293bbdb64fb196487698ab4219e9e48b73f530
A live param1() call on the contract now returns that value. param2() still returns hxxp://107[.]189[.]27[.]46:3000/app[.]js, the payload URL set on May 16.
The new hub sits in 216[.]126[.]231[.]0/24. RIPEstat shows that prefix announced by AS14956, RouterHosting LLC. The November 8 hub, 193[.]57[.]9[.]155, sits in a prefix announced by AS152900, Onidel Pty Ltd.
This matters because the implants look up the hub at runtime. Socket notes that the Go backdoor builds were reused unchanged from wave to wave, because the hub comes from the contract. A single contract write is enough to move every running implant. The November 9 reports name 193[.]57[.]9[.]155:4912, the November 8 hub. Anyone who blocked only that address is now blocking a server the implants have been told to leave. Defenders should add 216[.]126[.]231[.]122:4912 and keep watching the contract instead of any single IP.
The owner wallet held 0.012987 ETH when we checked. That's enough for many more setter calls.
If you ran either release
The reports agree on what to do:
- Treat the machine as compromised.
- Isolate and rebuild it.
- Rotate credentials.
- Move any Swarm node funds and stake to a newly generated wallet. The old keystore and its password were sent to the attacker together.
GitHub's advisory says secrets on an affected machine should be rotated from a different computer. Socket lists the persistence to look for: a ~/.solidity/ folder, the systemd user unit solidity-langsupport-runner.service or the LaunchAgent com.solidity.langsupport.runner on Linux and macOS, and a ScreenConnect install on Windows.
What to watch
- New setter calls from 0xFd3fc58bcbd8ccc77b6000201438eDfc636E7cA7 to 0xf8a900Db50b3331be6B768bA460BB59f3E40C344. The setter selectors are
0xc832508f(hub) and0xf56f9b48(payload URL). - Any outflow from the owner wallet, or new funding into it.
- The second contract and wallet Socket tied to the same operator on July 31 (
0x6a82595EEea0f4FDFB3aE13A2687BcbBE626e94Band0xA7B15A2B5F6992DC3BabB946375d6895fF1171BF). Socket says the two wallets were funded by the same high-volume payout wallet. - Connections to 216[.]126[.]231[.]122:4912 from developer machines.
Sources
- Socket, "Malicious bee-js npm Release and Swarm Desktop Releases Deliver SleepyDuck Ethereum C2 Backdoor" (Nov 9, IOC list): https://socket.dev/blog/bee-js-compromise
- StepSecurity, "@ethersphere/bee-js 13.3.0 Compromised": https://www.stepsecurity.io/blog/ethersphere-bee-js-13-3-0-compromise
- GitHub Advisory GHSA-hh7j-5m3j-23pf: https://github.com/advisories/GHSA-hh7j-5m3j-23pf
- The Hacker News: https://thehackernews.com/2026/11/hackers-compromise-etherspheres-bee-js.html
- Corgea: https://corgea.com/research/ethersphere-bee-js-13-3-0-swarm-desktop-0-56-2-evjawbreaker-ethereum-c2-november-2026
- Etherscan, contract transactions and live
param1()/param2()calls; tx 0x3d62efab689251a4f06283edd6293bbdb64fb196487698ab4219e9e48b73f530 - npm registry, @ethersphere/bee-js; GitHub releases, ethersphere/swarm-desktop
- RIPEstat prefix overview, 216[.]126[.]231[.]0/24 and 193[.]57[.]9[.]0/24