Pentesting-Methodologie für Browser-Extensions

Reading time: 28 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

Browser-Extensions werden in JavaScript geschrieben und vom Browser im Hintergrund geladen. Sie haben ihr DOM, können aber mit den DOMs anderer Sites interagieren. Das bedeutet, dass sie die Vertraulichkeit, Integrität und Verfügbarkeit (CIA) anderer Sites beeinträchtigen können.

Hauptkomponenten

Die Architektur einer Extension lässt sich am besten visualisieren und besteht aus drei Komponenten. Schauen wir uns jede Komponente genauer an.

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

Content Scripts

Jedes Content Script hat direkten Zugriff auf das DOM einer einzelnen Webseite und ist damit potenziell bösartigen Eingaben ausgesetzt. Das Content Script besitzt jedoch keine Berechtigungen außer der Möglichkeit, Nachrichten an den extension core zu senden.

Extension Core

Der extension core enthält die meisten Privilegien/Zugriffsrechte der Extension, kann jedoch nur über XMLHttpRequest und Content Scripts mit Web-Content interagieren. Außerdem hat der extension core keinen direkten Zugriff auf das Host-System.

Native Binary

Die Extension erlaubt ein natives Binary, das mit den vollen Benutzerprivilegien auf das Host-System zugreifen kann. Das native Binary interagiert mit dem extension core durch die standardmäßige Netscape Plugin Application Programming Interface (NPAPI), die von Flash und anderen Browser-Plug-ins verwendet wird.

Boundaries

caution

Um die vollen Benutzerprivilegien zu erlangen, muss ein Angreifer die Extension dazu bringen, bösartige Eingaben vom Content Script an den extension core und vom extension core an das native Binary weiterzuleiten.

Jede Komponente der Extension ist durch starke Schutzgrenzen voneinander getrennt. Jede Komponente läuft in einem separaten Betriebssystemprozess. Content Scripts und extension cores laufen in Sandbox-Prozessen, die den meisten Betriebssystemdiensten nicht zur Verfügung stehen.

Außerdem sind Content Scripts von ihren zugehörigen Webseiten getrennt, indem sie in einem separaten JavaScript-Heap laufen. Das Content Script und die Webseite haben Zugriff auf dasselbe zugrundeliegende DOM, tauschen jedoch niemals JavaScript-Pointer aus, wodurch das leaking von JavaScript-Funktionalität verhindert wird.

manifest.json

Eine Chrome-Extension ist im Grunde ein ZIP-Ordner mit einer .crx file extension. Der Kern der Extension ist die Datei manifest.json im Root des Ordners, die Layout, Berechtigungen und andere Konfigurationsoptionen festlegt.

Beispiel:

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

Content scripts werden geladen, wann immer der Benutzer zu einer passenden Seite navigiert, in unserem Fall jede Seite, die dem https://example.com/* Ausdruck entspricht und nicht dem *://*/*/business* regex. Sie werden wie die eigenen Skripte der Seite ausgeführt und haben uneingeschränkten Zugriff auf das Seiten-Document Object Model (DOM).

json
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],

Um weitere URLs einzuschließen oder auszuschließen, ist es außerdem möglich, include_globs und exclude_globs zu verwenden.

Dies ist ein Beispiel-Content-Script, das der Seite einen Explain-Button hinzufügt, wenn the storage API verwendet wird, um den message-Wert aus dem Speicher der Extension abzurufen.

js
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)
})

Eine Nachricht wird vom content script an die extension pages gesendet, wenn dieser Button angeklickt wird — und zwar über die Verwendung der runtime.sendMessage() API. Dies liegt an der Einschränkung, dass content scripts nicht direkt auf alle APIs zugreifen können; storage gehört zu den wenigen Ausnahmen. Für Funktionalitäten, die über diese Ausnahmen hinausgehen, werden Nachrichten an extension pages geschickt, mit denen content scripts kommunizieren können.

warning

