SecureAEye LAB ONLINE · SIG 657 · DET ~41s

← Blog

We tried to prevent ClickFix in the lab — and measured whether it actually worked

Pattern · 2026-10-28

In Delete the category we argued that the strongest move against a whole class of attacks is to make the mistake impossible rather than catchable. clickfix-fake-captcha is the clean test case for that idea: the lure delivers no file. A fake CAPTCHA or error tells the victim to paste a command and run it themselves — almost always through a script interpreter (powershell, mshta, wscript, cscript). The prevention claim is blunt: deny those interpreters to the standard user and the pasted command has nothing to run in. No judgement call per command, no detection race — the execution path is simply gone.

That was a hypothesis. This is us measuring it.

The A/B

Same victim, same user, same pasted command. The only thing that changes between the two runs is the control:

runcontrolwhat we expected
OFF (baseline)interpreter allowed for the userthe command runs
ONinterpreter denied for the user via AppLockerthe command is blocked, and the denial is logged

The "payload" is benign by construction — it only writes a short, run-scoped marker file, standing in for "the interpreter executed what the lure told the user to paste." Two signals decide the outcome, and both come from the SIEM (Wazuh), not from anyone's say-so:

What actually happened

Reached with the control off, blocked with it on. On the evidence, the pair grades proven: the control deletes the execution path for this attack, measured end to end on an instrumented host rather than asserted from a rule count.

Three things we're saying out loud, because they're the honest part

A prevention claim is only as good as what it refuses to hide. Three caveats shaped this result:

We measured the execution control, not the Win+R toggle. The pattern names two controls — disabling the Run dialog (a UI control) and denying the interpreters (an execution control). Our harness launches the command through VMware Tools, never through the Run dialog, so it can only honestly measure the execution control: interpreter denial via AppLocker. That's the one that neutralises the payload regardless of how it was pasted. The UI toggle is a complementary hardening step, checked by hand, not by this run.

The measured interpreter was cscript, not mshta. mshta's HTML-application engine writes nothing under our non-interactive automation session, so measuring it would have proven nothing either way. We ran the same one-liner through cscript, which executes it headless — and which is denied by the same AppLocker control and caught by the same block rule. mshta stays the canonical lure in the human runbook; cscript is what we could measure faithfully.

The control only bites after a reboot, and our first ON run proved it by failing. AppLocker caches its policy at boot, so an earlier ON attempt (run-2026-10-26-022) — control applied but no reboot — produced neither a marker nor a denial. By our own rule that is unverifiable, not a win: a silent run never counts as a block. We rebooted, re-ran, and only then got the logged denial above. We're keeping the unverifiable run on the record because it's the reason we trust the one that worked.

Why it matters

Most of security spends its effort on detecting the thing after it starts. This is a small, concrete data point for the opposite bet: for an attack whose entire mechanism is "get a human to run an interpreter," taking the interpreter away doesn't need to win a race — there's no race left. One attack, one measured control, one honest caveat list. That's the unit we want to build a lot more of.

Grounded in the clickfix-prevention-windows A/B pair (OFF run-2026-10-26-021, ON run-2026-10-27-001; inconclusive first attempt run-2026-10-26-022), graded by lab/harness/grade.py's prevention verdict. See the experiment spec (lab/prevention/clickfix-run-box-experiment.md) for the full runbook.