Méthodologie de Pentesting des extensions de navigateur

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

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 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.

Composants principaux

La structure des extensions est plus claire lorsqu’elle est visualisée et se compose de trois composants. Examinons chaque composant en détail.

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

Content Scripts

Each content script has direct access to the DOM of a single web page and is thereby exposed to potentially malicious input. However, the content script contains no permissions other than the ability to send messages to 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 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 native binary qui peut accéder à la machine hôte avec les privilèges complets de l’utilisateur. Le native binary interagit avec l’extension core via l’interface standard Netscape Plugin Application Programming Interface (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 au core de l’extension et du core de l’extension au native binary.

Chaque composant de l’extension est séparé des autres par des barrières protectrices solides. Chaque composant s’exécute dans un processus distinct du système d’exploitation. Content scripts et extension cores s’exécutent dans des sandbox processes indisponibles pour 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 distinct. Le content script et la page web ont accès au même DOM sous-jacent, mais les deux n’échangent jamais de JavaScript pointers, ce qui empêche le leaking des fonctionnalités JavaScript.

manifest.json

Une extension Chrome n’est qu’un dossier ZIP avec une .crx file extension. Le cœur 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.

Example:

{
"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 scripts de la page elle-même et ont un accès arbitraire au Document Object Model (DOM) de la page.

"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.

Voici un exemple de content script qui ajoutera un bouton explain à la page et utilisera the storage API pour récupérer la valeur message du stockage de l’extension.

chrome.storage.local.get("message", (result) => {
let div = document.createElement("div")
div.innerHTML = result.message + " <button>Explain</button>"
div.querySelector("button").addEventListener("click", () => {
chrome.runtime.sendMessage("explain")
})
document.body.appendChild(div)
})

Un message est envoyé aux pages de l’extension par le script de contenu lorsque ce bouton est cliqué, via l’utilisation de l’runtime.sendMessage() API. Cela s’explique par la limitation des scripts de contenu en matière d’accès direct aux API, storage étant l’une des rares exceptions. Pour les fonctionnalités au-delà de ces exceptions, des messages sont envoyés aux pages de l’extension avec lesquelles les scripts de contenu peuvent communiquer.

Warning

Selon le navigateur, les capacités du script de contenu peuvent varier légèrement. Pour les navigateurs basés sur Chromium, la liste des capacités est disponible dans la Chrome Developers documentation, et pour Firefox, la MDN sert de source principale.
Il est également important de noter que les scripts de contenu peuvent communiquer avec les background scripts, leur permettant d’effectuer des actions et de renvoyer des réponses.

Pour afficher et déboguer les scripts de contenu dans Chrome, le menu des outils de développement Chrome peut être ouvert depuis Options > More tools > Developer tools ou en appuyant sur Ctrl + Shift + I.

Une fois les outils de développement affichés, cliquez sur l’onglet Source tab, puis sur l’onglet Content Scripts. Cela permet d’observer les scripts de contenu en cours d’exécution provenant de diverses extensions et de définir des points d’arrêt pour suivre le flux d’exécution.

Scripts de contenu injectés

Tip

Notez que Content Scripts aren’t mandatory car il est également possible d’injecter dynamiquement des scripts et de les injecter de façon programmatique dans les pages web via tabs.executeScript. Cela offre en réalité des contrôles plus granulaires.

Pour l’injection programmatique d’un script de contenu, l’extension doit disposer des host permissions pour la page dans laquelle les scripts doivent être injectés. Ces permissions peuvent être obtenues soit en les demandant dans le manifeste de l’extension, soit de façon temporaire via activeTab.

Example activeTab-based extension

{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Injecter un fichier JS au clic :
// 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:
//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 scripting

// 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".

Les valeurs possibles sont :

  • document_idle: Dès que possible
  • document_start: Après tout fichier de 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

{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}

Par service-worker.js

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 pendant toute 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 nerveux 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 browser 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 d’arrière-plan de l’extension.

Tip

La commodité fournie par le browser 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 background script:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "explain") {
chrome.tabs.create({ url: "https://example.net/explanation" })
}
})

Il utilise runtime.onMessage API pour écouter les messages. Lorsqu’un message "explain" est reçu, il utilise tabs API pour ouvrir une page dans un nouvel onglet.

Pour déboguer le background script vous pouvez aller aux détails de l’extension et inspecter le service worker, cela ouvrira les outils de développement avec le background script :

Pages d’options et autres