Je nach Browser können die Fähigkeiten des content scripts leicht variieren. Für Chromium-basierte Browser ist die Auflistung der Fähigkeiten in der Chrome Developers documentation verfügbar, und für Firefox dient die MDN als Hauptquelle.
Es ist außerdem erwähnenswert, dass content scripts mit background scripts kommunizieren können, wodurch sie Aktionen ausführen und Antworten zurückmelden können.

Zum Anzeigen und Debuggen von content scripts in Chrome kann das Chrome developer tools-Menü über Options > More tools > Developer tools aufgerufen werden ODER durch Drücken von Ctrl + Shift + I.

Wenn die Developer Tools angezeigt werden, klickt man auf den Source-Tab, gefolgt vom Content Scripts-Tab. So können laufende content scripts verschiedener Extensions beobachtet und Breakpoints gesetzt werden, um den Ausführungsfluss nachzuverfolgen.

Injizierte Content Scripts

tip

Beachte, dass Content Scripts nicht zwingend erforderlich sind, da Skripte auch dynamisch bzw. programmgesteuert per tabs.executeScript in Webseiten injiziert werden können. Das bietet tatsächlich feinere Kontrollen.

Für die programmgesteuerte Injektion eines content scripts muss die Extension host permissions für die Seite besitzen, in die die Skripte injiziert werden sollen. Diese Berechtigungen können entweder durch Anforderung im Manifest der Extension gesichert werden oder vorübergehend über activeTab.

Beispiel: activeTab-basierte Extension

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • JS-Datei bei Klick injizieren:
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"],
})
})
  • Funktion injizieren bei Klick:
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,
})
})

Beispiel mit scripting permissions

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" })

Um mehr URLs einzuschließen oder auszuschließen, ist es außerdem möglich, include_globs und exclude_globs zu verwenden.

Content Scripts run_at

Das Feld run_at steuert wann JavaScript-Dateien in die Webseite injiziert werden. Der bevorzugte und standardmäßige Wert ist "document_idle".

Die möglichen Werte sind:

  • document_idle: Wann immer möglich
  • document_start: Nach allen Dateien aus css, aber bevor das DOM aufgebaut wird oder andere Skripte ausgeführt werden.
  • document_end: Unmittelbar nachdem das DOM vollständig ist, jedoch bevor Subressourcen wie Bilder und Frames geladen sind.

Via manifest.json

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

Über service-worker.js

javascript
chrome.scripting.registerContentScripts([
{
id: "test",
matches: ["https://*.example.com/*"],
runAt: "document_idle",
js: ["contentScript.js"],
},
])

background

Nachrichten, die von Content-Skripten gesendet werden, werden von der Hintergrundseite empfangen, die eine zentrale Rolle bei der Koordination der Komponenten der Erweiterung spielt. Bemerkenswert ist, dass die Hintergrundseite über die gesamte Lebensdauer der Erweiterung hinweg bestehen bleibt und unauffällig ohne direkte Benutzerinteraktion arbeitet. Sie verfügt über ihr eigenes Document Object Model (DOM), was komplexe Interaktionen und Zustandsverwaltung ermöglicht.

Wichtige Punkte:

  • Rolle der Hintergrundseite: Fungiert als Schaltzentrale für die Erweiterung und stellt die Kommunikation und Koordination zwischen den verschiedenen Teilen der Erweiterung sicher.
  • Persistenz: Sie ist eine stets vorhandene Entität, für den Benutzer unsichtbar, aber integraler Bestandteil der Funktionalität der Erweiterung.
  • Automatische Erstellung: Falls nicht explizit definiert, erstellt der Browser automatisch eine Hintergrundseite. Diese automatisch erzeugte Seite wird alle im Manifest der Erweiterung angegebenen Hintergrundskripte enthalten und so den nahtlosen Betrieb der Hintergrundaufgaben der Erweiterung sicherstellen.

tip

