What started as one ClickFix alert in our MXDR service turned into a map of nine attacker groups hiding their command and control inside public smart contracts. Here’s what we found, and what it took an AI agent to find it.

The Alert
We’d been reading a string of posts about EtherHiding lately, and it left us curious to dig into the technique ourselves, especially the blockchain side of it. Recently, baseVISION CSIRT was reviewing a ClickFix case that had come through our MXDR service. Nothing about the surface of it was unusual. A legitimate, previously clean website was prompting the visitor to copy a command into the Windows Run dialog to fix a supposed problem with their browser. We see plenty of these. One outgoing connection in the traffic stood out. The compromised page was calling out to a blockchain API endpoint.
Working through the injected script turned up something specific. An eth_call, the kind of call you use to read a public blockchain without writing to it, aimed at a Polygon RPC endpoint and a particular contract address. That’s the C2 lookup. Because smart contracts on a public chain are exactly that, public, we pointed an AI reversing agent at it to analyze the contract, and it handed back the domain the campaign was using for its second stage at that moment.
That alone would have been a good afternoon’s work. But it’s a blockchain, so everything in it is public and permanent. Could we retrieve more of it? The current value sitting there for anyone to read means every value ever written to that slot is too, going back to the day the contract was deployed. So is the wallet that deployed it. We weren’t looking at a single indicator anymore. We were looking at a case file the attacker had been keeping for us, one they couldn’t delete.
What EtherHiding Actually Is
The technique is called EtherHiding. Instead of hardcoding the domain for its next stage into a malicious script, the operator writes it into a small smart contract on a public blockchain. The victim’s browser makes a read call to a public RPC endpoint, the same kind of endpoint any wallet or app uses to talk to the chain, and gets the current address back out of contract storage.

For the people running these campaigns, that has a specific set of advantages. A smart contract has no registrar, no hosting account, and no company that can be served a takedown notice. Updating it costs one transaction, and that transaction is cheap enough that Google’s Threat Intelligence Group has pointed to the low cost per update as part of what makes the technique attractive to attackers in the first place. One transaction updates the pointer for every compromised site relying on that contract at once, and the browser is only ever talking to public infrastructure like polygon-rpc.com or a Base node, so there’s no malicious domain anywhere in the flow until the pointer resolves.
Guardio Labs coined the name in 2023, off a ClearFake campaign that had moved onto the blockchain after its previous hosting, Cloudflare Workers, kept getting taken down.1 It stayed obscure until Google’s GTIG reported an actor linked to North Korea, tracked as UNC5342, using it in social engineering campaigns since February 2025.2 The wider security industry started paying attention to it as a category around then, rather than as one campaign’s quirk. The shift has kept accelerating since. In our own MXDR casework, a significant share of the ClickFix cases we’ve handled since this summer have pivoted to exactly this technique.
This year, though, some attackers have started treating the technique as outdated. In August 2026, researchers at OpenSourceMalware described a technique called NullReceiver, found in a set of trojanized npm packages, that encodes a C2 address into the destination of a transaction that moves no value, sent to a wallet that has never existed, rather than into a contract’s storage.3 Their stated reasoning was that a contract is a fixed, identifiable thing defenders can watch and that visibly emits an event on every update, while a transaction to a wallet with no contract behind it leaves nothing to fingerprint. It’s a fair critique, and it may be where this goes next. For now, though, most of what we’re seeing still runs the older way, and our best guess is that’s because operators are running commodity kits rather than writing their own tooling.
Turning the Ledger Against Its Owner
Once we had the first contract, the question was how far the same idea would take us. Here’s what that looks like, stripped down to one concrete example, from a cluster whose campaign has since gone quiet. On Polygon, the contract exposes a function called getData(). Anyone can call it. No account, no API key, and no block explorer required.

One call gets you the current pointer. The same call, replayed against old blocks instead of the latest one, gets you history. We had an AI agent repeatedly split the chain’s block range in half to find the exact points where the stored value changed, which reconstructs an operator’s entire rotation history, with timestamps attached, from a contract that has never been verified on any block explorer and was never meant to be read by anyone but the malware itself.
From there the investigation became a pivoting exercise, and it’s the kind AI turns out to be unreasonably good at.
- Profiling contracts by bytecode, not by trusting the loader. A scan for
PUSH4instructions across the live bytecode recovers every function selector a contract actually exposes, whether or not the loader script ever called it. - Walking from contract to deployer to every other contract that deployer ever made. A contract’s address is computed from its deployer’s address and a simple counter, so once you have the deployer you can compute every address it has ever deployed to and check which ones hold code. One sweep across 441 samples this way landed on the same three Polygon contracts, from three independent angles.
- Searching for the technique, not the indicator. Using a threat intelligence platform’s search across whole file contents rather than a hash lookup, and searching all three possible byte alignments a function selector can land on once it’s encoded in base64, not just the plaintext string. That single search surfaced an entire cluster we had no prior indicator for, plus a sample set of 443 files on Polygon that most of the rest of this work was built from.
- Watching the live ones properly. Baseline the whole contract set, then reread it a day later against an archive endpoint, checking a case we already knew had changed first, so a nil result actually means something. We caught one operator change its published address twice inside a single day this way, the second time within about an hour of it happening, each change traceable to an exact block.
Blockchain analysis wasn’t our team’s core specialty going into this. We just pointed an agent at a contract and told it to keep going, and it turned out that “keep going” scales in a way manual analysis doesn’t.
What We Found
Put together, the work surfaced nine distinct clusters, thirty malicious contracts, and thirteen deployer wallets, spread across Polygon, Base and BSC, plus two test networks including one on Ethereum’s Sepolia.