Les extensions de navigateur peuvent contenir différents types de pages :

  • Pages d’action sont affichées dans un menu déroulant lorsque l’icône de l’extension est cliquée.
  • Pages que l’extension ouvrira dans un nouvel onglet.
  • Pages d’options : Cette page s’affiche au-dessus de l’extension lorsqu’on clique. 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 background pages car elles chargent dynamiquement le contenu selon les besoins. Malgré cela, elles partagent certaines capacités avec la background page :

  • Communication with Content Scripts: Similaire à la background page, ces pages peuvent recevoir des messages 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 si 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.

Vérifiez comment ces paramètres fonctionnent et comment ils pourraient être abusés dans :

BrowExt - permissions & host_permissions

content_security_policy

Une content security policy peut aussi être déclarée dans le manifest.json. Si une telle politique est définie, elle pourrait être vulnérable.

La configuration par défaut pour les pages d’extension du navigateur est plutôt restrictive :

script-src 'self'; object-src 'self';

Pour plus d’infos sur CSP et d’éventuels bypasses, consultez :

Content Security Policy (CSP) Bypass

web_accessible_resources

Pour qu’une page web puisse accéder à une page d’une extension de navigateur, comme une page .html par exemple, cette page doit être mentionnée dans le champ web_accessible_resources du manifest.json.
Par exemple:

{
...
"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://<extension-id>/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 (section frame-ancestors) avant de confirmer qu’une attaque ClickJacking est possible.

Le fait de pouvoir accéder à ces pages les rend potentiellement vulnérables au ClickJacking :

BrowExt - ClickJacking

Tip

Autoriser le chargement de ces pages uniquement depuis l’extension et non depuis des URL arbitraires pourrait prévenir les attaques ClickJacking.

Caution

Notez que les pages listées dans web_accessible_resources et d’autres pages de l’extension peuvent aussi contacter les background scripts. Donc si l’une de ces pages est vulnérable à XSS, cela pourrait ouvrir une faille 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’ID de l’extension. Par conséquent, si une XSS est trouvée en abusant des mêmes paramètres, elle pourrait être exploitée même si la page n’est pas configurée dans web_accessible_resources.

externally_connectable

D’après les docs, la propriété de manifest "externally_connectable" déclare quelles extensions et quelles pages web peuvent se connecter à votre extension via runtime.connect et runtime.sendMessage.

  • Si la clé externally_connectable n’est pas déclarée dans le manifest de votre extension ou 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 spécifiés, comme dans "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], seules ces applications peuvent se connecter.
  • Si des matches sont spécifiés, ces web apps pourront se connecter :
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • Si c’est spécifié comme vide : "externally_connectable": {}, aucune application ou site web ne pourra se connecter.

Moins il y a d’extensions et d’URLs indiquées ici, plus petite sera l’attack surface.

Caution

Si une page web vulnérable à XSS ou 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 bypass très puissant.

De plus, si le client installe une rouge extension, même si elle n’est pas autorisée à communiquer avec l’extension vulnérable, elle pourrait injecter des données XSS dans une page web autorisée ou abuser des APIs WebRequest ou DeclarativeNetRequest pour manipuler les requêtes sur un domaine ciblé, en modifiant 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 ce writeup.

Résumé de la communication

Extension <–> WebApp

Pour communiquer entre le Content Script et la page web, des post messages sont généralement utilisés. Par conséquent, 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 pourrait aussi communiquer avec l’application web en envoyant un Post Message (et donc la page web devrait s’y attendre) ou simplement faire charger un nouveau script par la page.

Inside the extension

Généralement la fonction chrome.runtime.sendMessage est utilisée pour envoyer un message à l’intérieur de l’extension (généralement traité 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 uniques ; 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 })

</details>

Il est également possible d'envoyer des messages depuis un background script vers un content script situé dans un onglet spécifique en appelant **`chrome.tabs.sendMessage`**, où vous devrez indiquer l'**identifiant (ID) de l'onglet** auquel envoyer le message.

### Des entités autorisées via `externally_connectable` 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, ...

Quand il est nécessaire de mentionner l’extension ID.

Native Messaging

Il est possible que les scripts d’arrière-plan communiquent avec des binaires présents sur le système, ce qui peut entraîner des vulnérabilités critiques telles que des RCEs si cette communication n’est pas correctement sécurisée. More on this later.

chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)

Communication Web ↔︎ Content Script Communication

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 une 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 parties comme canal de communication.

Post Messages

// 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
)
document.getElementById("theButton").addEventListener(
"click",
() => {
window.postMessage(
{ type: "FROM_PAGE", text: "Hello from the webpage!" },
"*"
)
},
false
)