Die Bequemlichkeit, die der Browser durch die automatische Erstellung einer Hintergrundseite (wenn diese nicht explizit deklariert ist) bietet, stellt sicher, dass alle notwendigen Hintergrundskripte integriert und einsatzbereit sind und vereinfacht so den Einrichtungsprozess der Erweiterung.

Beispiel für ein Hintergrundskript:

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

Es verwendet die runtime.onMessage API, um Nachrichten zu lauschen. Wenn eine "explain"-Nachricht empfangen wird, nutzt es die tabs API, um eine Seite in einem neuen Tab zu öffnen.

Um das background script zu debuggen, kannst du zu den extension details and inspect the service worker, gehen; das öffnet die developer tools mit dem background script:

Options pages and other

Browser extensions können verschiedene Arten von Seiten enthalten:

  • Action pages are displayed in a drop-down when the extension icon is clicked.
  • Seiten, die die Extension load in a new tab.
  • Option Pages: Diese Seite wird oben auf der Extension angezeigt, wenn sie angeklickt wird. In meinem vorherigen manifest konnte ich diese Seite unter chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca erreichen oder durch Klicken:

Beachte, dass diese Seiten nicht persistent wie background pages sind, da sie Inhalte dynamisch nach Bedarf laden. Trotz dessen teilen sie bestimmte Fähigkeiten mit der background page:

  • Communication with Content Scripts: Ähnlich zur background page können diese Seiten Nachrichten von content scripts empfangen und so die Interaktion innerhalb der Extension ermöglichen.
  • Access to Extension-Specific APIs: Diese Seiten haben umfassenden Zugriff auf extension-specific APIs, vorbehaltlich der für die Extension definierten Berechtigungen.

permissions & host_permissions

permissions und host_permissions sind Einträge in der manifest.json, die angeben, welche permissions die browser extensions hat (storage, location...) und in welchen web pages.

Da browser extensions so privileged sein können, könnte eine bösartige oder kompromittierte Extension einem Angreifer different means to steal sensitive information and spy on the user ermöglichen.

Prüfe, wie diese Einstellungen funktionieren und wie sie missbraucht werden könnten in:

BrowExt - permissions & host_permissions

content_security_policy

Eine content security policy kann ebenfalls in der manifest.json deklariert werden. Wenn eine definiert ist, könnte sie vulnerable sein.

Die Standardkonfiguration für browser extension pages ist relativ restriktiv:

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

Für weitere Informationen zu CSP und möglichen Bypässen siehe:

Content Security Policy (CSP) Bypass

web_accessible_resources

Damit eine Webseite auf eine Seite einer Browser-Erweiterung zugreifen kann, z. B. eine .html-Seite, muss diese Seite im Feld web_accessible_resources der manifest.json aufgeführt sein.
Zum Beispiel:

javascript
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}

Diese Seiten sind unter URLs wie folgt erreichbar:

chrome-extension://<extension-id>/message.html

In öffentlichen Extensions ist die extension-id zugänglich:

Wenn jedoch der manifest.json-Parameter use_dynamic_url verwendet wird, kann diese ID dynamisch sein.

tip

Beachte, dass selbst wenn hier eine Seite erwähnt wird, sie dank der Content Security Policy gegen ClickJacking geschützt sein kann. Du musst also auch diese (frame-ancestors Abschnitt) prüfen, bevor du bestätigst, dass ein ClickJacking-Angriff möglich ist.

Der erlaubte Zugriff auf diese Seiten macht sie potenziell für ClickJacking anfällig:

BrowExt - ClickJacking

tip

Das Zulassen, dass diese Seiten nur von der Extension und nicht von beliebigen URLs geladen werden, kann ClickJacking-Angriffe verhindern.

caution

Beachte, dass die Seiten aus web_accessible_resources und andere Seiten der Extension ebenfalls in der Lage sind, contacting background scripts. Wenn also eine dieser Seiten für XSS verwundbar ist, kann dies eine größere Schwachstelle eröffnen.

