# Méthodologie de pentesting des extensions de navigateur {{#include ../../banners/hacktricks-training.md}} ## Informations de base Les extensions de navigateur sont écrites en JavaScript et chargées par le navigateur en arrière-plan. Elles possèdent leur [DOM](https://www.w3schools.com/js/js_htmldom.asp) mais peuvent interagir avec le DOM d'autres sites. Cela signifie qu'elles peuvent compromettre la confidentialité, l'intégrité et la disponibilité (CIA) d'autres sites. ## Principaux composants La structure d'une extension se visualise mieux et se compose de trois composants. Examinons chacun en détail.

http://webblaze.cs.berkeley.edu/papers/Extensions.pdf

### **Content Scripts** Chaque content script a un accès direct au DOM d'une **page web unique** et est ainsi exposé à des **entrées potentiellement malveillantes**. Cependant, le content script ne possède aucune permission autre que la possibilité d'envoyer des messages à l'extension core. ### **Extension Core** L'extension core contient la plupart des privilèges/accès de l'extension, mais l'extension core ne peut interagir avec le contenu web que via [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) et les content scripts. De plus, l'extension core n'a pas d'accès direct à la machine hôte. ### **Native Binary** L'extension peut inclure un binaire natif qui peut **accéder à la machine hôte avec les privilèges complets de l'utilisateur.** Le binaire natif interagit avec l'extension core via l'API standard Netscape Plugin Application Programming Interface ([NPAPI](https://en.wikipedia.org/wiki/NPAPI)) utilisée par Flash et d'autres plug-ins de navigateur. ### Boundaries > [!CAUTION] > Pour obtenir les privilèges complets de l'utilisateur, un attaquant doit convaincre l'extension de transmettre une entrée malveillante du content script vers le core de l'extension, puis du core de l'extension vers le binaire natif. Chaque composant de l'extension est séparé des autres par des **barrières de protection solides**. Chaque composant s'exécute dans un **processus distinct du système d'exploitation**. Les content scripts et les extension cores s'exécutent dans des **processus sandbox** non accessibles à la plupart des services du système d'exploitation. De plus, les content scripts sont séparés de leurs pages web associées en **s'exécutant dans un tas JavaScript séparé**. Le content script et la page web ont **accès au même DOM sous-jacent**, mais les deux **n'échangent jamais de pointeurs JavaScript**, empêchant le leak de fonctionnalités JavaScript. ## **`manifest.json`** Une extension Chrome n'est qu'un dossier ZIP avec une extension de fichier [.crx](https://www.lifewire.com/crx-file-2620391). Le core de l'extension est le fichier **`manifest.json`** à la racine du dossier, qui spécifie la structure, les permissions et d'autres options de configuration. Exemple: ```json { "manifest_version": 2, "name": "My extension", "version": "1.0", "permissions": ["storage"], "content_scripts": [ { "js": ["script.js"], "matches": ["https://example.com/*", "https://www.example.com/*"], "exclude_matches": ["*://*/*business*"] } ], "background": { "scripts": ["background.js"] }, "options_ui": { "page": "options.html" } } ``` ### `content_scripts` Les content scripts sont **chargés** chaque fois que l'utilisateur **navigue vers une page correspondante**, dans notre cas toute page correspondant à l'expression **`https://example.com/*`** et ne correspondant pas au regex **`*://*/*/business*`**. Ils s'exécutent **comme les propres scripts de la page** et ont un accès arbitraire au [Document Object Model (DOM)](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model). ```json "content_scripts": [ { "js": [ "script.js" ], "matches": [ "https://example.com/*", "https://www.example.com/*" ], "exclude_matches": ["*://*/*business*"], } ], ``` Pour inclure ou exclure davantage d'URLs, il est également possible d'utiliser **`include_globs`** et **`exclude_globs`**. Ceci est un exemple de content script qui ajoutera un bouton explain à la page et utilisera [the storage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) pour récupérer la valeur `message` depuis le stockage de l'extension. ```js chrome.storage.local.get("message", (result) => { let div = document.createElement("div") div.innerHTML = result.message + " " div.querySelector("button").addEventListener("click", () => { chrome.runtime.sendMessage("explain") }) document.body.appendChild(div) }) ```
Un message est envoyé aux pages de l'extension par le content script lorsque ce bouton est cliqué, via l'utilisation de la [**runtime.sendMessage() API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage). Ceci est dû à la limitation du content script en matière d'accès direct aux APIs, `storage` étant l'une des rares exceptions. Pour les fonctionnalités dépassant ces exceptions, des messages sont envoyés aux pages de l'extension avec lesquelles les content scripts peuvent communiquer. > [!WARNING] > Selon le navigateur, les capacités du content script peuvent varier légèrement. Pour les navigateurs basés sur Chromium, la liste des capacités est disponible dans la [Chrome Developers documentation](https://developer.chrome.com/docs/extensions/mv3/content_scripts/#capabilities), et pour Firefox, le [MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#webextension_apis) sert de source principale.\ > Il est également important de noter que les content scripts peuvent communiquer avec les background scripts, leur permettant d'effectuer des actions et de renvoyer des réponses. Pour visualiser et déboguer les content scripts dans Chrome, le menu Chrome developer tools est accessible via Options > More tools > Developer tools OU en appuyant sur Ctrl + Shift + I. Une fois les developer tools affichés, cliquez sur l'onglet **Source tab**, puis sur l'onglet **Content Scripts**. Cela permet d'observer les content scripts en cours d'exécution provenant de différentes extensions et de poser des points d'arrêt pour suivre le flux d'exécution. ### Content scripts injectés > [!TIP] > Notez que **Content Scripts ne sont pas obligatoires** car il est aussi possible d'**injecter dynamiquement** des scripts et de les **injecter programmatiquement** dans des pages web via **`tabs.executeScript`**. Cela fournit en fait des contrôles plus **granulaires**. Pour l'injection programmatique d'un content script, l'extension doit disposer des [host permissions](https://developer.chrome.com/docs/extensions/reference/permissions) pour la page dans laquelle les scripts seront injectés. Ces permissions peuvent être obtenues soit en les **demandant** dans le manifest de l'extension, soit temporairement via [**activeTab**](https://developer.chrome.com/docs/extensions/reference/manifest/activeTab). #### Exemple d'extension basée sur activeTab ```json:manifest.json { "name": "My extension", ... "permissions": [ "activeTab", "scripting" ], "background": { "service_worker": "background.js" }, "action": { "default_title": "Action Button" } } ``` - **Injecter un fichier JS au clic:** ```javascript // content-script.js document.body.style.backgroundColor = "orange" //service-worker.js - Inject the JS file chrome.action.onClicked.addListener((tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ["content-script.js"], }) }) ``` - **Injecter une fonction** au clic : ```javascript //service-worker.js - Inject a function function injectedFunction() { document.body.style.backgroundColor = "orange" } chrome.action.onClicked.addListener((tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: injectedFunction, }) }) ``` #### Exemple avec autorisations de script ```javascript // service-workser.js chrome.scripting.registerContentScripts([ { id: "test", matches: ["https://*.example.com/*"], excludeMatches: ["*://*/*business*"], js: ["contentScript.js"], }, ]) // Another example chrome.tabs.executeScript(tabId, { file: "content_script.js" }) ``` Pour inclure ou exclure davantage d'URLs, il est également possible d'utiliser **`include_globs`** et **`exclude_globs`**. ### Scripts de contenu `run_at` Le champ `run_at` contrôle **quand les fichiers JavaScript sont injectés dans la page web**. La valeur préférée et par défaut est `"document_idle"`. The possible values are: - **`document_idle`**: Dès que possible - **`document_start`**: Après les fichiers `css`, mais avant que le DOM ne soit construit ou que d'autres scripts ne s'exécutent. - **`document_end`**: Immédiatement après que le DOM est complet, mais avant que les sous-ressources comme les images et les frames ne soient chargées. #### Via `manifest.json` ```json { "name": "My extension", ... "content_scripts": [ { "matches": ["https://*.example.com/*"], "run_at": "document_idle", "js": ["contentScript.js"] } ], ... } ``` Via **`service-worker.js`** ```javascript chrome.scripting.registerContentScripts([ { id: "test", matches: ["https://*.example.com/*"], runAt: "document_idle", js: ["contentScript.js"], }, ]) ``` ### `background` Les messages envoyés par les content scripts sont reçus par la **background page**, qui joue un rôle central dans la coordination des composants de l'extension. Notamment, la background page persiste tout au long de la durée de vie de l'extension, fonctionnant discrètement sans interaction directe avec l'utilisateur. Elle possède son propre Document Object Model (DOM), permettant des interactions complexes et la gestion d'état. **Points clés**: - **Background Page Role:** Agit comme le centre névralgique de l'extension, assurant la communication et la coordination entre les différentes parties de l'extension. - **Persistence:** C'est une entité toujours présente, invisible pour l'utilisateur mais essentielle au fonctionnement de l'extension. - **Automatic Generation:** Si elle n'est pas définie explicitement, le navigateur créera automatiquement une background page. Cette page auto-générée inclura tous les background scripts spécifiés dans le manifest de l'extension, garantissant le fonctionnement transparent des tâches en arrière-plan de l'extension. > [!TIP] > La commodité offerte par le navigateur en générant automatiquement une background page (lorsqu'elle n'est pas déclarée explicitement) garantit que tous les background scripts nécessaires sont intégrés et opérationnels, simplifiant le processus de configuration de l'extension. Exemple de background script: ```js chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request == "explain") { chrome.tabs.create({ url: "https://example.net/explanation" }) } }) ``` Il utilise [runtime.onMessage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage) pour écouter les messages. Lorsqu'un message `"explain"` est reçu, il utilise [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) pour ouvrir une page dans un nouvel onglet. Pour déboguer le script d'arrière-plan vous pouvez aller aux **extension details and inspect the service worker,** cela ouvrira les outils de développement avec le script d'arrière-plan :
### Options pages and other Les extensions de navigateur peuvent contenir différents types de pages : - **Action pages** sont affichées dans un **drop-down when the extension ico**n est cliqué. - Pages que l'extension **load in a new tab**. - **Option Pages** : Cette page s'affiche au-dessus de l'extension lorsqu'on clique dessus. Dans le manifest précédent, dans mon cas j'ai pu accéder à cette page via `chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca` ou en cliquant :
Notez que ces pages ne sont pas persistantes comme les pages d'arrière-plan car elles chargent dynamiquement le contenu selon les besoins. Malgré cela, elles partagent certaines capacités avec la page d'arrière-plan : - **Communication with Content Scripts:** De la même façon que la page d'arrière-plan, ces pages peuvent recevoir des messages depuis des content scripts, facilitant l'interaction au sein de l'extension. - **Access to Extension-Specific APIs:** Ces pages bénéficient d'un accès complet aux APIs spécifiques à l'extension, sous réserve des permissions définies pour l'extension. ### `permissions` & `host_permissions` **`permissions`** et **`host_permissions`** sont des entrées du `manifest.json` qui indiqueront **quelles permissions** l'extension du navigateur possède (storage, location...) et **sur quelles pages web**. Comme les extensions de navigateur peuvent être très **privilégiées**, une extension malveillante ou compromise pourrait permettre à un attaquant **différents moyens de voler des informations sensibles et d'espionner l'utilisateur**. Voir comment ces paramètres fonctionnent et comment ils peuvent être abusés dans : {{#ref}} browext-permissions-and-host_permissions.md {{#endref}} ### `content_security_policy` Une **content security policy** peut également être déclarée dans le `manifest.json`. Si une est définie, elle pourrait être **vulnérable**. Le paramètre par défaut pour les pages d'extension du navigateur est plutôt restrictif : ```bash script-src 'self'; object-src 'self'; ``` Pour plus d'infos sur CSP and potential bypasses, consultez: {{#ref}} ../content-security-policy-csp-bypass/ {{#endref}} ### `web_accessible_resources` Pour qu'une page web puisse accéder à une page d'une extension de navigateur, par exemple une page `.html`, cette page doit être mentionnée dans le champ **`web_accessible_resources`** de `manifest.json`.\ Par exemple: ```javascript { ... "web_accessible_resources": [ { "resources": [ "images/*.png" ], "matches": [ "https://example.com/*" ] }, { "resources": [ "fonts/*.woff" ], "matches": [ "https://example.com/*" ] } ], ... } ``` Ces pages sont accessibles via une URL comme : ``` chrome-extension:///message.html ``` Dans les extensions publiques, l'**extension-id est accessible** :
Cependant, si le paramètre `manifest.json` **`use_dynamic_url`** est utilisé, cet **id peut être dynamique**. > [!TIP] > Notez que même si une page est mentionnée ici, elle peut être **protégée contre le ClickJacking** grâce à la **Content Security Policy**. Vous devez donc également la vérifier (frame-ancestors section) avant de confirmer qu'une attaque ClickJacking est possible. Le fait de pouvoir accéder à ces pages les rend **potentiellement vulnérables au ClickJacking** : {{#ref}} browext-clickjacking.md {{#endref}} > [!TIP] > Autoriser le chargement de ces pages uniquement par l'extension et non par des URL aléatoires pourrait empêcher les attaques ClickJacking. > [!CAUTION] > Notez que les pages définies dans **`web_accessible_resources`** et d'autres pages de l'extension sont également capables de **contacter les background scripts**. Donc si l'une de ces pages est vulnérable à **XSS**, cela pourrait déboucher sur une vulnérabilité plus importante. > > De plus, notez que vous ne pouvez ouvrir dans des iframes que les pages indiquées dans **`web_accessible_resources`**, mais depuis un nouvel onglet il est possible d'accéder à n'importe quelle page de l'extension en connaissant l'extension ID. Par conséquent, si un XSS est découvert en abusant des mêmes paramètres, il pourrait être exploité même si la page n'est pas configurée dans **`web_accessible_resources`**. ### `externally_connectable` D'après la [**docs**](https://developer.chrome.com/docs/extensions/reference/manifest/externally_connectable), la propriété de manifest `"externally_connectable"` déclare **quelles extensions et quelles pages web peuvent se connecter** à votre extension via [runtime.connect](https://developer.chrome.com/docs/extensions/reference/runtime#method-connect) et [runtime.sendMessage](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage). - Si la clé **`externally_connectable`** n'est **pas** déclarée dans le manifest de votre extension ou si elle est déclarée comme **`"ids": ["*"]`**, **toutes les extensions peuvent se connecter, mais aucune page web ne peut se connecter**. - Si des **IDs spécifiques sont indiquées**, comme dans `"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]`, **seules ces applications** peuvent se connecter. - Si des **matches** sont spécifiés, ces web apps pourront se connecter : ```json "matches": [ "https://*.google.com/*", "*://*.chromium.org/*", ``` - Si c'est spécifié comme vide : **`"externally_connectable": {}`**, aucune app ou page web ne pourra se connecter. **Moins d'extensions et d'URLs** indiquées ici, **plus petite sera la surface d'attaque**. > [!CAUTION] > Si une page web **vulnerable to XSS or takeover** est indiquée dans **`externally_connectable`**, un attaquant pourra **envoyer des messages directement au background script**, contournant complètement le Content Script et son CSP. > > Par conséquent, il s'agit d'un **contournement très puissant**. > > De plus, si le client installe une extension malveillante, même si elle n'est pas autorisée à communiquer avec l'extension vulnérable, elle pourrait injecter **XSS data in an allowed web page** ou abuser des APIs **`WebRequest`** ou **`DeclarativeNetRequest`** pour manipuler les requêtes sur un domaine ciblé, altérant la requête d'une page pour un **JavaScript file**. (Notez que le CSP de la page ciblée pourrait prévenir ces attaques). Cette idée provient [**de cet article**](https://www.darkrelay.com/post/opera-zero-day-rce-vulnerability). ## Communication summary ### Extension <--> WebApp Pour communiquer entre le content script et la page web, on utilise généralement des post messages. Ainsi, dans l'application web vous trouverez généralement des appels à la fonction **`window.postMessage`** et, dans le content script, des listeners comme **`window.addEventListener`**. Notez cependant que l'extension peut aussi **communiquer avec l'application web en envoyant un Post Message** (et donc la page web doit s'y attendre) ou simplement faire charger un nouveau script par la page web. ### Inside the extension Généralement la fonction **`chrome.runtime.sendMessage`** est utilisée pour envoyer un message au sein de l'extension (généralement pris en charge par le `background` script) et, pour le recevoir et le gérer, un listener est déclaré en appelant **`chrome.runtime.onMessage.addListener`**. Il est aussi possible d'utiliser **`chrome.runtime.connect()`** pour avoir une connexion persistante au lieu d'envoyer des messages isolés ; on peut l'utiliser pour **envoyer** et **recevoir** des **messages** comme dans l'exemple suivant :
chrome.runtime.connect() exemple ```javascript var port = chrome.runtime.connect() // Listen for messages from the web page window.addEventListener( "message", (event) => { // Only accept messages from the same window if (event.source !== window) { return } // Check if the message type is "FROM_PAGE" if (event.data.type && event.data.type === "FROM_PAGE") { console.log("Content script received: " + event.data.text) // Forward the message to the background script port.postMessage({ type: "FROM_PAGE", text: event.data.text }) } }, false ) // Listen for messages from the background script port.onMessage.addListener(function (msg) { console.log("Content script received message from background script:", msg) // Handle the response message from the background script }) ```
Il est aussi possible d'envoyer des messages depuis un script d'arrière-plan vers un script de contenu situé dans un onglet spécifique en appelant **`chrome.tabs.sendMessage`** où vous devrez indiquer le **ID de l'onglet** auquel envoyer le message. ### Depuis les `externally_connectable` autorisés vers l'extension **Les web apps et les extensions de navigateur externes autorisées** dans la configuration `externally_connectable` peuvent envoyer des requêtes en utilisant : ```javascript chrome.runtime.sendMessage(extensionId, ... ``` Lorsque nécessaire pour mentionner l'**extension ID**. ### Native Messaging Il est possible que les background scripts communiquent avec des binaries du système, ce qui peut être **exposé à des vulnérabilités critiques telles que les RCEs** si cette communication n'est pas correctement sécurisée. [More on this later](#native-messaging). ```javascript chrome.runtime.sendNativeMessage( "com.my_company.my_application", { text: "Hello" }, function (response) { console.log("Received " + response) } ) ``` ## Communication Web **↔︎** Content Script Les environnements où opèrent les **content scripts** et où résident les pages hôtes sont **séparés** les uns des autres, garantissant **l'isolation**. Malgré cette isolation, les deux peuvent interagir avec le **Document Object Model (DOM)** de la page, une ressource partagée. Pour que la page hôte communique avec le **content script**, ou indirectement avec l'extension via le content script, elle doit utiliser le **DOM** accessible aux deux comme canal de communication. ### Post Messages ```javascript:content-script.js // This is like "chrome.runtime.sendMessage" but to maintain the connection var port = chrome.runtime.connect() window.addEventListener( "message", (event) => { // We only accept messages from ourselves if (event.source !== window) { return } if (event.data.type && event.data.type === "FROM_PAGE") { console.log("Content script received: " + event.data.text) // Forward the message to the background script port.postMessage(event.data.text) } }, false ) ``` ```javascript:example.js document.getElementById("theButton").addEventListener( "click", () => { window.postMessage( { type: "FROM_PAGE", text: "Hello from the webpage!" }, "*" ) }, false ) ``` Une communication Post Message sécurisée devrait vérifier l'authenticité du message reçu, cela peut être fait en vérifiant : - **`event.isTrusted`**: This is True only if the event was triggered by a users action - Le content script peut n'attendre un message que si l'utilisateur effectue une action - **origin domain**: n'accepter un message que d'une allowlist de domaines. - If a regex is used, be very careful - **Source**: `received_message.source !== window` can be used to check if the message was **from the same window** where the Content Script is listening. Les vérifications précédentes, même si elles sont effectuées, pourraient être vulnérables — consultez la page suivante : **potential Post Message bypasses** : {{#ref}} ../postmessage-vulnerabilities/ {{#endref}} ### Iframe Un autre moyen de communication possible peut être via des **Iframe URLs**, vous trouverez un exemple dans : {{#ref}} browext-xss-example.md {{#endref}} ### DOM Ce n'est pas "exactement" une voie de communication, mais le **web et le content script auront accès au web DOM**. Donc, si le **content script** lit des informations à partir de celui-ci, en **faisant confiance au web DOM**, le web pourrait **modifier ces données** (parce que le web ne devrait pas être digne de confiance, ou parce que le web est vulnérable à XSS) et **compromettre le Content Script**. Vous pouvez également trouver un exemple de **DOM based XSS to compromise a browser extension** dans : {{#ref}} browext-xss-example.md {{#endref}} ## Communication Content Script **↔︎** Background Script Un Content Script peut utiliser les fonctions [**runtime.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage) **or** [**tabs.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/tabs#method-sendMessage) pour envoyer un message **one-time JSON-serializable**. Pour gérer la **réponse**, utilisez la **Promise** retournée. Cependant, pour la rétrocompatibilité, vous pouvez toujours passer un **callback** comme dernier argument. Envoyer une requête depuis un **content script** ressemble à ceci : ```javascript ;(async () => { const response = await chrome.runtime.sendMessage({ greeting: "hello" }) // do something with response here, not outside the function console.log(response) })() ``` Envoyer une requête depuis l'**extension** (généralement un **background script**). Exemple montrant comment envoyer un message au content script dans l'onglet sélectionné : ```javascript // From https://stackoverflow.com/questions/36153999/how-to-send-a-message-between-chrome-extension-popup-and-content-script ;(async () => { const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true, }) const response = await chrome.tabs.sendMessage(tab.id, { greeting: "hello" }) // do something with response here, not outside the function console.log(response) })() ``` Du côté **destinataire**, vous devez mettre en place un [**runtime.onMessage**](https://developer.chrome.com/docs/extensions/reference/runtime#event-onMessage) **event listener** pour gérer le message. Cela est identique depuis un content script ou une extension page. ```javascript // From https://stackoverflow.com/questions/70406787/javascript-send-message-from-content-js-to-background-js chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { console.log( sender.tab ? "from a content script:" + sender.tab.url : "from the extension" ) if (request.greeting === "hello") sendResponse({ farewell: "goodbye" }) }) ``` Dans l'exemple mis en évidence, **`sendResponse()`** a été exécuté de manière synchrone. Pour modifier le gestionnaire d'événement `onMessage` afin que `sendResponse()` s'exécute de façon asynchrone, il est impératif d'incorporer `return true;`. Il est important de noter que lorsque plusieurs pages sont configurées pour recevoir des événements `onMessage`, **la première page à exécuter `sendResponse()`** pour un événement donné sera la seule à pouvoir délivrer la réponse efficacement. Les réponses ultérieures au même événement ne seront pas prises en compte. Lors de la création de nouvelles extensions, la préférence doit aller aux promises plutôt qu'aux callbacks. En ce qui concerne l'utilisation de callbacks, la fonction `sendResponse()` n'est considérée comme valide que si elle est exécutée directement dans le contexte synchrone, ou si le gestionnaire d'événement indique une opération asynchrone en retournant `true`. Si aucun des gestionnaires ne retourne `true`, ou si la fonction `sendResponse()` est supprimée de la mémoire (garbage-collected), le callback associé à `sendMessage()` sera déclenché par défaut. ## Native Messaging Les extensions de navigateur permettent également de communiquer avec des **binaires du système via stdin**. L'application doit installer un json l'indiquant dans un json comme : ```json { "name": "com.my_company.my_application", "description": "My Application", "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe", "type": "stdio", "allowed_origins": ["chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"] } ``` Où le `name` est la chaîne passée à [`runtime.connectNative()`](https://developer.chrome.com/docs/extensions/reference/api/runtime#method-connectNative) ou [`runtime.sendNativeMessage()`](https://developer.chrome.com/docs/extensions/reference/api/runtime#method-sendNativeMessage) pour communiquer avec l'application depuis les scripts d'arrière-plan de l'extension de navigateur. Le `path` est le chemin vers le binaire, il n'y a qu'un seul `type` valide qui est stdio (utiliser stdin et stdout) et les `allowed_origins` indiquent les extensions qui peuvent y accéder (et ne peuvent pas contenir de caractère générique). Chrome/Chromium recherchera ce json dans certaines clés de registre Windows et dans certains chemins sur macOS et Linux (plus d'infos dans les [**docs**](https://developer.chrome.com/docs/extensions/develop/concepts/native-messaging)). > [!TIP] > L'extension du navigateur a également besoin que la permission `nativeMessaing` soit déclarée pour pouvoir utiliser cette communication. Voici à quoi ressemble le code d'un script d'arrière-plan envoyant des messages à une application native : ```javascript chrome.runtime.sendNativeMessage( "com.my_company.my_application", { text: "Hello" }, function (response) { console.log("Received " + response) } ) ``` In [**this blog post**](https://spaceraccoon.dev/universal-code-execution-browser-extensions/), un schéma vulnérable abusant des native messages est proposé : 1. L'extension de navigateur a un pattern wildcard pour le content script. 2. Le content script transmet des messages `postMessage` au background script en utilisant `sendMessage`. 3. Le background script transmet le message à l'application native en utilisant `sendNativeMessage`. 4. L'application native traite le message de manière dangereuse, conduisant à une exécution de code. Et à l'intérieur, un exemple expliquant **comment passer de n'importe quelle page à RCE en abusant d'une extension de navigateur** est présenté. ## Sensitive Information in Memory/Code/Clipboard Si une extension de navigateur stocke **des informations sensibles dans sa mémoire**, celles-ci peuvent être **extraites** (surtout sur les machines Windows) et **recherchées** pour retrouver ces informations. Par conséquent, la mémoire de l'extension de navigateur **ne doit pas être considérée comme sûre** et les **informations sensibles** telles que des identifiants ou des phrases mnémoniques **ne doivent pas y être stockées**. Bien sûr, ne mettez pas d'informations sensibles dans le code, car il sera **public**. Pour dumper la mémoire du navigateur, vous pouvez **dump the process memory** ou aller dans les **settings** de l'extension de navigateur, cliquer sur **`Inspect pop-up`** -> Dans la section **`Memory`** -> **`Take a snaphost`** et faire **`CTRL+F`** pour rechercher dans le snapshot des informations sensibles. De plus, des informations hautement sensibles comme des clés mnémoniques ou des mots de passe **ne devraient pas pouvoir être copiées dans le presse-papiers** (ou au moins les supprimer du presse-papiers après quelques secondes), car des processus surveillant le presse-papiers pourraient alors les récupérer. ## Loading an Extension in the Browser 1. **Download** the Browser Extension & unzipped 2. Go to **`chrome://extensions/`** and **enable** the `Developer Mode` 3. Click the **`Load unpacked`** button In **Firefox** you go to **`about:debugging#/runtime/this-firefox`** and click **`Load Temporary Add-on`** button. ## Getting the source code from the store Le code source d'une extension Chrome peut être obtenu de plusieurs manières. Vous trouverez ci-dessous des explications détaillées et des instructions pour chaque option. ### Download Extension as ZIP via Command Line Le code source d'une extension Chrome peut être téléchargé en tant que fichier ZIP en utilisant la ligne de commande. Cela implique d'utiliser `curl` pour récupérer le fichier ZIP depuis une URL spécifique puis d'extraire le contenu du ZIP dans un répertoire. Voici les étapes : 1. Remplacez `"extension_id"` par l'ID réel de l'extension. 2. Exécutez les commandes suivantes : ```bash extension_id=your_extension_id # Replace with the actual extension ID curl -L -o "$extension_id.zip" "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" unzip -d "$extension_id-source" "$extension_id.zip" ``` ### Utiliser le site CRX Viewer [https://robwu.nl/crxviewer/](https://robwu.nl/crxviewer/) ### Utiliser l'extension CRX Viewer Une autre méthode pratique consiste à utiliser le Chrome Extension Source Viewer, qui est un projet open-source. Il peut être installé depuis le [Chrome Web Store](https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin?hl=en). Le code source du viewer est disponible dans son [GitHub repository](https://github.com/Rob--W/crxviewer). ### Voir le code source d'une extension installée localement Les extensions Chrome installées localement peuvent aussi être inspectées. Voici comment : 1. Accédez au répertoire de profil local de Chrome en visitant `chrome://version/` et en localisant le champ "Profile Path". 2. Naviguez vers le sous-dossier `Extensions/` dans le répertoire de profil. 3. Ce dossier contient toutes les extensions installées, généralement avec leur code source dans un format lisible. Pour identifier les extensions, vous pouvez faire correspondre leurs IDs à leurs noms : - Activez le Developer Mode sur la page `about:extensions` pour voir les IDs de chaque extension. - Dans le dossier de chaque extension, le fichier `manifest.json` contient un champ `name` lisible, vous aidant à identifier l'extension. ### Utiliser un archiveur ou un outil de décompression Allez sur le Chrome Web Store et téléchargez l'extension. Le fichier aura l'extension `.crx`. Changez l'extension du fichier de `.crx` en `.zip`. Utilisez un archiveur (comme WinRAR, 7-Zip, etc.) pour extraire le contenu du fichier ZIP. ### Utiliser le Developer Mode dans Chrome Ouvrez Chrome et allez sur `chrome://extensions/`. Activez "Developer mode" en haut à droite. Cliquez sur "Load unpacked extension...". Naviguez jusqu'au répertoire de votre extension. Cela ne télécharge pas le code source, mais c'est utile pour consulter et modifier le code d'une extension déjà téléchargée ou développée. ## Chrome extension manifest dataset Pour essayer de repérer des browser extensions vulnérables, vous pouvez utiliser le[https://github.com/palant/chrome-extension-manifests-dataset](https://github.com/palant/chrome-extension-manifests-dataset) et vérifier leurs fichiers manifest pour des signes potentiellement vulnérables. Par exemple, pour rechercher des extensions ayant plus de 25000 utilisateurs, `content_scripts` et la permission `nativeMessaing` : ```bash # Query example from https://spaceraccoon.dev/universal-code-execution-browser-extensions/ node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')" ``` ## Post-exploitation: Forced extension load & persistence (Windows) Stealthy technique to backdoor Chromium by directly editing per-user Preferences and forging valid HMACs, causing the browser to accept and activate an arbitrary unpacked extension without prompts or flags. {{#ref}} forced-extension-load-preferences-mac-forgery-windows.md {{#endref}} ## Security Audit Checklist Even though Browser Extensions have a **limited attack surface**, some of them might contain **vulnerabilities** or **potential hardening improvements**. The following ones are the most common ones: - [ ] **Limit** as much as possible requested **`permissions`** - [ ] **Limit** as much as possible **`host_permissions`** - [ ] Use a **strong** **`content_security_policy`** - [ ] **Limit** as much as possible the **`externally_connectable`**, if none is needed and possible, do not leave it by default, specify **`{}`** - [ ] If **URL vulnerable to XSS or to takeover** is mentioned here, an attacker will be able to **send messages to the background scripts directly**. Very powerful bypass. - [ ] **Limit** as much as possible the **`web_accessible_resources`**, even empty if possible. - [ ] If **`web_accessible_resources`** is not none, check for [**ClickJacking**](browext-clickjacking.md) - [ ] If any **communication** occurs from the **extension** to the **web page**, [**check for XSS**](browext-xss-example.md) **vulnerabilities** caused in the communication. - [ ] If Post Messages are used, check for [**Post Message vulnerabilities**](../postmessage-vulnerabilities/index.html)**.** - [ ] If the **Content Script access DOM details**, check that they **aren't introducing a XSS** if they get **modified** by the web - [ ] Make a special emphasis if this communication is also involved in the **Content Script -> Background script communication** - [ ] If the background script is communicating via **native messaging** check the communication is secure and sanitized - [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **code** - [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **memory** - [ ] **Sensitive information shouldn't be stored** inside the **file system unprotected** ## Browser Extension Risks - The app [https://crxaminer.tech/](https://crxaminer.tech/) analyzes some data like the permissions browser extension requests to give a risk level of using the browser extension. ## Tools ### [**Tarnish**](https://thehackerblog.com/tarnish/) - Pulls any Chrome extension from a provided Chrome webstore link. - [**manifest.json**](https://developer.chrome.com/extensions/manifest) **viewer**: simply displays a JSON-prettified version of the extension’s manifest. - **Fingerprint Analysis**: Detection of [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) and automatic generation of Chrome extension fingerprinting JavaScript. - **Potential Clickjacking Analysis**: Detection of extension HTML pages with the [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) directive set. These are potentially vulnerable to clickjacking depending on the purpose of the pages. - **Permission Warning(s) viewer**: which shows a list of all the Chrome permission prompt warnings which will be displayed upon a user attempting to install the extension. - **Dangerous Function(s)**: shows the location of dangerous functions which could potentially be exploited by an attacker (e.g. functions such as innerHTML, chrome.tabs.executeScript). - **Entry Point(s)**: shows where the extension takes in user/external input. This is useful for understanding an extension’s surface area and looking for potential points to send maliciously-crafted data to the extension. - Both the Dangerous Function(s) and Entry Point(s) scanners have the following for their generated alerts: - Relevant code snippet and line that caused the alert. - Description of the issue. - A “View File” button to view the full source file containing the code. - The path of the alerted file. - The full Chrome extension URI of the alerted file. - The type of file it is, such as a Background Page script, Content Script, Browser Action, etc. - If the vulnerable line is in a JavaScript file, the paths of all of the pages where it is included as well as these page’s type, and [web_accessible_resource](https://developer.chrome.com/extensions/manifest/web_accessible_resources) status. - **Content Security Policy (CSP) analyzer and bypass checker**: This will point out weaknesses in your extension’s CSP and will also illuminate any potential ways to bypass your CSP due to whitelisted CDNs, etc. - **Known Vulnerable Libraries**: This uses [Retire.js](https://retirejs.github.io/retire.js/) to check for any usage of known-vulnerable JavaScript libraries. - Download extension and formatted versions. - Download the original extension. - Download a beautified version of the extension (auto prettified HTML and JavaScript). - Automatic caching of scan results, running an extension scan will take a good amount of time the first time you run it. However the second time, assuming the extension hasn’t been updated, will be almost instant due to the results being cached. - Linkable Report URLs, easily link someone else to an extension report generated by tarnish. ### [Neto](https://github.com/elevenpaths/neto) Project Neto is a Python 3 package conceived to analyse and unravel hidden features of browser plugins and extensions for well-known browsers such as Firefox and Chrome. It automates the process of unzipping the packaged files to extract these features from relevant resources in a extension like `manifest.json`, localization folders or Javascript and HTML source files. ## References - **Thanks to** [**@naivenom**](https://twitter.com/naivenom) **for the help with this methodology** - [https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing](https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing) - [https://palant.info/2022/08/10/anatomy-of-a-basic-extension/](https://palant.info/2022/08/10/anatomy-of-a-basic-extension/) - [https://palant.info/2022/08/24/attack-surface-of-extension-pages/](https://palant.info/2022/08/24/attack-surface-of-extension-pages/) - [https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/](https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/) - [https://help.passbolt.com/assets/files/PBL-02-report.pdf](https://help.passbolt.com/assets/files/PBL-02-report.pdf) - [https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts) - [https://developer.chrome.com/docs/extensions/mv2/background-pages](https://developer.chrome.com/docs/extensions/mv2/background-pages) - [https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/](https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/) - [https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0](https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0) {{#include ../../banners/hacktricks-training.md}}