Cross-Frame Scripting (XFS) via Background Proxy in Vimium
MediumPublished Sep 1, 2026
### 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 increa...
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)
More Google advisories
All Google| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Sep 2 | Google Chrome: improper authorization | Low3.1 | 152.0.7977.75 |
| Sep 2 | Google Chrome: spoofing | Medium5.4 | 152.0.7977.75 |
| Sep 2 | Google Chrome: information disclosure | Medium5.3 | 152.0.7977.75 |
| Sep 2 | Google Chrome: missing authorization | Low3.1 | 152.0.7977.75 |
| Sep 2 | Google Chrome: improper authorization | Medium6.5 | 152.0.7977.75 |
| Sep 2 | Google Chrome: remote code execution | High8.8 | 152.0.7977.75 |