Hacktricks-skills browser-extension-pentest

Security testing methodology for browser extensions (Chrome, Firefox, Chromium). Use this skill whenever you need to audit, analyze, or pentest a browser extension for vulnerabilities. Trigger this skill for extension security reviews, manifest.json analysis, content script vulnerability assessment, native messaging security checks, or when investigating extension-based attacks. Don't forget to use this skill for any browser extension security work, even if the user doesn't explicitly mention "pentesting" or "security audit".

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

Browser Extension Pentesting Methodology

A comprehensive guide for security testing browser extensions across Chrome, Firefox, and Chromium-based browsers.

Quick Start

  1. Get the extension source (see Getting Source Code)
  2. Analyze manifest.json for permissions and attack surface
  3. Review content scripts for DOM-based vulnerabilities
  4. Check background scripts for message handling issues
  5. Test native messaging if present
  6. Run the security checklist

Getting Source Code

From Chrome Web Store (by Extension ID)

extension_id="YOUR_EXTENSION_ID"
curl -L -o "$extension_id.crx" "https://clients2.google.com/service/update2/crx?response=redirect&os=mac&arch=x86-64&nacl_arch=x86-64&prod=chromecrx&prodchannel=stable&prodversion=44.0.2403.130&x=id%3D$extension_id%26uc"
mv "$extension_id.crx" "$extension_id.zip"
unzip -d "$extension_id-source" "$extension_id.zip"

Alternative Methods

  • CRX Viewer website: https://robwu.nl/crxviewer/
  • CRX Viewer extension: Install from Chrome Web Store
  • Local profile: Navigate to
    chrome://version/
    → Profile Path →
    Extensions/
    folder
  • File archiver: Rename
    .crx
    to
    .zip
    and extract

Load Unpacked Extension

Chrome:

chrome://extensions/
→ Enable Developer Mode → Load unpacked

Firefox:

about:debugging#/runtime/this-firefox
→ Load Temporary Add-on

Extension Architecture

Browser extensions have three main components with strong isolation boundaries:

Content Scripts

  • Access: Direct DOM access to single web page
  • Risk: Exposed to potentially malicious input from the page
  • Permissions: Limited to sending messages to extension core
  • Isolation: Runs in separate JavaScript heap from web page

Extension Core (Background)

  • Access: Most extension privileges
  • Communication: XMLHttpRequest and content scripts only
  • Persistence: Runs continuously in background
  • No direct host machine access

Native Binary

  • Access: Full user privileges on host machine
  • Communication: NPAPI with extension core
  • Risk: Critical if compromised (RCE possible)

Manifest.json Analysis

The

manifest.json
is the extension's configuration file. Analyze these key fields:

Permissions

{
  "permissions": ["storage", "tabs", "nativeMessaging"],
  "host_permissions": ["https://*.example.com/*"]
}

Check for excessive permissions:

  • nativeMessaging
    - Can communicate with native binaries (RCE risk)
  • tabs
    - Can access all tab data
  • cookies
    - Can read all cookies
  • webRequest
    - Can intercept/modify network requests
  • storage
    - Can access extension storage

Content Scripts

"content_scripts": [
  {
    "js": ["script.js"],
    "matches": ["https://example.com/*"],
    "exclude_matches": ["*://*/*business*"],
    "run_at": "document_idle"
  }
]

Key fields:

  • matches
    - URLs where script injects (wider = more attack surface)
  • run_at
    - When script runs (
    document_start
    ,
    document_end
    ,
    document_idle
    )
  • js
    - JavaScript files to inject

Web Accessible Resources

"web_accessible_resources": [
  {
    "resources": ["images/*.png"],
    "matches": ["https://example.com/*"]
  }
]

Risk: Pages listed here can be accessed via

chrome-extension://<extension-id>/page.html
and may be vulnerable to:

  • ClickJacking (check CSP
    frame-ancestors
    )
  • XSS if the page processes user input

Externally Connectable

"externally_connectable": {
  "matches": ["https://*.google.com/*"]
}

Critical: If a vulnerable web page is listed here, attackers can:

  • Send messages directly to background script
  • Bypass content script and CSP protections
  • This is a very powerful bypass

Best practice: Use

{}
if no external connections needed

Content Security Policy

"content_security_policy": "script-src 'self'; object-src 'self';"

Default is restrictive but check for:

  • Whitelisted CDNs that could be compromised
  • unsafe-inline
    or
    unsafe-eval
    directives
  • Missing
    frame-ancestors
    for web_accessible_resources

Communication Patterns

Web Page ↔ Content Script

PostMessage (most common):

// Web page sends
window.postMessage({ type: "FROM_PAGE", text: "data" }, "*")

// Content script receives
window.addEventListener("message", (event) => {
  if (event.source !== window) return
  if (event.data.type === "FROM_PAGE") {
    // Handle message
  }
})

