Browser Extension Pentesting Methodology

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

Extension-Layouts sehen am besten aus, wenn sie visualisiert werden, und bestehen aus drei Komponenten. Schauen wir uns jede Komponente genauer an.

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

Content Scripts

Each content script hat direkten Zugriff auf das DOM einer einzelnen Webseite und ist damit potenziell bösartiger Eingabe ausgesetzt. Allerdings hat das content script keine anderen Berechtigungen außer der Möglichkeit, Nachrichten an das extension core zu senden.

Extension Core

Das extension core enthält die meisten der Extension-Privilegien/-Zugriffe, kann jedoch nur über XMLHttpRequest und content scripts mit Webinhalten interagieren. Außerdem hat das extension core keinen direkten Zugriff auf das Host-System.

Native Binary

Die Extension erlaubt ein native binary, das mit den vollständigen Rechten des Benutzers auf das Host-System zugreifen kann. Das native binary interagiert mit dem extension core über die standardmäßige Netscape Plugin Application Programming Interface (NPAPI), die auch von Flash und anderen Browser-Plugins verwendet wird.

Grenzen

Caution

Um die vollständigen Berechtigungen des Benutzers zu erhalten, muss ein Angreifer die Extension dazu bringen, bösartige Eingaben vom content script an das extension’s core und vom extension’s 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 für die meisten Betriebssystemdienste nicht verfügbar sind.

Darüber hinaus laufen content scripts getrennt von den zugehörigen Webseiten in einem getrennten JavaScript-Heap. Das content script und die Webseite haben Zugriff auf dasselbe zugrunde liegende DOM, aber die beiden tauschen niemals JavaScript pointers aus, wodurch das leaking von JavaScript-Funktionalität verhindert wird.

manifest.json

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

Beispiel:

{
"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 Ausdruck https://example.com/* entspricht und nicht der Regex *://*/*/business* entspricht. Sie werden wie die eigenen Skripte der Seite ausgeführt und haben uneingeschränkten Zugriff auf das Document Object Model (DOM) der Seite.

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

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

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

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 Erweiterungsseiten gesendet, wenn dieser Button geklickt wird, mittels der runtime.sendMessage() API. Das liegt an der Einschränkung des content scripts beim direkten Zugriff auf APIs, wobei storage eine der wenigen Ausnahmen ist. Für Funktionalitäten über diese Ausnahmen hinaus werden Nachrichten an Erweiterungsseiten 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 Liste der Fähigkeiten in der Chrome Developers documentation verfügbar, und für Firefox dient das MDN als primäre Quelle.
Es ist außerdem erwähnenswert, dass content scripts mit background scripts kommunizieren können, wodurch sie Aktionen ausführen und Antworten zurückgeben 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.

Sobald die Developer Tools angezeigt werden, ist auf den Source tab zu klicken, gefolgt vom Content Scripts Tab. Dadurch können laufende content scripts verschiedener Extensions beobachtet und Breakpoints gesetzt werden, um den Ausführungsfluss zu verfolgen.

Injektierte Content Scripts

Tip

Beachte, dass Content Scripts nicht obligatorisch sind, da es auch möglich ist, Skripte dynamisch zu injecten und sie programmgesteuert in Webseiten zu injizieren via tabs.executeScript. Das bietet tatsächlich eine feinere Kontrolle.

Für das programmgesteuerte Injizieren eines content scripts muss die Extension host permissions für die Seite haben, in die die Skripte injiziert werden sollen. Diese Berechtigungen können entweder durch Anforderung im Manifest der Extension gesichert werden oder auf temporärer Basis über activeTab.

Beispiel: auf activeTab basierende Extension

{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Inject a JS file bei Klick:
// 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"],
})
})
  • Inject a function bei Klick:
//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 Skriptberechtigungen

// 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 weitere URLs einzubeziehen oder auszuschließen, ist es auch möglich, include_globs und exclude_globs zu verwenden.

Content-Skripte run_at

Das Feld run_at steuert wann JavaScript-Dateien in die Webseite injiziert werden. Der bevorzugte und Standardwert ist "document_idle".

Die möglichen Werte sind:

  • document_idle: Wann immer möglich
  • document_start: Nach allen Dateien aus css, aber bevor irgendein anderer DOM aufgebaut oder ein anderes Script ausgeführt wird.
  • document_end: Unmittelbar nachdem der DOM vollständig ist, aber bevor Subressourcen wie Bilder und Frames geladen sind.

Über manifest.json

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

Über service-worker.js

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

background

Von content scripts gesendete Nachrichten werden von der background page empfangen, die eine zentrale Rolle bei der Koordination der Komponenten der Extension übernimmt. Bemerkenswert ist, dass die background page über die gesamte Lebensdauer der Extension hinweg besteht und unauffällig ohne direkte Nutzerinteraktion arbeitet. Sie verfügt über ihr eigenes Document Object Model (DOM), was komplexe Interaktionen und Zustandsverwaltung ermöglicht.

Key Points:

  • Background Page Role: Dient als Schaltzentrale der Extension und sorgt für Kommunikation und Koordination zwischen den einzelnen Teilen der Extension.
  • Persistence: Sie ist dauerhaft vorhanden, für den Nutzer unsichtbar, aber integraler Bestandteil der Funktionalität der Extension.
  • Automatic Generation: Wenn nicht explizit definiert, erstellt der browser automatisch eine background page. Diese automatisch erzeugte Seite enthält alle background scripts, die im Manifest der Extension angegeben sind, und stellt so den reibungslosen Ablauf der background tasks sicher.

Tip

Die Bequemlichkeit, die der browser durch das automatische Erzeugen einer background page (wenn sie nicht explizit deklariert ist) bietet, stellt sicher, dass alle notwendigen background scripts integriert und einsatzbereit sind und vereinfacht so den Setup-Prozess der Extension.

Example background script:

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

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

Um das background script zu debuggen, kannst du zu den Erweiterungsdetails gehen und den Service Worker inspizieren, dies öffnet die Developer Tools mit dem background script:

Optionsseiten und anderes

Browser extensions können verschiedene Arten von Seiten enthalten:

  • Action pages werden in einem Drop-down angezeigt, wenn auf das Erweiterungssymbol geklickt wird.
  • Seiten, die die Erweiterung in einem neuen Tab laden.
  • Option Pages: Diese Seite wird oben über der Erweiterung angezeigt, wenn sie angeklickt wird. In meinem Fall konnte ich auf diese Seite unter chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca zugreifen oder durch Klicken:

Beachte, dass diese Seiten im Gegensatz zu Hintergrundseiten nicht persistent sind, da sie Inhalte bei Bedarf dynamisch laden. Trotzdem teilen sie bestimmte Fähigkeiten mit der Hintergrundseite:

  • Communication with Content Scripts: Ähnlich wie die Hintergrundseite 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-spezifische APIs, vorbehaltlich der in der Erweiterung definierten Berechtigungen.

permissions & host_permissions

permissions und host_permissions sind Einträge in der manifest.json, die angeben, welche Berechtigungen die Browser-Erweiterung hat (storage, location…) und in welchen Webpages.

Da Browser-Erweiterungen so privilegiert sein können, könnte eine bösartige oder kompromittierte Erweiterung dem Angreifer verschiedene Möglichkeiten bieten, sensible Informationen zu stehlen und den Benutzer auszuspionieren.

Sieh dir an, 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 verwundbar sein.

Die Standardeinstellung für Seiten von Browser-Erweiterungen ist relativ restriktiv:

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

Für mehr Infos zu CSP und möglichen bypasses 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:

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

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

Allerdings kann diese id dynamisch sein, wenn der manifest.json-Parameter use_dynamic_url verwendet wird.

Tip

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

Der Zugriff auf diese Seiten kann sie potenziell anfällig für ClickJacking machen:

BrowExt - ClickJacking

Tip

Wenn diese Seiten nur von der extension und nicht von beliebigen URLs geladen werden dürfen, kann das ClickJacking-Angriffe verhindern.

Caution

Beachte, dass Seiten aus web_accessible_resources und andere Seiten der Extension ebenfalls in der Lage sind, background scripts zu kontaktieren. Wenn also eine dieser Seiten für XSS verwundbar ist, könnte das eine größere Schwachstelle eröffnen.

Außerdem kann man Seiten, die in web_accessible_resources angegeben sind, nur innerhalb von iframes öffnen, aber in einem neuen Tab ist es möglich, jede Seite der Extension zu erreichen, wenn man die Extension-ID kennt. Daher könnte ein gefundener XSS, der 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 web pages sich verbinden können mit deiner Extension über runtime.connect und runtime.sendMessage.

  • Wenn der Schlüssel externally_connectable nicht im Manifest deiner Extension deklariert ist oder als "ids": ["*"] angegeben ist, können alle extensions eine Verbindung herstellen, aber keine web pages.
  • Wenn spezifische IDs angegeben sind, wie in "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], können nur diese Anwendungen eine Verbindung herstellen.
  • Wenn matches angegeben sind, können sich diese Web-Apps verbinden:
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • If it’s specified as empty: "externally_connectable": {}, no app or web will be able to connect.

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

Caution

Wenn eine Webseite, die gegenüber XSS oder takeover verwundbar ist, 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 könnte, wenn der Client eine rogue extension installiert, selbst wenn diese nicht zur Kommunikation mit der verwundbaren extension berechtigt ist, sie XSS-Daten in einer erlaubten Webseite injizieren oder die APIs WebRequest oder DeclarativeNetRequest missbrauchen, um Requests auf einer Ziel-Domain zu manipulieren und die Anforderung einer Seite für eine JavaScript file zu verändern. (Beachte, dass CSP auf der Zielseite diese Angriffe verhindern könnte). Diese Idee stammt aus diesem Writeup.

Kommunikationszusammenfassung

Extension <–> WebApp

Um zwischen dem content script und der Webseite zu kommunizieren, werden üblicherweise Post Messages verwendet. Daher findet man in der Webanwendung meist 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 Webseite diese daher erwarten sollte) oder einfach die Webseite ein neues Script laden lässt.

Inside the extension

Üblicherweise wird die Funktion chrome.runtime.sendMessage verwendet, um eine Nachricht innerhalb der extension zu senden (üblicherweise vom background script verarbeitet), und um sie zu empfangen und zu verarbeiten, wird ein Listener mit chrome.runtime.onMessage.addListener deklariert.

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

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

