Back

Blog

Engineering

Two Years After the Solana web3.js Backdoor, CrowdStrike Still Doesn't Flag It

Minh Anh Day

On December 3, 2024, someone published two new versions of @solana/web3.js, the JavaScript library that almost every Solana application uses to talk to the blockchain. Versions 1.95.6 and 1.95.7 looked like routine patch releases. They contained one small addition: a function that took any private key the library handled and sent it to sol-rpc.xyz, tucked into request headers dressed up as ordinary CDN traffic. (The domain was picked to look like one of the legitimate Solana RPC endpoints the library talks to all day. Attackers, it turns out, also know how to name things.)

According to the official advisory, the malicious versions were live between 3:20pm and 8:25pm UTC, just over five hours. Anyone who installed them in that window and then ran a bot or backend that handled a private key directly handed that key to the attacker.

That was almost two years ago. Two years is a long time in security. The incident was analyzed, written up, and argued about; sol-rpc.xyz is on every blocklist that matters. So this month, we rebuilt the same attack, pointed it at fresh domains, and ran it on a customer's Mac that was protected by CrowdStrike Falcon.

Falcon didn't raise a single alert.

Why this matters more now than it did in 2024

In 2024, pulling this off took some skill. You had to know which functions in the library touched the private key, write an exfiltration path quiet enough to pass as CDN traffic, and pick a domain that would blend in. None of that is genius-level work, but it filtered out a lot of would-be attackers.

That filter is gone. A good AI model will read a library, find every code path that handles a secret, and write a tidy, well-commented function to send it somewhere, for anyone who asks the right way. Attacks that used to require a sophisticated attacker now only require a sophisticated model, and those are available to everybody. (Including, presumably, the people who phished their way into a maintainer's npm account. Models write very convincing phishing emails, too.)

So the question isn't whether someone will try this again. It's whether anything on your machines will notice when they do.

What makes this attack different

Most of the malware stories you've heard follow a familiar plot. Something gets onto your machine, it runs (a new process, a dropped binary, a sketchy install script), and then it does something bad. Endpoint security tools have spent two decades getting very good at that middle step. A new process appearing out of nowhere is suspicious. A binary whose hash matches known malware is suspicious. A Word document that spawns PowerShell is extremely suspicious.

The web3.js backdoor skipped the middle step entirely. There was no install hook. The malicious code lived inside the library itself, and it ran whenever an application that used the library ran. I'm going to call this class of attack in-process key theft:

Malicious code, running inside a trusted program that's already running, reads a secret that the program legitimately uses, and sends it out over ordinary HTTPS.

Nothing new gets launched. No child process, no new binary, no install script. The code runs inside the exact node process the operator meant to run, a process that is supposed to read that key and supposed to make HTTPS requests all day. From the outside, the theft is a short file read followed by one encrypted request to a host the program hasn't talked to before.

That's the whole attack. It's almost insultingly simple.

Why endpoint tools miss it

Think about what an EDR sensor actually sees during the theft:

  • A signed, well-known node binary, running exactly as it was started.

  • That process reading a file it reads every time it starts.

  • That process making an HTTPS connection on port 443, which it does constantly.

Each of those events is completely normal on its own. The things that would trigger an alert, a known-bad file hash or a known-bad domain, only exist after somebody has found the attack, analyzed it, and published the indicators. That's why two years of hindsight didn't help: Falcon may well know everything about sol-rpc.xyz, but the next attacker won't use sol-rpc.xyz. During the live window, which is the only window that matters, the attacker's domain is just another domain, and the poisoned package is just another version.

This is a structural blind spot, not a missing signature. If your detection is built around new things running and known bad things, an attack that runs nothing new and uses nothing known is invisible by construction.

The payload doesn't help either. The key travels inside TLS, so nothing on the network can read it. And by the time a monitoring tool gets around to asking "what files does this process have open?", the answer is none. The bot opened its key file, read it, and closed it in a few milliseconds.

How we catch it

At EDAMAME, we stopped asking "is this event suspicious?" and started asking "is this combination suspicious?"

Reading a wallet key is normal. A program talking to a new website is normal. But a program that just touched a wallet key and is now, for the first time in its life, talking to a host it has never contacted before? That's the shape of this attack, and it's a shape that legitimate software almost never has. It also doesn't care what the domain is called, which is the whole point.

So the detection has two pieces:

  1. Remember the read. The operating system tells us when a process opens a sensitive credential file (wallet keys, SSH keys, cloud credentials), and we keep that fact around for a while after the file is closed. A key read that lasted three milliseconds is still on record fifteen minutes later.

  2. Know what's normal for each program. We keep track of which destinations each individual program has used. Not each machine, each program: your trading bot and your build script get separate histories. When the bot that has spent the last hour talking to the Solana RPC suddenly talks to someone new, that host is flagged as new for that program.

When those facts line up, EDAMAME raises an alert, and the alert names the new host. This matters more than it sounds. An earlier version of our engine occasionally pinned the finding on the legitimate RPC connection instead, which is roughly the security equivalent of reporting the bank instead of the robber.

The test

We built a safe stand-in for the web3.js attack. It uses fake keys (random bytes, never a real wallet), and the "stolen" data is a fingerprint clearly labeled SIMULATED. It never sends any actual key material, and it refuses to contact the real attacker's domain. It runs two variants of the theft:

  • Read once: the bot reads its key file, closes it, and later sends the key to a new host.

  • CDN-fronted: the key file stays open, and the theft is one request to a host behind a CDN.

To make it realistic, each bot first runs normally for over an hour, talking only to the Solana RPC. Then it restarts with the backdoor. That's the real campaign's shape: a running bot, an npm update, a restart.

The conditions, so they are on the record. macOS, on a customer machine we are not naming, running CrowdStrike Falcon at its latest release as of 1 October 2026. EDAMAME and Falcon were installed side by side on that same host, and both variants ran there.

EDAMAME raised an alert on both variants, and each alert named the exfiltration host. Falcon raised no alert on either of them.

What this means

Falcon is very good at the problem it was built to solve. But that problem was "something bad started running," and the most dangerous supply-chain attacks don't start anything. They arrive as a poisoned dependency inside a program you trust, and a program you trust doing slightly the wrong thing looks, event by event, exactly like that program doing the right thing.

Two years after web3.js, that gap is still open. And now that a good model can write the attack for anyone who asks, the people walking through it won't need to be sophisticated at all.

The fix isn't a bigger blacklist. A blacklist always arrives after the theft. The fix is understanding what each program normally does, so you can see the moment it does something it has never done before, with something it should never share.

Minh Anh Day

Share this post