The ClickFix Attack We Could Have Stopped

A customer forwarded us something last week that has stuck with me. It was a live attack campaign — a good one, in the professional-admiration sense — along with a detailed public write-up dissecting exactly how it worked. The analysis was genuinely impressive. It traced the whole thing from the moment a victim landed on a compromised website to the moment their machine was compromised, and it closed with a section on how to defend against it.

And that closing section is what stuck with me, because it never mentioned us. Not Report URI specifically — I mean it never mentioned the entire layer of the web where this attack begins and where, quietly, it could have been stopped before it ever got going.

This is a story about that gap.

What the customer sent us

The attack is a variant of something called ClickFix, and it works by turning the victim into the delivery mechanism.

You visit a website. The site has been compromised — in one version through a hacked WordPress install, in the version our reader flagged it was through a tampered-with tag manager — and without anyone noticing, it has started running a small piece of JavaScript it was never meant to run. That script doesn't do anything dramatic on its own. It quietly fetches its real instructions from somewhere else, then puts a convincing dialog box up on the screen: a fake "verify you're human" prompt, the kind everyone has been trained to click through without thinking.

The prompt asks you to do three small things — a keyboard shortcut, paste, enter. What it doesn't tell you is that the page has already slipped a command onto your clipboard. Follow the friendly instructions and you run that command yourself, by hand. From there a chain of built-in Windows tools pulls down the actual malware and runs it, with nothing ever downloading the way your browser would normally warn you about. Your machine is now compromised, and you did the compromising!

It's a nasty piece of design, because it sidesteps almost everything. There's no dodgy download to block, no macro to disable, no exploit to patch. The user is walked right through the middle of every safeguard.

The part that makes it hard to kill

Here's the detail that elevates this campaign from clever to genuinely difficult: the malicious payload isn't hosted anywhere you can take it down.

Instead of sitting on a server or a domain, it's stored on a public blockchain. The injected script simply reads it back out when it needs it. There's no host to have suspended, no registrar to complain to, no server to seize. When the attacker wants to change the payload, they update it with a single transaction from a wallet only they control, and every compromised site picks up the new version automatically.

This technique — hiding the payload in plain sight on a blockchain — is specifically designed to defeat the standard response. Normally you fight this kind of thing by finding the bad infrastructure and getting it taken down. Here, you can watch the infrastructure all day and never lay a finger on it. Chasing it is a game you're going to lose.

So if you can't take the payload down, the obvious question is the more interesting one anyway: how do you stop it from ever running?

The one thing the whole attack depends on

Strip away the blockchain, the fake dialog, the clipboard trick, and the living-off-the-land command chain, and every version of this attack rests on a single fact:

A website ran a script it was never supposed to run.

That's it. That's the crack the whole thing grows out of. And unlike the blockchain payload, that first step isn't happening on the attacker's turf — it's happening on the website's own pages, in territory the site owner completely controls.

This is the layer the write-up skipped. It's the layer we've spent years building tools for. And it's the layer where this attack quietly falls apart.

A site can tell the browser, in advance, which scripts it vouches for — and instruct it to refuse to run anything else. The injected script doesn't carry that vouch, because the attacker was never in a position to add it. So the browser simply doesn't run it. No script, no fetch from the blockchain, no fake dialog, no clipboard command, no compromise. The attack dies at the first step, and everything downstream — all the cleverness the write-up admired — never gets the chance to happen.

Even a site not ready to slam that door shut can leave it ajar and watch. Point a site at Report URI and run in a report-only mode, and the very first time an unauthorised script tries to run or phone home, you get told. Think about what that's worth here: this is an attack that is otherwise completely invisible to the site owner. Nothing breaks, nothing errors, nothing shows up in a log — the whole thing plays out inside visitors' browsers, and the first sign of trouble is someone's computer getting owned. A report-only setup turns that silence into an early warning, at the exact moment the tampering starts.

And if you simply want to know the instant your site starts serving a script it wasn't serving yesterday, that's what Script Watch is for. A rogue addition to a tag manager, a new script on a page that had none — that's precisely the change it exists to catch.

Backstop versus first line

None of this is an argument against defending the endpoint. If a user does end up pasting that command, you absolutely want protections on the machine to catch the behaviour. Those defences are real and they matter.

But they're a backstop — the last line, the thing that saves you when everything earlier has already failed. The write-up treated the last line as the only line. And the first line, the one that stops a visitor from ever seeing the fake dialog in the first place, was sitting right there on the web page the whole time, unmentioned.

That's the uncomfortable bit. This wasn't a novel, unstoppable attack that demanded some new defence nobody had invented yet. It was an attack that a control we already have, already talk about, and already build tooling for would have ended before it started — if only the site had been running it.

We could have stopped this one. The reason to care about the web layer isn't this campaign; it's the next one, which will use a different payload and a different hiding place and the exact same first step. Close that first step, and the cleverness of everything after it stops mattering.

If your defences against this class of attack currently stop at the endpoint, that's the gap worth closing — and it's a lot closer to hand than a blockchain takedown.

Note: The behaviour we observed was consistent with UNC5142 / CLEARSHORT activity previously documented by Google's Threat Intelligence Group and Mandiant.