Une communication Post Message sécurisée doit 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: peut n’accepter un message que d’une allowlist de domaines.
  • Si un regex est utilisé, faites très attention
  • Source: received_message.source !== window peut être utilisé pour vérifier si le message provenait de la même fenêtre où le Content Script écoute.

Les vérifications précédentes, même si elles sont effectuées, peuvent être vulnérables, consultez donc la page suivante pour les potentiels Post Message bypasses :

PostMessage Vulnerabilities

Iframe

Une autre voie possible de communication peut se faire via des Iframe URLs, vous pouvez trouver un exemple dans :

BrowExt - XSS Example

DOM

Ce n’est pas “exactement” un moyen 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 doit pas être fiable, ou parce que le web est vulnérable à XSS) et compromettre le Content Script.

Vous pouvez aussi trouver un exemple d’un DOM based XSS to compromise a browser extension dans :

BrowExt - XSS Example

Content Script ↔︎ Background Script Communication

Un Content Script peut utiliser les fonctions runtime.sendMessage() ou tabs.sendMessage() pour envoyer un message one-time JSON-serializable.

Pour gérer la réponse, utilisez la Promise retournée. Toutefois, pour compatibilité ascendante, vous pouvez toujours passer un callback comme dernier argument.

Envoyer une requête depuis un Content Script ressemble à ceci :

