Cross-Frame Scripting (XFS) via Background Proxy in Vimium
Summary
Vimium's background script provides an openUrlInCurrentTab message handler that allows content scripts to request a URL to be opened in the current tab. When a javascript: URL is provided, the background script uses the chrome.scripting.executeScript API to inject and execute the code.
However, the background script does not specify a frameId in the injection target. According to the Chrome Extension API documentation for Manifest V3, if frameId is omitted, the script is injected into the main frame by default.
Since any frame (including cross-origin iframes) where Vimium is active can send this message to the background script, a compromised content script in a low-privilege subframe can execute arbitrary JavaScript in the high-privilege top-level origin.
Vulnerability Detail
- Vulnerability Type: Cross-Frame Scripting (XFS) / Privilege Escalation
- Affected Component:
background_scripts/tab_operations.js - Affected Versions: Up to 2.4.2 (Manifest V3)
Severity
Medium - This is a medium-severity privilege escalation vulnerability. It does not provide an initial entry point for an attack, but it can be used to significantly increase the impact of a separate, hypothetical DOM-based XSS flaw within Vimium. If an attacker achieves code execution within a subframe's Vimium context, this flaw allows them to escalate those...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| Vimium Product | < 2.4.3 | 2.4.3 |
Details and references
### Summary Vimium's background script provides an `openUrlInCurrentTab` message handler that allows content scripts to request a URL to be opened in the current tab. When a `javascript:` URL is provided, the background script uses the `chrome.scripting.executeScript` API to inject and execute the code. However, the background script does not specify a `frameId` in the injection target. According to the Chrome Extension API documentation for Manifest V3, if `frameId` is omitted, the script is injected into the **main frame** by default. Since any frame (including cross-origin iframes) where Vimium is active can send this message to the background script, a compromised content script in a low-privilege subframe can execute arbitrary JavaScript in the high-privilege top-level origin. ### Vulnerability Detail - **Vulnerability Type:** Cross-Frame Scripting (XFS) / Privilege Escalation - **Affected Component:** `background_scripts/tab_operations.js` - **Affected Versions:** Up to 2.4.2 (Manifest V3) ### Severity Medium - This is a medium-severity privilege escalation vulnerability. It does not provide an initial entry point for an attack, but it can be used to significantly increase the impact of a separate, hypothetical DOM-based XSS flaw within Vimium. If an attacker achieves code execution within a subframe's Vimium context, this flaw allows them to escalate those privileges to the top-level origin, potentially leading to Cross-Site Scripting (XSS) on the parent page. ### Proof of Concept The vulnerable code is located in `background_scripts/tab_operations.js`: ```javascript export async function openUrlInCurrentTab(request) { const urlStr = await UrlUtils.convertToUrl(request.url); // ... if (UrlUtils.hasJavascriptProtocol(urlStr)) { const scriptingArgs = { target: { tabId: request.tabId }, // VULNERABILITY: Omitted frameId func: (text) => { ... }, args: [urlStr], }; // ... chrome.scripting.executeScript(scriptingArgs); } } ``` The `request` object is populated from the `sender` in `background_scripts/main.js`, but `sender.frameId` was previously ignored during the injection phase. ## Reproduction Steps 1. Navigate to a website that embeds a cross-origin iframe (e.g., a page with a YouTube embed or a third-party ad). 2. Open **Chrome DevTools** (`F12`). 3. Go to the **Console** tab. 4. Click the **Execution Context selector** (the dropdown in the top-left of the console, usually showing `top`). 5. Find the entry for the **cross-origin iframe** and select the **Vimium** extension context associated with it (it will be listed under the extension's name). 6. Execute the following payload in the console: ```javascript chrome.runtime.sendMessage({ handler: "openUrlInCurrentTab", url: "javascript:alert('XFS Success! Target Origin: ' + window.location.origin + '\\nLogged from: ' + document.origin)", }); ``` 7. **Success Condition**: An alert dialog will appear. Observe that `window.location.origin` reflects the **top-level page's origin**, proving that the script was successfully proxied from the iframe into the parent frame. ### Further Analysis The background script must explicitly target the frame that originated the request. **Fix:** In `background_scripts/main.js`, `sender.frameId` is captured and passed to the handler. In `background_scripts/tab_operations.js`, the `frameId` is included in the `target` object: ```javascript target: { tabId: request.tabId, frameIds: [request.frameId] } ``` ### Timeline **Date reported**: 2026-07-17 **Date fixed**: 2026-07-22 **Date disclosed**: 2026-09-01 https://github.com/philc/vimium/commit/4427dc6783503c90b5975876f8547588d33c8ee7
- Severity from
- GitHub (reviewed advisory)