Außerdem kannst du nur Seiten, die in web_accessible_resources angegeben sind, innerhalb von iframes öffnen; in einem neuen Tab ist es jedoch möglich, jede Seite der Extension aufzurufen, wenn man die Extension-ID kennt. Daher könnte ein XSS, das dieselben Parameter ausnutzt, missbraucht werden, selbst wenn die Seite nicht in web_accessible_resources konfiguriert ist.

externally_connectable

Laut den docs deklariert die Manifest-Eigenschaft "externally_connectable", welche Extensions und Webseiten sich mit deiner Extension via runtime.connect und runtime.sendMessage verbinden können.

  • Wenn der externally_connectable-Schlüssel nicht im Manifest deiner Extension deklariert ist oder er als "ids": ["*"] angegeben ist, können alle Extensions eine Verbindung herstellen, aber keine Webseiten.
  • Wenn konkrete IDs angegeben sind, wie in "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], können nur diese Anwendungen eine Verbindung herstellen.
  • Wenn matches angegeben sind, können diese Web-Apps eine Verbindung herstellen:
json
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • Wenn es als leer angegeben ist: "externally_connectable": {}, wird keine App oder Website eine Verbindung herstellen können.

Je weniger extensions und URLs hier angegeben sind, desto kleiner die Angriffsfläche.

caution

Wenn eine Webseite vulnerable to XSS or takeover in externally_connectable angegeben ist, kann ein Angreifer Nachrichten direkt an das background script senden, wodurch das Content Script und dessen CSP vollständig umgangen werden.

Daher ist dies ein sehr mächtiger bypass.

Außerdem, wenn der Client eine bösartige Extension installiert, selbst wenn sie nicht erlaubt ist, mit der verwundbaren Extension zu kommunizieren, könnte sie XSS data in an allowed web page injizieren oder die WebRequest- bzw. DeclarativeNetRequest-APIs missbrauchen, um Anfragen für eine zielgerichtete Domain zu manipulieren und so die Anforderung einer Seite für eine JavaScript file zu verändern. (Beachte, dass CSP auf der zielgerichteten Seite diese Angriffe verhindern könnte). Diese Idee stammt from this writeup.

Kommunikationsübersicht

Extension <--> WebApp

Um zwischen dem Content Script und der Webseite zu kommunizieren, werden üblicherweise post messages verwendet. Daher findet man in der Webanwendung normalerweise Aufrufe der Funktion window.postMessage und im Content Script Listener wie window.addEventListener. Beachte jedoch, dass die Extension auch mit der Webanwendung kommunizieren kann, indem sie eine Post Message sendet (und die Webanwendung dies daher erwarten sollte) oder einfach die Seite ein neues Script laden lässt.

Innerhalb der Extension

Normalerweise wird die Funktion chrome.runtime.sendMessage verwendet, um eine Nachricht innerhalb der Extension zu senden (meist vom background script verarbeitet). Um sie zu empfangen und zu verarbeiten, wird ein Listener deklariert, der chrome.runtime.onMessage.addListener aufruft.

Es ist auch möglich, chrome.runtime.connect() zu verwenden, um eine persistente Verbindung statt einzelner Nachrichten zu haben; damit kann man send und receive messages wie im folgenden Beispiel verwenden:

chrome.runtime.connect() Beispiel
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
})

Es ist auch möglich, Nachrichten von einem Hintergrundskript an ein Content-Skript in einem bestimmten Tab zu senden, indem man chrome.tabs.sendMessage aufruft, wobei Sie die ID des Tabs angeben müssen, an den die Nachricht gesendet werden soll.

Von erlaubten externally_connectable zur Erweiterung

Web-Apps und externe Browser-Erweiterungen, die in der externally_connectable-Konfiguration erlaubt sind, können Anfragen senden using :

javascript
chrome.runtime.sendMessage(extensionId, ...

Wo es nötig ist, die extension ID zu erwähnen.

Native Messaging

Es ist möglich, dass background scripts mit binaries im System kommunizieren, die anfällig für kritische Sicherheitslücken wie RCEs sein können, wenn diese Kommunikation nicht richtig abgesichert ist. Mehr dazu später.

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

Web ↔︎ Content Script Kommunikation

Die Umgebungen, in denen content scripts ausgeführt werden, und die Host-Seiten sind voneinander separiert, wodurch Isolation gewährleistet wird. Trotz dieser Isolation können beide mit dem Document Object Model (DOM) der Seite interagieren, einer gemeinsamen Ressource. Damit die Host-Seite mit dem content script kommunizieren kann oder indirekt mit der extension über das content script, muss sie das für beide zugängliche DOM als Kommunikationskanal verwenden.

Post Messages

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

Eine sichere Post Message-Kommunikation sollte die Authentizität der empfangenen Nachricht prüfen; das kann durch folgende Checks erfolgen:

  • event.isTrusted: Dies ist True nur, wenn das Event durch eine Benutzeraktion ausgelöst wurde
  • Das Content Script könnte nur dann Nachrichten erwarten, wenn der Nutzer eine Aktion ausführt
  • origin domain: könnte so konfiguriert sein, dass nur eine Allowlist von Domains Nachrichten akzeptiert
  • Wenn ein Regex verwendet wird, sei sehr vorsichtig
  • Source: received_message.source !== window kann verwendet werden, um zu prüfen, ob die Nachricht from the same window stammt, in dem das Content Script lauscht.

Die vorherigen Checks können, selbst wenn sie durchgeführt werden, verwundbar sein — siehe auf der folgenden Seite potential Post Message bypasses:

PostMessage Vulnerabilities

Iframe

Eine weitere mögliche Kommunikationsart kann über Iframe URLs erfolgen; ein Beispiel dazu findest du in:

BrowExt - XSS Example

DOM

Das ist nicht „genau“ eine Kommunikationsart, aber web und das Content Script haben Zugriff auf den web DOM. Wenn das content script also Informationen daraus liest und trusting the web DOM, könnte das web diese modify this data (weil dem web nicht vertraut werden sollte oder weil das web für XSS verwundbar ist) und damit compromise the Content Script.

Ein Beispiel für eine DOM based XSS to compromise a browser extension findest du ebenfalls in:

BrowExt - XSS Example

Content Script ↔︎ Background Script Communication

Ein Content Script kann die Funktionen runtime.sendMessage() oder tabs.sendMessage() verwenden, um eine one-time JSON-serializable Nachricht zu senden.

Um die response zu verarbeiten, nutze das zurückgegebene Promise. Zur Abwärtskompatibilität kannst du jedoch weiterhin einen callback als letztes Argument übergeben.

Das Senden einer Anfrage aus einem content script sieht so aus:

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

Senden einer Anfrage von der extension (in der Regel ein background script). Beispiel, wie man eine Nachricht an das content script im ausgewählten tab sendet:

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)
})()

Auf der Empfängerseite müssen Sie einen runtime.onMessage event listener einrichten, um die Nachricht zu verarbeiten. Das sieht sowohl im content script als auch in einer extension page gleich aus.

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" })
})

Im hervorgehobenen Beispiel wurde sendResponse() synchron ausgeführt. Um den onMessage-Event-Handler für eine asynchrone Ausführung von sendResponse() anzupassen, ist es zwingend erforderlich, return true; einzufügen.

Eine wichtige Überlegung ist, dass in Szenarien, in denen mehrere Seiten onMessage-Events empfangen sollen, die erste Seite, die sendResponse() für ein bestimmtes Event ausführt, die einzige ist, die die Antwort effektiv liefern kann. Alle nachfolgenden Antworten auf dasselbe Event werden nicht berücksichtigt.