Security checks needed:

  • event.isTrusted
    - Verify user action triggered it
  • event.origin
    - Validate sender domain
  • event.source !== window
    - Check same window

Content Script ↔ Background Script

// Send message
chrome.runtime.sendMessage({ greeting: "hello" })

// Receive message
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.greeting === "hello") {
    sendResponse({ farewell: "goodbye" })
  }
})

For async responses, return

true
from the listener:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // Async operation
  setTimeout(() => {
    sendResponse({ result: "done" })
  }, 1000)
  return true // Keep channel open
})

Native Messaging

chrome.runtime.sendNativeMessage(
  "com.my_company.my_application",
  { text: "Hello" },
  (response) => console.log(response)
)

Native host JSON (installed on system):

{
  "name": "com.my_company.my_application",
  "path": "/path/to/binary",
  "type": "stdio",
  "allowed_origins": ["chrome-extension://<extension-id>/"]
}

Critical vulnerability pattern:

  1. Content script has wildcard URL match
  2. Content script forwards
    postMessage
    to background via
    sendMessage
  3. Background forwards to native app via
    sendNativeMessage
  4. Native app processes input unsafely → RCE

Vulnerability Testing

DOM-Based XSS

Content scripts trust the DOM. If the web page can modify DOM elements the content script reads:

// Vulnerable: Reading untrusted DOM
const data = document.getElementById("user-input").textContent
chrome.runtime.sendMessage({ data: data })

Test: Inject XSS into page elements the extension reads

PostMessage Bypasses

Check for:

  • Missing origin validation
  • Overly broad
    "*"
    target
  • Regex-based domain validation (can be bypassed)
  • Missing
    event.isTrusted
    check

ClickJacking

For

web_accessible_resources
pages:

  1. Check CSP for
    frame-ancestors
    directive
  2. Try loading in iframe:
    <iframe src="chrome-extension://<id>/page.html"></iframe>
  3. If loads, test UI overlay attacks

Information Disclosure

Memory: Sensitive data in extension memory can be dumped:

  • Chrome DevTools → Memory tab → Take snapshot → Search
  • Process memory dumps (Windows)

Code: Never store secrets in extension code (publicly visible)

Clipboard: Sensitive data copied to clipboard can be monitored

Security Audit Checklist

Use this checklist for every extension audit:

  • Permissions: Minimize requested permissions
  • Host permissions: Limit to necessary domains only
  • CSP: Strong policy, no
    unsafe-inline
    /
    unsafe-eval
  • Externally connectable: Set to
    {}
    if not needed
    • No XSS-vulnerable URLs listed
  • Web accessible resources: Minimize or empty
    • Check for ClickJacking if present
  • PostMessage communication: Validate origin, source, isTrusted
  • DOM access: Don't trust DOM data from web page
  • Native messaging: Sanitize all input, validate output
  • Sensitive data: Not in code, memory, or clipboard
  • File system: Sensitive data not stored unprotected

Tools

Tarnish

https://thehackerblog.com/tarnish/

Features:

  • Manifest.json viewer
  • Web accessible resources detection
  • ClickJacking analysis
  • Dangerous function detection (innerHTML, executeScript)
  • Entry point identification
  • CSP analyzer and bypass checker
  • Known vulnerable library detection (Retire.js)

Neto

https://github.com/elevenpaths/neto

Python package for analyzing browser extensions:

  • Extracts features from manifest.json
  • Parses JavaScript and HTML source
  • Analyzes localization folders

CRXaminer

https://crxaminer.tech/

Risk assessment based on permissions and other factors

Common Attack Patterns

Universal Code Execution

  1. Extension has wildcard content script match
  2. Content script forwards postMessage to background
  3. Background forwards to native messaging
  4. Native app executes unsanitized input

Impact: RCE on user's machine

Extension Takeover

  1. Find extension with vulnerable externally_connectable URL
  2. Compromise the URL (XSS, takeover)
  3. Send malicious messages directly to background script
  4. Bypass all content script protections

Forced Extension Load (Windows)

Edit per-user Preferences and forge HMACs to load arbitrary unpacked extensions without prompts. See

forced-extension-load-preferences-mac-forgery-windows.md
for details.

References

Quick Commands

Query vulnerable extensions from dataset

# Extensions with 250k+ users, content_scripts, and nativeMessaging
node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')"

Download and extract extension

ext_id="YOUR_ID"
curl -L -o "$ext_id.crx" "https://clients2.google.com/service/update2/crx?response=redirect&os=mac&arch=x86-64&prod=chromecrx&prodchannel=stable&x=id%3D$ext_id%26uc"
mv "$ext_id.crx" "$ext_id.zip"
unzip -d "$ext_id" "$ext_id.zip"

Load in Chrome for testing

  1. Go to
    chrome://extensions/
  2. Enable Developer Mode
  3. Click "Load unpacked"
  4. Select extracted extension folder