October 15, 2026
Clickjacking: When the User Clicks Without Knowing
The art of UI redressing โ from basic iframes to SVG filter tricks.

By Raksharoot
8 min read
This is Part 6 of the Web Pentesting series by Raksharoot. Missed the earlier parts? Catch up before this one. The full list is here: Web Pentesting: A Complete Guide
Disclaimer: Educational content only. Only test systems you own or have written permission to test. Never use these techniques on real users without explicit authorization.
Clickjacking is one of those bugs that sounds simple until you actually try it.
The idea is basic: put a transparent iframe over a button, get the user to click, and they end up clicking something on the hidden page. Classic UI redressing.
But the modern version of this bug is a lot weirder than the 2010-era tutorials make it sound. There are SVG filter tricks that distort iframes you can't even read. Double-click timing attacks that land clicks on pages you never framed. Popup-based variants that don't use iframes at all. And browser extension clickjacking that hijacks autofill dropdowns.
Let me walk through how this actually works today.
The core idea
A clickjacking attack has three pieces:
- A target page that has a sensitive action- a button, a link, a form submit.
- An attacker page that embeds the target in an iframe.
- A visual trick that makes the user think they're clicking something on the attacker page, when they're actually clicking the hidden target.
The classic version: set the iframe's opacity to something like 0.1 , position it over a decoy button, and let the user click.
The user sees "Click me." They click. The click lands on the hidden iframe's submit button. Done.
Prepopulating the form is the real power move
The trick above only works if the form is already filled out. Otherwise the user clicks a submit button on an empty form and nothing happens.
But a lot of apps support prepopulation via query parameters.
If ?email=attacker@evil.com fills the email field, you don't need the user to type anything. They just click, and the form submits with your value.
That turns a "meh" clickjacking bug into a full account takeover primitive. If the form is a password reset, an email change, or an MFA disable โ you own the account.
Always test for parameter prepopulation on framable forms. It's the difference between a PoC and a payout.
Drag-and-drop: when typing isn't an option
Sometimes the form won't prepopulate. The user has to type something.
Drag-and-drop solves this.
The attacker page has a draggable element with the payload embedded:
The user drags the element into the framed form's input field. The text gets written in. No typing required, just a drag and a click.
This works because drag-and-drop is a native browser behavior that crosses iframe boundaries in ways normal input doesn't. It's a smaller attack surface, but it's real.
XSS plus clickjacking: the combo bug
Here's a pattern that keeps showing up.
You find a self XSS, an XSS that only fires when the user submits their own input. Normally it's useless, because you can't force a victim to type a payload into their own profile.
But if the page with the form is framable and supports parameter prepopulation, you can combine the two:
- Frame the vulnerable page.
- Prepopulate the form with your XSS payload using query parameters.
- Get the user to click the submit button.
The form submits. The XSS fires. You now have XSS in the victim's session.
That's how a low-severity self-XSS becomes a high-severity account takeover.
DoubleClickjacking: the modern bypass
Every few years, browsers add a new clickjacking defense. And every few years, someone finds a new way around it.
DoubleClickjacking is the latest example. It doesn't rely on iframes at all, or at least, not in the traditional way.
The idea: ask the victim to double-click a button. Then use the timing gap between mousedown and click to swap what's under the cursor. The first click lands on your button. The second click lands on the target page's button, because you swapped the pages in between.
This bypasses every frame-busting defense, because the target is never framed. It's a full-page navigation that happens mid-double-click.
The catch: it only works for actions that require a single click. OAuth permission prompts. One-click confirmations. Approval dialogs.
So it's not universal, but for OAuth flows it's devastating.
Popup-based DoubleClickjacking
Some variants skip iframes entirely and use a popup window that tracks the cursor.
The attacker page tracks mousemove and moves a small popup window to stay under the cursor. While the popup is same-origin, moveTo() works. Once it's aligned with a target button, the popup navigates back to the target origin, so the next click lands on the real button.
Because cross-origin moveTo() is blocked, the popup has to briefly navigate to an attacker origin for repositioning, then use history.back() or location tricks to return to the target.
It's fiddly, but it works. And it means clickjacking doesn't require iframes anymore
Distorting secrets into CAPTCHA prompts
If a framable page renders a secret, a reset code, an API token, a session ID, you can distort the pixels until they look like a CAPTCHA. Then you add an input field on your own page that says "type the code you see."
The user transcribes the victim's secret into your input. You capture it. Game over.
The pattern attribute can even enforce the exact format of the secret, so the user can't submit anything wrong:
This is genuinely clever. It turns a rendered-secret page into a self-service extraction tool.
Manipulating form inputs
Filters can also surgically erase placeholder text, validation hints, or labels, while keeping the user's keystrokes visible.
The technique uses feComposite to amplify brightness, feTile to crop to the input field, feMorphology to thicken the typed glyphs, and feColorMatrix to isolate them. The result****: the victim types their real password into what looks like a normal input field, but they're actually typing into the hidden iframe.
On top of that, the attacker can overlay their own HTML labels โ "Enter your email," "Enter your password", so the victim thinks they're filling out a legitimate form.
Pixel probes and state machines
This is the part that's genuinely wild.
By cropping a tiny 2โ4 pixel region of the iframe with feTile , tiling it to full viewport, and thresholding the color, you can read UI state without JavaScript.
Then you can chain Boolean operations- AND, OR, XOR, NOT entirely inside the SVG filter graph. Which means you can build a state machine that tracks whether a dialog is open, whether a checkbox is checked, whether a success banner appeared.
Each state gates a different overlay bitmap. So you can walk a user through a multi-step workflow โ password reset, approval, destructive confirmation, without ever touching the DOM or knowing what's actually on the page.
This is one of those techniques that's so far outside the normal mental model of "clickjacking" that most testers never look for it.
Sandboxed iframes and the Basic Auth modal
Here's a niche one.
A sandboxed iframe without allow-popups normally can't open dialogs. But a 401 response with a WWW-Authenticate: Basic header triggers a browser-native auth prompt, and that prompt isn't considered a popup by the sandbox.
So you can do this:
The browser shows a login prompt inside a "sandboxed" widget. Users see a modal asking for credentials on a page that doesn't look like it should be asking. Some will type their real credentials. Password managers may even offer to autofill them.
It's phishing, but dressed up as a legitimate iframe widget.
Browser extension clickjacking
This is a newer variant and it's nasty.
Password managers inject autofill dropdowns near focused inputs. The attacker page focuses a controlled input and overlays or hides the extension's dropdown. A coerced user click selects a stored credential, and it gets filled into the attacker's field instead of a legitimate one.
No iframe required. Just DOM and CSS manipulation.
A real-world example: Dashlane disclosed a passkey dialog clickjacking issue in 2025. If the relying party domain had XSS, an attacker could overlay HTML on the extension's passkey dialog. A click on the attacker's element would proceed with the legitimate passkey login. The passkey material itself wasn't exposed, but the login went through, which is enough.
This is the kind of bug that only exists because browser extensions inject UI into pages. It's a new attack surface, and it's not well understood yet.
How to defend against it
Okay, enough attack stuff. What actually stops this?
1. Client-side defenses (weak)
Frame-busting scripts like if (top !== self) top.location = self.location are the old standby. They're better than nothing, but they fail more often than people think.
- If JavaScript is disabled, they don't run.
- If the iframe uses sandbox="allow-forms allow-scripts" (without allow-topnavigation ), the script can't escape the frame.
So don't rely on frame-busting scripts alone.
2. Server-side defenses (strong)
X-Frame-Options โ the original header:
- deny- nobody can frame the page
- sameorigin- only the same origin can frame it
- allow-from https://trusted.com โ only that origin can frame it (deprecated, inconsistently supported)
3. CSP frame-ancestors- the modern recommendation:
Content-Security-Policy: frame-ancestors 'self';
Options:
- frame-ancestors 'none'- equivalent to X-Frame-Options: deny
- frame-ancestors 'self'- equivalent to X-Frame-Options: sameorigin
- frame-ancestors https://trusted.com- only that origin can frame
This is the header you want. X-Frame-Options is still useful for old browsers, but frame-ancestors is the future.
4. CSP frame-src and child-src
Control what your page can frame, not who can frame you. Related but different:
Content-Security-Policy: frame-src 'self' https://trusted-website.com;
5. Anti-CSRF tokens
the last line of defense. Even if a clickjacking attack succeeds, a valid CSRF token makes the resulting request fail. But this only works if every state-changing endpoint actually validates the token.
The checklist
Not exhaustive, but this is the order I go in:
- Check if the target page is framable.
- Check X-Frame-Options and frame-ancestors.
- Test prepopulation via query parameters.
- Look for drag-and-drop writeable inputs.
- Test multistep flows with two decoys.
- Test self-XSS + clickjacking combos.
- Test double-click timing attacks.
- Test popup-based variants.
- Test SVG filter distortion on secret-rendering pages.
- Test sandboxed Basic Auth modals.
- Check browser extension UI overlays.
- Verify CSRF tokens on state-changing endpoints.
The thing to remember
Clickjacking isn't a single bug. It's a category of UI manipulation that gets reinvented every time browsers add a new feature.
The classic iframe trick still works on a shocking number of sites. The SVG filter tricks are terrifying once you understand them. And the newer variants, double-click timing, popup tracking, extension UI overlay, are still flying under most testers radars.
When you're testing, don't just check X-Frame-Options and move on. Look for sensitive actions that can be triggered with one click. Look for pages that render secrets. Look for browser-native UI that isn't protected by the same rules as the page itself.
That's where the real clickjacking bugs live.
That's Part 6. Read the whole series in order: Web Pentesting: A Complete Guide.