Alongside the contracts, we recovered 71 hostnames, 27 IP addresses, 46 URLs, and 55 file hashes tied to these campaigns, of which 18 are second stage droppers or executables rather than loaders and landing pages. We also pulled another three dozen historical C2 domains straight out of contract storage history, ones we’re still working through and haven’t finished attributing.
Eighteen of the compromised sites we found this way were third party websites with no relationship to us or our customers. Several were still compromised when we checked. We notified every one we were able to reach.
The Same Idea, Three Ways
The operators surprised us with how differently they had implemented the same basic idea.

On Polygon, the pattern is about as simple as it gets. A contract holds one string, the current C2 hostname, rewritten whenever the operator wants to move. One cluster rotated its address twelve times in four days.
On Base, one operator built something closer to a content management system than a pointer. The contract has functions to add a script, update it, activate it, and deactivate it, and the operator versions their injected JavaScript directly in contract storage.
On BSC, one contract holds the payload itself, compressed with gzip, not just an address. The victim’s browser pulls it straight out of storage, inflates it right there, and runs it. There is no download step anywhere in that chain, nothing for a network defender to catch in transit.
The page carrying all three tiers had its oldest tier already dormant by the time we found it, so our best read is successive reinfection. Three injections stacked up over time because nobody ever finished cleaning the site, rather than one operator running three techniques in parallel. Two of the three wallets behind it are confirmed as the controlling account rather than just the deployer. Whether that adds up to one actor or three, we didn’t establish, and we’d rather leave that open than guess.
Downstream, we traced these chains to a NetSupport RAT deployment, a LuaJIT loader talking to a raw IP address with no domain in front of it at all, and one operator running a fleet of 155 separate contracts on a single test network.
The Compromised Sites
Almost every compromised site in this set runs WordPress, and the injector itself is worth a moment, because it explains why these infections last as long as they do.
It installs as a plugin with a name that sounds plausible, a common word followed by a string of random hex characters that changes with every install. It only loads its payload for ordinary visitors, gated behind a single if (is_admin()) check, so a site owner logging into wp-admin to check on things never sees it and never triggers it. On activation, it copies a ZIP archive of itself into the uploads folder disguised as an image file, and if anything about the plugin changes or gets deleted, it restores itself from that hidden copy on the next page load.
Deleting the plugin does not clean the site, because the plugin is the payload, not the entry point.
A proper cleanup has to find and remove the plugin directory, the disguised ZIP, and its state file, and then, separately, find whatever let the attacker in in the first place.
What AI Actually Did Here
We read a lot about how AI can support adversaries. Better phishing, faster exploit development, faster vulnerability finding. This is what it looks like used the other way, and it doesn’t get talked about nearly as much.
We couldn’t have done this by hand. Not at this volume, not in this time. The corpus for one chain alone ended up at 443 files, spread across four distinct obfuscation styles, each needing its own unpacking approach. None of the contract interfaces were verified on any explorer, so there was no source code to read. Every one of them had its function names recovered indirectly instead. Scrape the raw selectors straight out of the bytecode, then match each one against known signature databases, or try every plausible function name, hashing each one, until it matches. Reconstructing rotation history means hundreds of RPC reads per contract, across five different networks. Any one of those is a day of work on its own.
The reversing knowledge is the same either way. The agents just applied it in parallel and didn’t get bored on read 400. Our job shifted to laying out the plan, structuring the research, guiding the agents, and reviewing what they brought back, sometimes using another agent just to gather more data. The reviewing mattered. A couple of things came back confidently wrong, stated with the same tone of certainty as everything it got right. This was a first attempt, and there’s a lot we’d do differently. We didn’t expect this volume of findings, and validating what came back got harder faster than we expected.
Here’s the simple version. When we use agents in a structured way, research that would normally take weeks gets done in three days. The limiting factor was never the analysis. It was how fast we could check what came back.
Attackers who automate their own operations move at machine speed. They rotate infrastructure in hours and reuse the same kit across a dozen campaigns without much extra effort. Defenders have been stuck working at human speed against that. One ticket, one analyst, hours spent deobfuscating a script by hand or getting up to speed on an unfamiliar language like smart contract bytecode before the actual analysis can even begin. Pointing an agent at a contract and getting a checkable answer back in minutes instead of days is what actually lets us catch up to the adversary and match their pace instead of just keeping up.
Where This Goes
A few of these findings have already turned into hunts inside customer environments, looking for past visits to the compromised sites we found. We’ve handed the parts relevant to detection to the team building signatures for this pattern more broadly. Separately, we’ve started thinking about whether parts of this can run continuously rather than only once. Watching specific contracts for new activity, rather than reconstructing history after the fact once we happen to notice a case.
The same permanence that makes attackers hard to take down means their history is sitting there to read too.
Further reading
- Guardio Labs, “EtherHiding: Hiding Web2 Malicious Code in Web3 Smart Contracts”, October 2023. The original naming of the technique, off a ClearFake campaign.
- Google Cloud / Google Threat Intelligence Group, “DPRK adopts EtherHiding”, October 16, 2025. Reports UNC5342 using the technique since February 2025, and the low cost per update that makes it practical to run.
- The Hacker News, coverage of NullReceiver in trojanized npm packages, August 2026, drawing on research from OpenSourceMalware.
TLP:CLEAR. All contract addresses and technical detail in this post concern dormant infrastructure or the attackers’ own contracts on the chain, not the compromised third party sites we identified, which have been notified where we could reach them and are not named here.