Beim Erstellen neuer Extensions sollte Promises gegenüber callbacks bevorzugt werden. Bei Verwendung von callbacks ist die Funktion sendResponse() nur dann gültig, wenn sie direkt im synchronen Kontext ausgeführt wird oder wenn der Event-Handler durch Rückgabe von true eine asynchrone Operation signalisiert. Wenn keiner der Handler true zurückgibt oder wenn die sendResponse()-Funktion aus dem Speicher entfernt (garbage-collected) wird, wird standardmäßig der Callback von sendMessage() ausgelöst.

Native Messaging

Browser extensions ermöglichen außerdem die Kommunikation mit binaries im System über stdin. Die Anwendung muss eine json installieren, die dies angibt — beispielsweise eine json wie:

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

Dabei ist name der String, der an runtime.connectNative() oder runtime.sendNativeMessage() übergeben wird, um von den Hintergrundskripten der Browser-Erweiterung mit der Anwendung zu kommunizieren. Der path ist der Pfad zur Binary, es gibt nur einen gültigen type, nämlich stdio (Verwendung von stdin und stdout), und die allowed_origins geben die Erweiterungen an, die darauf zugreifen können (Wildcard ist nicht erlaubt).

Chrome/Chromium sucht dieses json in bestimmten Windows-Registry-Einträgen und in einigen Pfaden unter macOS und Linux (mehr Infos in den docs).

tip

Die Browser-Erweiterung benötigt außerdem die deklarierte Berechtigung nativeMessaing, um diese Kommunikation nutzen zu können.

So sieht ein Beispiel für Code eines Hintergrundskripts aus, das Nachrichten an eine native Anwendung sendet:

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

In this blog post, wird ein verwundbares Muster zum Missbrauch von native messages vorgeschlagen:

  1. Browser extension hat ein Wildcard-Pattern für das content script.
  2. Content script leitet postMessage-Nachrichten an das background script weiter, indem es sendMessage verwendet.
  3. Background script leitet die Nachricht an die native application weiter mittels sendNativeMessage.
  4. Native application verarbeitet die Nachricht unsicher, was zu code execution führen kann.

Darin wird ein Beispiel erklärt, wie man von jeder Seite zu RCE gelangen kann, indem man eine Browser extension ausnutzt.

Sensitive Information in Memory/Code/Clipboard

Wenn eine Browser Extension sensible Informationen im Arbeitsspeicher speichert, könnten diese (insbesondere auf Windows-Maschinen) gedumpt und nach diesen Informationen durchsucht werden.

Deshalb sollte der Arbeitsspeicher der Browser Extension nicht als sicher angesehen werden und sensible Informationen wie Credentials oder mnemonic phrases sollten nicht gespeichert werden.

Lege natürlich keine sensiblen Informationen in den Code, da dieser öffentlich sein wird.

Um den Speicher des Browsers zu dumpen, kannst du entweder den Prozessspeicher dumpen oder in die Einstellungen der Browser Extension gehen, auf Inspect pop-up klicken -> im Memory Bereich -> Take a snaphost und mit CTRL+F innerhalb des Snapshots nach sensiblen Informationen suchen.

Außerdem sollte es nicht erlaubt sein, hochsensible Informationen wie mnemonic keys oder Passwörter in die clipboard zu kopieren (oder sie zumindest nach ein paar Sekunden wieder daraus zu entfernen), da Prozesse, die die clipboard überwachen, sonst darauf zugreifen können.

Loading an Extension in the Browser

  1. Herunterladen der Browser Extension & entpacken
  2. Gehe zu chrome://extensions/ und aktiviere den Developer Mode
  3. Klicke auf den Button Load unpacked

In Firefox gehst du zu about:debugging#/runtime/this-firefox und klickst auf den Button Load Temporary Add-on.

Getting the source code from the store

Der Quellcode einer Chrome-Erweiterung kann auf verschiedene Weisen beschafft werden. Im Folgenden findest du detaillierte Erklärungen und Anleitungen für jede Option.

Download Extension as ZIP via Command Line