;(async () => {
const response = await chrome.runtime.sendMessage({ greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()

Envoi d’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é :

// 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é récepteur, vous devez mettre en place un runtime.onMessage écouteur d’événements pour gérer le message. C’est identique depuis un content script ou une extension page.

// 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ée de manière synchrone. Pour modifier le gestionnaire d’événements onMessage afin d’exécuter sendResponse() de façon asynchrone, il est impératif d’incorporer return true;.

Une considération importante est que dans les scénarios où plusieurs pages sont configurées pour recevoir des événements onMessage, la première page à exécuter sendResponse() pour un événement spécifique sera la seule capable de délivrer la réponse efficacement. Toute réponse ultérieure au même événement ne sera pas prise en compte.

Lors de la création de nouvelles extensions, la préférence devrait aller aux promises plutôt qu’aux callbacks. En ce qui concerne l’utilisation des 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énements indique une opération asynchrone en retournant true. Si aucun des gestionnaires ne retourne true ou si la fonction sendResponse() est retirée de la mémoire (garbage-collected), le callback associé à sendMessage() sera déclenché par défaut.

Native Messaging

Browser extensions also allow to communicate with binaries in the system via stdin. The application must install a json indicating so in a json like:

{
"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/"]
}

name est la chaîne passée à runtime.connectNative() ou runtime.sendNativeMessage() pour communiquer avec l’application depuis les scripts d’arrière-plan de l’extension du navigateur. Le path est le chemin vers le binaire, il n’existe 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 wildcard).

Chrome/Chromium recherchera ce json dans certaines entrées du registre Windows et dans certains chemins sur macOS et Linux (plus d’infos dans les docs).

Tip

L’extension du navigateur a aussi besoin de la permission nativeMessaing déclarée afin de pouvoir utiliser cette communication.

Voici à quoi ressemble un exemple de code de script d’arrière-plan envoyant des messages à une application native :

chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)

In this blog post, un modèle vulnérable abusant des native messages est proposé :

  1. Browser Extension 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, menant à une exécution de code.

Et un exemple y explique comment passer de n’importe quelle page à RCE en abusant d’une Browser Extension.

Sensitive Information in Memory/Code/Clipboard

Si une Browser Extension stocke des informations sensibles dans sa mémoire, celles-ci peuvent être dumped (surtout sur les machines Windows) et recherchées pour ces informations.

Par conséquent, la mémoire de la Browser Extension ne doit pas être considérée comme sécurisée et les informations sensibles, comme des identifiants ou des phrases mnémoniques, ne doivent pas ê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 la Browser Extension, cliquer sur Inspect pop-up -> dans la section Memory -> Take a snaphost et CTRL+F pour rechercher dans le snapshot des informations sensibles.

De plus, les informations hautement sensibles comme les clés mnémoniques ou les mots de passe ne devraient pas pouvoir être copiées dans le clipboard (ou au moins les supprimer du clipboard après quelques secondes), car des processus surveillant le clipboard pourront alors les récupérer.

Loading an Extension in the Browser

  1. Téléchargez la Browser Extension et dézippez-la
  2. Allez sur chrome://extensions/ et activez le Developer Mode
  3. Cliquez sur le bouton Load unpacked

Dans Firefox, allez sur about:debugging#/runtime/this-firefox et cliquez sur le bouton Load Temporary Add-on.

Getting the source code from the store

Le code source d’une extension Chrome peut être obtenu par différentes méthodes. Ci-dessous se trouvent 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 :
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/

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. Le code source du viewer est disponible dans son GitHub repository.

Afficher la source d’une extension installée localement

Chrome extensions installées localement peuvent aussi être inspectées. Voici comment :

  1. Accédez à votre répertoire de profil local 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 archiver de fichiers ou un décompresseur

Allez sur le Chrome Web Store et téléchargez l’extension. Le fichier aura une extension .crx. Changez l’extension du fichier de .crx en .zip. Utilisez n’importe quel archiver (comme WinRAR, 7-Zip, etc.) pour extraire le contenu du fichier ZIP.

Utiliser le mode développeur 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 afficher et modifier le code d’une extension déjà téléchargée ou développée.

Jeu de données des manifest d’extensions Chrome

In order to try to spot vulnerable browser extensions you could use thehttps://github.com/palant/chrome-extension-manifests-dataset and check their manifest files for potentially vulnerable signs. For example to check for extensions with more than 25000 users, content_scripts and the permission nativeMessaing:

# 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)

Technique furtive pour backdoor Chromium en modifiant directement les Preferences par utilisateur et en forgeant des HMAC valides, poussant le navigateur à accepter et activer une extension unpacked arbitraire sans invites ni flags.

Forced Extension Load Preferences Mac Forgery Windows

Détection des mises à jour malveillantes d’extension (Diff statique de versions)

Les compromissions de la supply-chain arrivent souvent sous la forme de mises à jour malveillantes d’extensions auparavant bénignes. Une approche pratique et peu bruyante consiste à comparer un nouveau package d’extension avec la dernière version connue bonne en utilisant l’analyse statique (par exemple, Assemblyline). L’objectif est d’alerter sur des deltas à fort signal plutôt que sur n’importe quel changement.

Flux de travail

  • Soumettre les deux versions (ancienne + nouvelle) au même profil d’analyse statique.
  • Signaler les nouveaux ou mis à jour background/service worker scripts (persistence + logique privilégiée).
  • Signaler les nouveaux ou mis à jour content scripts (accès DOM et collecte de données).
  • Signaler les nouvelles permissions/host_permissions ajoutées dans manifest.json.
  • Signaler les nouveaux domaines extraits du code (endpoints potentiels de C2/exfil).
  • Signaler les nouvelles détections d’analyse statique (ex. décodage base64, récolte de cookies, constructeurs de requêtes réseau, motifs d’obfuscation).
  • Signaler les anomalies statistiques comme des sauts d’entropie marqués ou des z-scores outliers dans les scripts modifiés.

Détecter précisément les changements de scripts

  • Nouveau script ajouté → détecter via diff de manifest.json.
  • Script existant modifié (manifest inchangé) → comparer les hashes par fichier depuis l’arborescence extraite (ex. sortie Extract d’Assemblyline). Cela attrape les mises à jour furtives des workers ou des content scripts existants.

Détections avant divulgation publique

Pour éviter les détections « mode facile » basées sur des IOCs déjà connus, désactiver les services alimentés par threat-intel et se fier aux signaux intrinsèques (domaines, signatures heuristiques, deltas de scripts, anomalies d’entropie). Cela augmente les chances de détecter des mises à jour malveillantes avant leur signalement public.

Exemple de logique d’alerte à haute confiance

  • Combo faible bruit : nouveaux domaines + nouvelles détections d’analyse statique + background/service worker mis à jour + content scripts ajoutés ou mis à jour.
  • Couverture plus large : nouveau domaine + background/service worker nouveau ou mis à jour (rappel plus élevé, bruit plus important).

Services Assemblyline clés pour ce workflow :

  • Extract : déballe l’extension et produit des hachages par fichier.
  • Characterize : calcule des caractéristiques de fichier (ex. entropie).
  • JsJAWS / FrankenStrings / URLCreator : exposent des heuristiques JS, des chaînes et des domaines pour faire le diff entre versions.

Checklist d’audit de sécurité

Même si les extensions de navigateur ont une surface d’attaque limitée, certaines peuvent contenir des vulnérabilités ou des améliorations de durcissement possibles. Les points suivants sont les plus courants :

  • Limiter autant que possible les permissions demandées
  • Limiter autant que possible les host_permissions
  • Utiliser une content_security_policy forte
  • Limiter autant que possible le externally_connectable ; si aucun n’est nécessaire et possible, ne pas le laisser par défaut, spécifier {}
  • Si une URL vulnérable à XSS ou à takeover est mentionnée ici, un attaquant pourra envoyer des messages directement aux background scripts. Contournement très puissant.
  • Limiter autant que possible les web_accessible_resources, même vide si possible.
  • Si web_accessible_resources n’est pas vide, vérifier le ClickJacking
  • Si une communication se produit de l’extension vers la page web, vérifier les XSS introduites par la communication.
  • Si Post Messages sont utilisés, vérifier les Post Message vulnerabilities.
  • Si le Content Script accède au DOM, vérifier qu’il n’introduit pas de XSS si le DOM est modifié par la page web
  • Mettre un accent particulier si cette communication implique aussi la communication Content Script -> Background script
  • Si le background script communique via native messaging, vérifier que la communication est sécurisée et nettoyée
  • Les informations sensibles ne devraient pas être stockées dans le code de l’extension
  • Les informations sensibles ne devraient pas être stockées dans la mémoire de l’extension
  • Les informations sensibles ne devraient pas être stockées sur le système de fichiers sans protection

Risques liés aux extensions de navigateur

  • L’app https://crxaminer.tech/ analyse certaines données comme les permissions demandées par l’extension pour donner un niveau de risque lié à l’utilisation de l’extension.

Outils

Tarnish

  • Récupère n’importe quelle extension Chrome à partir d’un lien Chrome webstore fourni.
  • Visionneuse de manifest.json : affiche simplement une version JSON prettifiée du manifest de l’extension.
  • Analyse de fingerprint : détection de web_accessible_resources et génération automatique de JavaScript de fingerprinting pour extensions Chrome.
  • Analyse potentielle de Clickjacking : détection des pages HTML d’extension avec la directive web_accessible_resources activée. Ces pages peuvent être vulnérables au clickjacking selon leur finalité.
  • Visionneuse d’avertissements de permissions : montre la liste de tous les avertissements de permission Chrome qui seront affichés lorsqu’un utilisateur tentera d’installer l’extension.
  • Fonctions dangereuses : montre l’emplacement des fonctions dangereuses qui pourraient potentiellement être exploitées par un attaquant (ex. fonctions telles que innerHTML, chrome.tabs.executeScript).
  • Points d’entrée : montre où l’extension prend en entrée des données utilisateur/externe. Utile pour comprendre la surface d’une extension et chercher des points potentiels d’envoi de données malicieusement conçues.
  • Les scanners Dangerous Function(s) et Entry Point(s) fournissent pour chaque alerte générée :
    • Extrait de code pertinent et ligne causant l’alerte.
    • Description du problème.
    • Un bouton “View File” pour voir le fichier source complet contenant le code.
    • Le chemin du fichier alerté.
    • L’URI complet de l’extension Chrome du fichier alerté.
    • Le type de fichier (Background Page script, Content Script, Browser Action, etc.).
    • Si la ligne vulnérable est dans un fichier JavaScript, les chemins de toutes les pages où il est inclus ainsi que le type de ces pages et le statut web_accessible_resource.
  • Analyseur CSP et vérificateur de contournement : pointe les faiblesses dans la CSP de votre extension et illumine les moyens potentiels de la contourner à cause de CDNs whitelistés, etc.
  • Bibliothèques connues vulnérables : utilise Retire.js pour vérifier l’utilisation de bibliothèques JavaScript connues vulnérables.
  • Télécharger l’extension et des versions formatées.
  • Télécharger l’extension originale.
  • Télécharger une version beautified de l’extension (HTML et JavaScript automatiquement prettifiés).
  • Mise en cache automatique des résultats d’analyse : une première analyse prendra du temps, mais la seconde sera presque instantanée si l’extension n’a pas été mise à jour.
  • URLs de rapports partageables, permettant de lier facilement quelqu’un à un rapport généré par tarnish.

Neto

Le projet Neto est un paquet Python 3 conçu pour analyser et dénouer les fonctionnalités cachées des plugins et extensions de navigateurs connus tels que Firefox et Chrome. Il automatise le processus de décompression des fichiers packagés pour extraire ces fonctionnalités à partir de ressources pertinentes dans une extension comme manifest.json, les dossiers de localisation ou les fichiers source Javascript et HTML.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks