blockintAI · Research blog

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:

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:

  1. Treat the machine as compromised.
  2. Isolate and rebuild it.
  3. Rotate credentials.
  4. 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) and 0xf56f9b48 (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 (0x6a82595EEea0f4FDFB3aE13A2687BcbBE626e94B and 0xA7B15A2B5F6992DC3BabB946375d6895fF1171BF). 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