Der Quellcode einer Chrome-Erweiterung kann per Kommandozeile als ZIP-Datei heruntergeladen werden. Dazu wird curl verwendet, um die ZIP-Datei von einer bestimmten URL zu holen und anschließend den Inhalt der ZIP-Datei in ein Verzeichnis zu entpacken. Hier sind die Schritte:

  1. Ersetze "extension_id" durch die tatsächliche ID der Erweiterung.
  2. Führe die folgenden Befehle aus:
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"

Die CRX Viewer-Website verwenden

https://robwu.nl/crxviewer/

Die CRX Viewer-Erweiterung verwenden

Eine weitere bequeme Methode ist die Verwendung des Chrome Extension Source Viewer, ein Open-Source-Projekt. Es kann aus dem Chrome Web Store installiert werden. Der Quellcode des Viewers ist in seinem GitHub repository verfügbar.

Quellcode einer lokal installierten Erweiterung anzeigen

Lokal installierte Chrome-Erweiterungen können ebenfalls inspiziert werden. So geht's:

  1. Rufen Sie Ihr lokales Chrome-Profilverzeichnis auf, indem Sie chrome://version/ öffnen und das Feld "Profile Path" finden.
  2. Navigieren Sie zum Unterordner Extensions/ im Profilverzeichnis.
  3. Dieser Ordner enthält alle installierten Erweiterungen, typischerweise mit ihrem Quellcode in lesbarem Format.

Um Erweiterungen zu identifizieren, können Sie deren IDs den Namen zuordnen:

  • Aktivieren Sie Developer Mode auf der about:extensions-Seite, um die IDs jeder Erweiterung zu sehen.
  • Innerhalb jedes Erweiterungsordners enthält die manifest.json-Datei ein lesbares name-Feld, das bei der Identifikation hilft.

Einen Datei-Archivierer oder Unpacker verwenden

Gehen Sie zum Chrome Web Store und laden Sie die Erweiterung herunter. Die Datei hat die Erweiterung .crx. Ändern Sie die Dateiendung von .crx zu .zip. Verwenden Sie einen beliebigen Datei-Archivierer (wie WinRAR, 7-Zip, etc.), um den Inhalt der ZIP-Datei zu extrahieren.

Developer Mode in Chrome verwenden

Öffnen Sie Chrome und gehen Sie zu chrome://extensions/. Aktivieren Sie "Developer mode" oben rechts. Klicken Sie auf "Load unpacked extension...". Navigieren Sie zum Verzeichnis Ihrer Erweiterung. Dadurch wird der Quellcode nicht heruntergeladen, aber es ist nützlich, um den Code einer bereits heruntergeladenen oder entwickelten Erweiterung anzusehen und zu modifizieren.

Chrome extension manifest dataset

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:

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.

Forced Extension Load Preferences Mac Forgery Windows

Security Audit Checklist

Auch wenn Browser Extensions eine begrenzte Angriffsfläche haben, können einige von ihnen Vulnerabilities oder mögliche Härtungsverbesserungen enthalten. Die folgenden Punkte sind die häufigsten:

  • Beschränke so weit wie möglich die angeforderten permissions
  • Beschränke so weit wie möglich host_permissions
  • Verwende eine starke content_security_policy
  • Beschränke so weit wie möglich das externally_connectable; wenn keines benötigt wird, lasse es nicht standardmäßig offen, setze {}
  • Wenn hier eine URL vulnerable to XSS or to takeover erwähnt wird, kann ein Angreifer directly send messages to the background scripts. Sehr mächtiger Bypass.
  • Beschränke so weit wie möglich die web_accessible_resources, idealerweise leer wenn möglich.
  • Falls web_accessible_resources nicht leer ist, prüfe auf ClickJacking
  • Wenn irgendeine Kommunikation von der Extension zur Webseite erfolgt, prüfe auf XSS Vulnerabilities, die durch die Kommunikation verursacht werden könnten.
  • Wenn Post Messages verwendet werden, prüfe auf Post Message vulnerabilities.
  • Wenn das Content Script auf DOM-Details zugreift, stelle sicher, dass dadurch keine XSS eingeführt werden, falls diese vom Web modifiziert werden
  • Lege besonderen Wert auf diese Kommunikation, wenn sie in der Content Script -> Background script communication involviert ist
  • Wenn das background script über native messaging kommuniziert, prüfe, dass die Kommunikation sicher ist und Eingaben sanitisiert werden
  • 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

  • Die App https://crxaminer.tech/ analysiert einige Daten wie die angeforderten permissions einer Browser Extension, um ein Risikoniveau für die Nutzung der Browser Extension anzugeben.