</details>

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 die **ID des Tabs** angegeben werden muss, 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 wie folgt senden:
```javascript
chrome.runtime.sendMessage(extensionId, ...

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

Native Messaging

Es ist möglich, dass die Hintergrundskripte mit Binärdateien im System kommunizieren, die anfällig für kritische Schwachstellen wie RCEs sein können, wenn diese Kommunikation nicht ordnungsgemäß gesichert ist. More on this later.

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 laufen, und die, in denen Host-Seiten existieren, sind voneinander getrennt, was Isolation sicherstellt. Trotz dieser Isolation können beide mit dem gemeinsamen Document Object Model (DOM) der Seite interagieren. Damit die Host-Seite mit dem content script kommunizieren kann, oder indirekt mit der Erweiterung über das content script, muss sie das für beide zugängliche DOM als Kommunikationskanal nutzen.

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
)

Eine sichere Post Message-Kommunikation sollte die Authentizität der empfangenen Nachricht prüfen, das kann überprüft werden durch:

  • event.isTrusted: Dies ist True nur, wenn das Event durch eine Benutzeraktion ausgelöst wurde
  • Das Content Script könnte eine Nachricht nur erwarten, wenn der Benutzer eine Aktion ausführt
  • origin domain: könnte Nachrichten nur von einer Allowlist von Domains erwarten
  • Wenn ein regex verwendet wird, sei sehr vorsichtig
  • Quelle: received_message.source !== window kann verwendet werden, um zu prüfen, ob die Nachricht aus demselben Fenster stammt, in dem das Content Script lauscht.

Die vorherigen Prüfungen könnten, selbst wenn sie durchgeführt werden, verwundbar sein — prüfe daher auf der folgenden Seite mögliche Post Message-Bypässe:

PostMessage Vulnerabilities

Iframe

Eine weitere mögliche Kommunikationsmethode können Iframe URLs sein, ein Beispiel findest du in:

BrowExt - XSS Example

DOM

Das ist nicht “genau” eine Kommunikationsmethode, aber das web und das Content Script haben Zugriff auf das web DOM. Wenn das Content Script also Informationen daraus liest und dem web DOM vertraut, könnte das web diese Daten modifizieren (weil das web nicht vertraut werden sollte oder weil das web für XSS anfällig ist) und damit das Content Script kompromittieren.

Ein Beispiel für ein DOM-basiertes XSS zum Kompromittieren einer browser extension findest du ebenfalls in:

BrowExt - XSS Example

Content Script ↔︎ Background Script Kommunikation

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

Um die response zu verarbeiten, verwende das zurückgegebene Promise. Aus Gründen der Abwärtskompatibilität kannst du jedoch weiterhin einen callback als letztes Argument übergeben.

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

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

Senden einer Anfrage aus der extension (normalerweise aus einem background script). Beispiel, wie man eine Nachricht an das content script im ausgewählten Tab sendet:

// 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 Empfangsseite musst du einen runtime.onMessage Event-Listener einrichten, um die Nachricht zu verarbeiten. Das sieht vom content script oder extension page gleich aus.

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

In dem hervorgehobenen Beispiel wurde sendResponse() synchron ausgeführt. Um den onMessage-Event-Handler für eine asynchrone Ausführung von sendResponse() zu ändern, ist es zwingend erforderlich, return true; zu verwenden.

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

Beim Entwickeln neuer Extensions sollte die Präferenz auf promises statt callbacks liegen. Bei Verwendung von callbacks gilt die Funktion sendResponse() nur dann als gültig, wenn sie direkt im synchronen Kontext ausgeführt wird oder wenn der Event-Handler eine asynchrone Operation durch Zurückgeben von true anzeigt. Gibt keiner der Handler true zurück oder wird sendResponse() aus dem Speicher entfernt (garbage-collected), wird standardmäßig der Callback, der mit der Funktion sendMessage() verknüpft ist, ausgelöst.

Native Messaging

Browser extensions erlauben außerdem die Kommunikation mit binaries in the system via stdin. Die Anwendung muss dazu ein json installieren, das dies angibt, in einem json wie:

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

Wobei name die Zeichenkette ist, die 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 (use stdin and stdout), und die allowed_origins geben die Extensions an, die darauf zugreifen dürfen (sie dürfen kein Wildcard enthalten).

Chrome/Chromium sucht dieses json in bestimmten Windows-Registry-Einträgen und in einigen Pfaden auf 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 beispielhaft ein Hintergrundskript aus, das Nachrichten an eine native Anwendung sendet:

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

In this blog post, ein verwundbares Muster vorgeschlagen, das native messages missbraucht:

  1. Browser Extension hat ein Wildcard-Pattern für content script.
  2. content script leitet postMessage-Nachrichten an den background script weiter, indem es sendMessage verwendet.
  3. background script sendet die Nachricht an die native application mittels sendNativeMessage.
  4. native application verarbeitet die Nachricht unsicher, was zu code execution führt.

Und darin wird ein Beispiel erklärt, wie man von jeder Seite zu RCE gelangt, indem man eine browser extension ausnutzt.

Sensible Informationen in Memory/Code/Clipboard

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

Daher sollte der Speicher der Browser Extension nicht als sicher angesehen werden und sensible Informationen wie Zugangsdaten oder mnemonic phrases sollten nicht gespeichert werden.

Natürlich sollten keine sensiblen Informationen im code abgelegt werden, da dieser öffentlich sein wird.

Um den Speicher des Browsers zu dumpen, können Sie den Prozessspeicher auslesen oder in die Einstellungen der Browser Extension gehen, auf Inspect pop-up klicken -> Im Memory-Bereich -> Take a snaphost und CTRL+F drücken, um im Snapshot nach sensiblen Informationen zu suchen.

Außerdem sollten hochsensible Informationen wie mnemonic keys oder Passwörter nicht in die clipboard kopiert werden dürfen (oder zumindest nach ein paar Sekunden aus der clipboard entfernt werden), da sonst Prozesse, die die clipboard überwachen, darauf zugreifen können.

Loading an Extension in the Browser

  1. Herunterladen der Browser Extension und entpacken
  2. Gehen Sie zu chrome://extensions/ und den Developer Mode aktivieren
  3. Klicken Sie auf die Schaltfläche Load unpacked

In Firefox gehen Sie zu about:debugging#/runtime/this-firefox und klicken auf die Schaltfläche Load Temporary Add-on.

Getting the source code from the store

Der Quellcode einer Chrome extension kann auf verschiedenen Wegen beschafft werden. Nachfolgend sind detaillierte Erklärungen und Anweisungen für jede Option aufgeführt.

Download Extension as ZIP via Command Line

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

  1. Ersetzen Sie "extension_id" durch die tatsächliche ID der Extension.
  2. Führen Sie die folgenden Befehle aus:
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"

CRX Viewer-Website verwenden

https://robwu.nl/crxviewer/

CRX Viewer-Erweiterung verwenden

Eine weitere praktische Methode ist die Verwendung des Chrome Extension Source Viewer, einem 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. Greife auf dein lokales Chrome-Profilverzeichnis zu, indem du chrome://version/ aufrufst und das Feld “Profile Path” findest.
  2. Navigiere zum Unterordner Extensions/ im Profilverzeichnis.
  3. Dieser Ordner enthält alle installierten Erweiterungen, typischerweise mit ihrem Quellcode in lesbarer Form.

Um Erweiterungen zu identifizieren, kannst du ihre IDs den Namen zuordnen:

  • Aktiviere den Entwicklermodus auf der about:extensions-Seite, um die IDs jeder Erweiterung zu sehen.
  • Innerhalb des Ordners jeder Erweiterung enthält die Datei manifest.json ein lesbares name-Feld, das dir hilft, die Erweiterung zu identifizieren.

Dateiarchivprogramm oder Entpacker verwenden

Gehe zum Chrome Web Store und lade die Erweiterung herunter. Die Datei hat die Endung .crx. Ändere die Dateiendung von .crx zu .zip. Verwende ein beliebiges Archivprogramm (z. B. WinRAR, 7-Zip, etc.), um den Inhalt der ZIP-Datei zu entpacken.

Developer Mode in Chrome verwenden

Öffne Chrome und rufe chrome://extensions/ auf. Aktiviere oben rechts den “Developer mode”. Klicke auf “Load unpacked extension…”. Navigiere zu dem Verzeichnis deiner Erweiterung. Das lädt den Quellcode nicht herunter, ist aber nützlich, um den Code einer bereits heruntergeladenen oder entwickelten Erweiterung anzusehen und zu bearbeiten.

Chrome-Erweiterungs-Manifest-Datensatz

Um zu versuchen, verwundbare Browser-Erweiterungen zu finden, kannst du das https://github.com/palant/chrome-extension-manifests-dataset verwenden und deren manifest files auf potenziell verwundbare Hinweise prüfen. Zum Beispiel, um nach Erweiterungen mit mehr als 25000 Nutzern, content_scripts und der Berechtigung nativeMessaing zu suchen:

# 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 Technik, um Chromium zu backdooren, indem per-user Preferences direkt bearbeitet und gültige HMACs gefälscht werden, sodass der Browser eine beliebige unpacked extension ohne Aufforderungen oder Flags akzeptiert und aktiviert.

Forced Extension Load Preferences Mac Forgery Windows

Erkennung bösartiger Extension-Updates (statischer Versionsvergleich)

Supply-chain-Kompromittierungen treten häufig als bösartige Updates an zuvor harmlosen Extensions auf. Ein praktikabler, geräuscharmer Ansatz ist, ein neues Extension-Paket mit der zuletzt bekannten guten Version mittels statischer Analyse (zum Beispiel Assemblyline) zu vergleichen. Ziel ist, auf signalstarke Deltas statt auf jede Änderung zu alarmieren.

Workflow

  • Reiche beide Versionen ein (alt + neu) beim selben static-analysis-Profil.
  • Markiere neue oder aktualisierte background/service worker scripts (persistence + privileged logic).
  • Markiere neue oder aktualisierte content scripts (DOM-Zugriff und Datensammlung).
  • Markiere neu hinzugefügte permissions/host_permissions in manifest.json.
  • Markiere neue Domains, extrahiert aus dem Code (potentielle C2-/exfil-Endpunkte).
  • Markiere neue static-analysis-Detektionen (z. B. base64 decode, cookie harvesting, network-request builders, Obfuskationsmuster).
  • Markiere statistische Anomalien wie starke Entropiesprünge oder Ausreißer-z-Scores in geänderten Skripten.

Skriptänderungen genau erkennen

  • New script added → erkennen via manifest.json-Diff.
  • Existing script modified (manifest unchanged) → vergleiche per-file hashes aus dem extrahierten Dateibaum (z. B. Assemblyline Extract-Output). Das fängt stealthy Updates an bestehenden Workern oder Content Scripts ab.

Detektionen vor Veröffentlichung

Um „Easy-Mode“-Detektionen auf Basis bereits bekannter IOCs zu vermeiden, deaktiviere threat-intel-fed Services und verlasse dich auf intrinsische Signale (Domains, heuristische Signaturen, Script-Deltas, Entropie-Anomalien). Das erhöht die Chancen, bösartige Updates vor öffentlicher Meldung zu entdecken.

Beispiel für Alarmlogik mit hoher Trefferwahrscheinlichkeit

  • Low-noise combo: new domains + new static-analysis detections + updated background/service worker + updated or added content scripts.
  • Broader catch: new domain + new or updated background/service worker (höhere Recall, mehr Noise).

Wichtige Assemblyline-Services für diesen Workflow:

  • Extract: entpackt die Extension und liefert per-file hashes.
  • Characterize: berechnet Dateieigenschaften (z. B. Entropie).
  • JsJAWS / FrankenStrings / URLCreator: heben JS-Heuristiken, Strings und Domains hervor, um zwischen Versionen zu diffen.

Sicherheits-Audit-Checkliste

Auch wenn Browser Extensions eine begrenzte Angriffsfläche haben, können einige von ihnen Vulnerabilities oder Verbesserungspotenzial für Hardening enthalten. Die folgenden Punkte sind die häufigsten:

  • Begrenze so weit wie möglich angeforderte permissions
  • Begrenze so weit wie möglich host_permissions
  • Nutze eine starke content_security_policy
  • Begrenze so weit wie möglich das externally_connectable; wenn keins benötigt wird, lege standardmäßig {} fest
  • Wenn hier eine URL genannt ist, die anfällig für XSS oder zur Übernahme geeignet ist, kann ein Angreifer direkt Nachrichten an die background scripts senden. Sehr mächtiger Bypass.
  • Begrenze so weit wie möglich die web_accessible_resources, wenn möglich sogar leer.
  • Wenn 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 diese Kommunikation entstehen können (siehe XSS).
  • Wenn Post Messages verwendet werden, prüfe auf Post Message vulnerabilities.
  • Wenn das Content Script DOM-Details verwendet, prüfe, dass es keinen XSS einführt, falls die DOM-Inhalte durch die Webseite modifiziert werden.
  • Lege besonderes Augenmerk auf die Content Script -> Background script-Kommunikation.
  • Wenn der background script über native messaging kommuniziert, stelle sicher, dass die Kommunikation sicher und sanitized ist.
  • Sensitive information sollte nicht im Code der Browser Extension gespeichert werden.
  • Sensitive information sollte nicht im Speicher der Browser Extension gehalten werden.
  • Sensitive information sollte nicht ungeschützt im Dateisystem gespeichert werden.

Risiken von Browser Extensions

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

Tools

Tarnish

  • Lädt jede Chrome-Extension von einem angegebenen Chrome Webstore-Link herunter.
  • manifest.json Viewer: zeigt eine JSON-prettified Version des Manifests an.
  • Fingerprint Analysis: Detektion von web_accessible_resources und automatische Generierung von Chrome-Extension-Fingerprinting-JavaScript.
  • Potentielle Clickjacking-Analyse: Erkennung von Extension-HTML-Seiten mit der web_accessible_resources-Direktive. Diese können abhängig vom Zweck der Seiten potentiell für Clickjacking anfällig sein.
  • Permission Warning(s) viewer: zeigt eine Liste aller Chrome-Permission-Prompt-Warnungen, die beim Versuch, die Extension zu installieren, angezeigt werden.
  • Dangerous Function(s): zeigt die Stellen gefährlicher Funktionen, die potenziell von einem Angreifer ausgenutzt werden können (z. B. innerHTML, chrome.tabs.executeScript).
  • Entry Point(s): zeigt, wo die Extension Benutzereingaben/externe Eingaben annimmt. Nützlich, um die Angriffsfläche zu verstehen und mögliche Stellen zu finden, an die man bösartig gestaltete Daten senden kann.
  • Sowohl die Dangerous Function(s)- als auch die Entry Point(s)-Scanner liefern für ihre Alerts:
    • Relevanten Codeausschnitt und die Zeile, die den Alert verursacht hat.
    • Beschreibung des Problems.
    • Einen „View File“-Button, um die vollständige Quelldatei anzusehen.
    • Den Pfad der gemeldeten Datei.
    • Die vollständige Chrome-Extension-URI der gemeldeten Datei.
    • Den Dateityp, z. B. Background Page script, Content Script, Browser Action etc.
    • Wenn die verwundbare Zeile in einer JavaScript-Datei ist, die Pfade aller Seiten, in denen sie eingebunden ist, sowie den Typ dieser Seiten und den web_accessible_resource-Status.
  • Content Security Policy (CSP) analyzer and bypass checker: hebt Schwächen in der CSP der Extension hervor und zeigt mögliche Bypass-Wege aufgrund whitelisted CDNs etc.
  • Known Vulnerable Libraries: nutzt Retire.js zur Prüfung auf bekannte verwundbare JavaScript-Bibliotheken.
  • Download der Extension und formatierte Versionen.
  • Download der Original-Extension.
  • Download einer beautified Version der Extension (auto prettified HTML und JavaScript).
  • Automatisches Caching der Scan-Ergebnisse; ein Extension-Scan benötigt beim ersten Mal Zeit, beim zweiten Mal (sofern die Extension nicht aktualisiert wurde) ist das Ergebnis nahezu sofort verfügbar.
  • Linkbare Report-URLs, um anderen einfach einen Extension-Report von tarnish zu teilen.

Neto

Project Neto ist ein Python-3-Paket, entwickelt, 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 Dateien, um diese Features aus relevanten Ressourcen einer Extension wie manifest.json, Lokalisierungsordnern oder Javascript- und HTML-Quell-Dateien zu extrahieren.

Referenzen

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