Tools

Tarnish

  • Lädt jede Chrome extension von einem angegebenen Chrome webstore link herunter.
  • manifest.json viewer: zeigt einfach eine JSON-prettified Version des Manifests der Extension an.
  • Fingerprint Analysis: Erkennung von web_accessible_resources und automatische Generierung von Chrome extension fingerprinting JavaScript.
  • Potential Clickjacking Analysis: Erkennung von Extension-HTML-Seiten mit der web_accessible_resources-Direktive. Diese können je nach Zweck der Seiten potentiell für Clickjacking anfällig sein.
  • Permission Warning(s) viewer: zeigt eine Liste aller Chrome permission prompt warnings, die angezeigt werden, wenn ein Benutzer versucht, die Extension zu installieren.
  • Dangerous Function(s): zeigt die Position potenziell gefährlicher Funktionen, die von einem Angreifer ausgenutzt werden könnten (z. B. Funktionen wie innerHTML, chrome.tabs.executeScript).
  • Entry Point(s): zeigt, wo die Extension Benutzer-/externe Eingaben entgegennimmt. Nützlich, um die Angriffsfläche einer Extension zu verstehen und potenzielle Stellen zu finden, um bösartig gestaltete Daten an die Extension zu senden.
  • Sowohl die Dangerous Function(s)- als auch die Entry Point(s)-Scanner liefern für ihre generierten Alerts Folgendes:
    • Relevanter Code-Snippet und die Zeile, die den Alert ausgelöst hat.
    • Beschreibung des Problems.
    • Einen „View File“-Button, um die komplette Quelldatei mit dem Code anzusehen.
    • Den Pfad der betroffenen Datei.
    • Die vollständige Chrome extension URI der betroffenen Datei.
    • Den Dateityp, z. B. Background Page script, Content Script, Browser Action usw.
    • Falls die verwundbare Zeile in einer JavaScript-Datei ist, die Pfade aller Seiten, in denen sie eingebunden ist, sowie den Typ dieser Seiten und ihren web_accessible_resource-Status.
  • Content Security Policy (CSP) analyzer and bypass checker: Hebt Schwächen in der CSP deiner Extension hervor und zeigt mögliche Umgehungen der CSP durch erlaubte CDNs usw. auf.
  • Known Vulnerable Libraries: Verwendet Retire.js zur Überprüfung auf bekannte verwundbare JavaScript-Bibliotheken.
  • Download der Extension und formatierte Versionen.
  • Download der Original-Extension.
  • Download einer beautified Version der Extension (automatisch prettified HTML und JavaScript).
  • Automatisches Caching der Scan-Ergebnisse; der erste Scan einer Extension kann einige Zeit dauern. Beim zweiten Scan (sofern die Extension nicht aktualisiert wurde) ist das Ergebnis fast sofort verfügbar, da es gecached wurde.
  • Linkbare Report-URLs, um jemandem einfach einen Link zu einem von tarnish erstellten Extension-Report zu geben.

Neto

Project Neto ist ein Python 3 Paket, das entwickelt wurde, um versteckte Features von Browser plugins und extensions für bekannte Browser wie Firefox und Chrome zu analysieren und aufzudecken. Es automatisiert das Entpacken der gepackten Dateien, um relevante Ressourcen aus einer Extension wie manifest.json, Lokalisierungsordnern oder Javascript- und HTML-Quellfiles zu extrahieren.

References

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks