Browser Extension Pentesting Methodology

Reading time: 28 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di base

Le browser extension sono scritte in JavaScript e caricate dal browser in background. Hanno il loro DOM ma possono interagire con i DOM di altri siti. Questo significa che possono compromettere la riservatezza, l'integrità e la disponibilità (CIA) di altri siti.

Componenti principali

I layout delle extension si visualizzano meglio e consistono di tre componenti. Vediamo ogni componente in dettaglio.

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

Content Scripts

Each content script ha accesso diretto al DOM di una single web page e è quindi esposto a potentially malicious input. Tuttavia, il content script non contiene permessi oltre alla possibilità di inviare messaggi all'extension core.

Extension Core

L'extension core contiene la maggior parte dei privilegi/accessi dell'estensione, ma l'extension core può interagire con il contenuto web solo tramite XMLHttpRequest e content scripts. Inoltre, l'extension core non ha accesso diretto alla macchina host.

Native Binary

L'estensione può includere un native binary che può accedere alla macchina host con i pieni privilegi dell'utente. Il native binary interagisce con l'extension core tramite la Netscape Plugin Application Programming Interface (NPAPI) usata da Flash e altri plugin del browser.

Confini

caution

Per ottenere i pieni privilegi dell'utente, un attacker deve convincere l'estensione a passare malicious input dal content script all'extension core e dall'extension core al native binary.

Ogni componente dell'estensione è separato dagli altri da forti confini protettivi. Ogni componente gira in un processo del sistema operativo separato. Content scripts e extension cores girano in sandbox processes non disponibili alla maggior parte dei servizi del sistema operativo.

Inoltre, i content scripts sono separati dalle pagine web associate eseguendo in un separate JavaScript heap. Il content script e la web page hanno accesso allo stesso underlying DOM, ma i due never exchange JavaScript pointers, prevenendo il leaking of JavaScript functionality.

manifest.json

Una Chrome extension è semplicemente una cartella ZIP con un .crx file extension. Il core dell'estensione è il file manifest.json alla radice della cartella, che specifica layout, permissions e altre opzioni di configurazione.

Esempio:

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

Gli script di contenuto vengono caricati ogni volta che l'utente naviga verso una pagina corrispondente, nel nostro caso qualsiasi pagina che corrisponde all'espressione https://example.com/* e che non corrisponde alla regex *://*/*/business*. Vengono eseguiti come gli script della pagina stessa e hanno accesso arbitrario al Document Object Model (DOM) della pagina.

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

Per includere o escludere ulteriori URL è anche possibile usare include_globs e exclude_globs.

Questo è un esempio di content script che aggiungerà un pulsante 'explain' alla pagina quando the storage API viene utilizzata per recuperare il valore message dallo storage dell'estensione.

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

Un messaggio viene inviato alle pagine dell'estensione dal content script quando questo pulsante viene cliccato, tramite l'utilizzo della runtime.sendMessage() API. Ciò è dovuto alla limitazione del content script nell'accesso diretto alle API, con storage che rappresenta una delle poche eccezioni. Per funzionalità oltre queste eccezioni, i messaggi vengono inviati alle pagine dell'estensione con cui i content script possono comunicare.

warning

A seconda del browser, le capacità del content script possono variare leggermente. Per i browser basati su Chromium, la lista delle capacità è disponibile nella Chrome Developers documentation, e per Firefox, la MDN funge da fonte principale.
È inoltre importante notare che i content script hanno la capacità di comunicare con i background scripts, permettendo loro di eseguire azioni e rimandare risposte.

Per visualizzare e fare debugging dei content script in Chrome, il menu dei Chrome developer tools può essere aperto da Options > More tools > Developer tools O premendo Ctrl + Shift + I.

Una volta visualizzati i developer tools, cliccare la Source tab, seguita dalla Content Scripts tab. Questo permette di osservare i content script in esecuzione provenienti da varie estensioni e impostare breakpoint per tracciare il flusso di esecuzione.

Injected content scripts

tip

Nota che Content Scripts aren't mandatory in quanto è anche possibile dynamically inject script e programatically inject them nelle pagine web tramite tabs.executeScript. Questo fornisce in realtà controlli più granular.

Per l'iniezione programmatica di un content script, l'estensione deve avere host permissions per la pagina in cui gli script devono essere iniettati. Questi permessi possono essere ottenuti o richiedendoli nel manifest dell'estensione o temporaneamente tramite activeTab.

Example activeTab-based extension

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Iniettare un file JS al clic:
javascript
// content-script.js
document.body.style.backgroundColor = "orange"

//service-worker.js - Inject the JS file
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"],
})
})
  • Iniettare una function al clic:
javascript
//service-worker.js - Inject a function
function injectedFunction() {
document.body.style.backgroundColor = "orange"
}

chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: injectedFunction,
})
})

Esempio con permessi di scripting

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

Per includere o escludere più URL, è anche possibile usare include_globs e exclude_globs.

Script di contenuto run_at

Il campo run_at controlla quando i file JavaScript vengono iniettati nella pagina web. Il valore preferito e di default è "document_idle".

I possibili valori sono:

  • document_idle: Ogni volta che è possibile
  • document_start: Dopo i file css, ma prima che il DOM venga costruito o che venga eseguito qualsiasi altro script.
  • document_end: Immediatamente dopo il completamento del DOM, ma prima che sottorisorse come immagini e frame siano caricate.

Tramite manifest.json

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

Via service-worker.js

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

background

I messaggi inviati dai content scripts sono ricevuti dalla background page, che svolge un ruolo centrale nel coordinare i componenti dell'estensione. In particolare, la background page persiste per tutta la durata dell'estensione, operando in modo discreto senza interazione diretta dell'utente. Possiede il proprio Document Object Model (DOM), permettendo interazioni complesse e la gestione dello stato.

Punti chiave:

  • Ruolo della background page: Agisce come centro nevralgico per l'estensione, garantendo comunicazione e coordinamento tra le varie parti dell'estensione.
  • Persistenza: È un'entità sempre presente, invisibile all'utente ma fondamentale per la funzionalità dell'estensione.
  • Generazione automatica: Se non definita esplicitamente, il browser creerà automaticamente una background page. Questa pagina auto-generata includerà tutti gli background scripts specificati nel manifest dell'estensione, assicurando il corretto funzionamento delle attività di background dell'estensione.

tip

La comodità fornita dal browser nel generare automaticamente una background page (quando non dichiarata esplicitamente) garantisce che tutti gli background scripts necessari siano integrati e operativi, semplificando il processo di configurazione dell'estensione.

Esempio di background script:

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

Utilizza runtime.onMessage API per ascoltare i messaggi. Quando viene ricevuto un messaggio "explain", utilizza tabs API per aprire una pagina in una nuova scheda.

Per fare il debug del background script puoi aprire i dettagli dell'estensione e ispezionare il service worker, questo aprirà i developer tools con lo script di background:

Pagine Options e altro

Le estensioni del browser possono contenere diversi tipi di pagine:

  • Action pages vengono visualizzate in un menu a discesa quando l'icona dell'estensione viene cliccata.
  • Pagine che l'estensione caricherà in una nuova scheda.
  • Option Pages: Questa pagina viene mostrata sopra l'estensione quando cliccata. Nel manifest precedente, nel mio caso sono riuscito ad accedere a questa pagina in chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca o cliccando:

Nota che queste pagine non sono persistenti come le background pages poiché caricano dinamicamente il contenuto quando necessario. Nonostante ciò, condividono alcune capacità con la background page:

  • Communication with Content Scripts: Come la background page, queste pagine possono ricevere messaggi dai content scripts, facilitando l'interazione all'interno dell'estensione.
  • Access to Extension-Specific APIs: Queste pagine hanno accesso completo alle API specifiche dell'estensione, soggette ai permessi definiti per l'estensione.

permissions & host_permissions

permissions e host_permissions sono voci del manifest.json che indicano quali permessi l'estensione del browser possiede (storage, location...) e in quali pagine web.

Poiché le estensioni del browser possono essere così privilegiate, un'estensione malevola o compromessa potrebbe permettere all'attaccante diversi mezzi per rubare informazioni sensibili e spiare l'utente.

Controlla come queste impostazioni funzionano e come potrebbero essere abusate in:

BrowExt - permissions & host_permissions

content_security_policy

Una content security policy può essere dichiarata anche all'interno del manifest.json. Se è definita, potrebbe essere vulnerabile.

L'impostazione predefinita per le pagine delle estensioni del browser è piuttosto restrittiva:

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

Per maggiori informazioni su CSP e sui potential bypasses consulta:

Content Security Policy (CSP) Bypass

web_accessible_resources

Affinché una pagina web possa accedere a una pagina di un'estensione del browser, ad esempio una .html, questa pagina deve essere menzionata nel campo web_accessible_resources del manifest.json.
Per esempio:

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

Queste pagine sono accessibili tramite URL come:

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

Nelle estensioni pubbliche l'extension-id è accessibile:

Tuttavia, se nel manifest.json è usato il parametro use_dynamic_url, questo id può essere dinamico.

tip

Nota che anche se una pagina è menzionata qui, potrebbe essere protetta contro ClickJacking grazie alla Content Security Policy. Quindi devi anche verificarla (sezione frame-ancestors) prima di confermare che un attacco ClickJacking sia possibile.

Avere il permesso di accedere a queste pagine le rende potenzialmente vulnerabili a ClickJacking:

BrowExt - ClickJacking

tip

Consentire che queste pagine siano caricate solo dall'extension e non da URL casuali potrebbe prevenire attacchi ClickJacking.

caution

Nota che le pagine definite in web_accessible_resources e altre pagine dell'extension sono anche in grado di contattare background scripts. Quindi se una di queste pagine è vulnerabile a XSS potrebbe aprire una vulnerabilità più ampia.

Inoltre, nota che puoi aprire dentro iframe solo le pagine indicate in web_accessible_resources, ma da una nuova tab è possibile accedere a qualsiasi pagina dell'extension conoscendo l'extension ID. Pertanto, se viene trovato un XSS che abusa degli stessi parametri, potrebbe essere sfruttato anche se la pagina non è configurata in web_accessible_resources.

externally_connectable

Secondo la docs, la proprietà di manifest "externally_connectable" dichiara quali extensions e web pages possono connettersi alla tua extension via runtime.connect e runtime.sendMessage.

  • Se la chiave externally_connectable non è dichiarata nel manifest della tua extension o è dichiarata come "ids": ["*"], tutte le extensions possono connettersi, ma nessuna web page può connettersi.
  • Se vengono specificati ID specifici, come in "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], solo quelle applicazioni possono connettersi.
  • Se sono specificati dei matches, quelle web app saranno in grado di connettersi:
json
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • Se è specificato vuoto: "externally_connectable": {}, nessuna app o web potrà connettersi.

La minore quantità di extension e URL indicate qui, tanto più piccola sarà la superficie di attacco.

caution

Se una pagina web vulnerabile a XSS o takeover è indicata in externally_connectable, un attacker sarà in grado di inviare messaggi direttamente allo script di background, bypassando completamente il Content Script e il suo CSP.

Pertanto, questo è un bypass molto potente.

Inoltre, se il client installa una rogue extension, anche se non è autorizzata a comunicare con l'estensione vulnerabile, potrebbe injectare dati XSS in una pagina web consentita o abusare delle API WebRequest o DeclarativeNetRequest per manipolare le richieste su un dominio target alterando la richiesta di una JavaScript file. (Nota che il CSP sulla pagina target potrebbe prevenire questi attacchi). Questa idea proviene from this writeup.

Riepilogo della comunicazione

Estensione <--> WebApp

Per comunicare tra il Content Script e la pagina web vengono solitamente usati i post message. Pertanto, nell'applicazione web di solito troverai chiamate alla funzione window.postMessage e, nel Content Script, listener come window.addEventListener. Nota tuttavia che l'estensione potrebbe anche comunicare con l'applicazione web inviando un Post Message (e quindi la pagina web dovrebbe aspettarselo) oppure semplicemente far caricare alla pagina un nuovo script.

All'interno dell'estensione

Di solito la funzione chrome.runtime.sendMessage viene usata per inviare un messaggio all'interno dell'estensione (solitamente gestito dallo script background) e, per riceverlo e gestirlo, viene dichiarato un listener chiamando chrome.runtime.onMessage.addListener.

È anche possibile usare chrome.runtime.connect() per avere una connessione persistente invece di inviare singoli messaggi; può essere usata per inviare e ricevere messaggi come nell'esempio seguente:

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

È anche possibile inviare messaggi da un background script a un content script situato in una specifica scheda chiamando chrome.tabs.sendMessage dove dovrai indicare l'ID della scheda a cui inviare il messaggio.

Da externally_connectable consentiti verso l'estensione

Web apps e external browser extensions consentite nella configurazione externally_connectable possono inviare richieste usando :

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

Quando è necessario menzionare la extension ID.

Native Messaging

È possibile che i background scripts comunichino con binari presenti nel sistema, operazione che potrebbe essere soggetta a vulnerabilità critiche come RCEs se tale comunicazione non è adeguatamente protetta. More on this later.

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

Web ↔︎ Comunicazione con Content Script

Gli ambienti in cui operano i content scripts e quelli delle pagine host sono separati l'uno dall'altro, garantendo isolamento. Nonostante questo isolamento, entrambi possono interagire con il Document Object Model (DOM) della pagina, una risorsa condivisa. Perché la pagina host possa comunicare con il content script, o indirettamente con l'estensione tramite il content script, è necessario utilizzare il DOM accessibile a entrambe le parti come canale di comunicazione.

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
)

Una comunicazione Post Message sicura dovrebbe verificare l'autenticità del messaggio ricevuto, questo può essere fatto controllando:

  • event.isTrusted: Questo è True solo se l'evento è stato innescato da un'azione dell'utente
  • Il content script potrebbe aspettarsi un messaggio solo se l'utente esegue qualche azione
  • origin domain: potrebbe aspettarsi un messaggio solo da una allowlist di domini.
  • Se viene usata una regex, fai molta attenzione
  • Source: received_message.source !== window può essere usato per verificare se il messaggio proveniva dalla stessa window in cui il Content Script sta ascoltando.

I controlli precedenti, anche se eseguiti, potrebbero essere vulnerabili, quindi consulta nella pagina seguente i potenziali bypass di Post Message:

PostMessage Vulnerabilities

Iframe

Un altro possibile canale di comunicazione potrebbe essere tramite Iframe URLs, puoi trovare un esempio in:

BrowExt - XSS Example

DOM

Questo non è "esattamente" un metodo di comunicazione, ma il web e il content script avranno accesso al web DOM. Quindi, se il content script legge informazioni da esso, fidandosi del web DOM, il web potrebbe modificare questi dati (perché il web non dovrebbe essere considerato affidabile, o perché il web è vulnerabile a XSS) e compromettere il Content Script.

Puoi anche trovare un esempio di una DOM based XSS per compromettere un browser extension in:

BrowExt - XSS Example

Content Script ↔︎ Background Script Comunicazione

Un Content Script può usare le funzioni runtime.sendMessage() or tabs.sendMessage() per inviare un messaggio one-time JSON-serializable.

Per gestire la response, usa la Promise restituita. Tuttavia, per compatibilità retroattiva, puoi ancora passare una callback come ultimo argomento.

L'invio di una request da un content script appare così:

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

Invio di una richiesta dall'extension (di solito un background script). Esempio di come inviare un messaggio al content script nella tab selezionata:

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

Dal lato ricevente, devi impostare un runtime.onMessage event listener per gestire il messaggio. Questo è identico sia da un content script sia da una extension page.

javascript
// From https://stackoverflow.com/questions/70406787/javascript-send-message-from-content-js-to-background-js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(
sender.tab
? "from a content script:" + sender.tab.url
: "from the extension"
)
if (request.greeting === "hello") sendResponse({ farewell: "goodbye" })
})

Nell'esempio evidenziato, sendResponse() è stato eseguito in modo sincrono. Per modificare l'handler dell'evento onMessage affinché sendResponse() venga eseguito in modo asincrono, è imperativo includere return true;.

Un'importante considerazione è che, in scenari in cui più pagine sono predisposte a ricevere eventi onMessage, la prima pagina a eseguire sendResponse() per uno specifico evento sarà l'unica in grado di fornire la risposta efficacemente. Qualsiasi risposta successiva allo stesso evento non verrà presa in considerazione.

Quando si sviluppano nuove estensioni, la preferenza dovrebbe essere per le promises anziché per i callbacks. Per quanto riguarda l'uso dei callbacks, la funzione sendResponse() è considerata valida solo se viene eseguita direttamente nel contesto sincrono, oppure se l'handler dell'evento indica un'operazione asincrona restituendo true. Se nessuno degli handler restituisce true oppure se la funzione sendResponse() viene rimossa dalla memoria (garbage-collected), il callback associato a sendMessage() verrà attivato di default.

Native Messaging

Le estensioni del browser permettono anche di comunicare con binari nel sistema via stdin. L'applicazione deve installare un json che lo indichi in un json come:

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

Dove il name è la stringa passata a runtime.connectNative() o runtime.sendNativeMessage() per comunicare con l'applicazione dagli script di background dell'estensione del browser. Il path è il percorso al binario, c'è solo 1 type valido che è stdio (use stdin and stdout) e gli allowed_origins indicano le estensioni che possono accedervi (e non possono avere wildcard).

Chrome/Chromium cercherà questo json in alcune voci del registro di Windows e in alcuni percorsi su macOS e Linux (maggiori info nella docs).

tip

L'estensione del browser deve dichiarare anche il permesso nativeMessaing per poter utilizzare questa comunicazione.

Ecco come appare il codice di uno script di background che invia messaggi a un'applicazione nativa:

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

In this blog post, viene proposto uno schema vulnerabile che sfrutta native messages:

  1. Browser extension ha un pattern wildcard per il content script.
  2. Content script passa messaggi postMessage allo background script usando sendMessage.
  3. Background script inoltra il messaggio all'applicazione nativa usando sendNativeMessage.
  4. Native application gestisce il messaggio in modo pericoloso, portando a code execution.

Al suo interno è spiegato un esempio di come passare da qualsiasi pagina a RCE abusando di un'estensione del browser.

Informazioni sensibili in memoria/codice/clipboard

Se un'estensione del browser memorizza informazioni sensibili nella sua memoria, queste potrebbero essere dumped (soprattutto su macchine Windows) e cercate per trovare tali informazioni.

Pertanto, la memoria dell'estensione del browser non dovrebbe essere considerata sicura e informazioni sensibili come credenziali o frasi mnemoniche non dovrebbero essere memorizzate.

Ovviamente, non inserire informazioni sensibili nel codice, poiché saranno pubbliche.

Per dumpare la memoria dal browser puoi dump the process memory oppure andare nelle impostazioni dell'estensione del browser, cliccare su Inspect pop-up -> nella sezione Memory -> Take a snaphost e usare CTRL+F per cercare all'interno dello snapshot informazioni sensibili.

Inoltre, informazioni altamente sensibili come chiavi mnemoniche o password non dovrebbero poter essere copiate negli appunti (clipboard) (o almeno rimosse dagli appunti dopo pochi secondi) perché processi che monitorano il clipboard potrebbero ottenerle.

Caricare un'estensione nel browser

  1. Scarica l'estensione del browser e decomprimi
  2. Vai a chrome://extensions/ e abilita la Developer Mode
  3. Clicca il pulsante Load unpacked

In Firefox vai a about:debugging#/runtime/this-firefox e clicca il pulsante Load Temporary Add-on.

Ottenere il codice sorgente dallo store

Il codice sorgente di un'estensione Chrome può essere ottenuto con vari metodi. Di seguito sono riportate spiegazioni dettagliate e istruzioni per ogni opzione.

Scaricare l'estensione come ZIP tramite linea di comando

Il codice sorgente di un'estensione Chrome può essere scaricato come file ZIP usando la linea di comando. Questo comporta l'uso di curl per recuperare il file ZIP da un URL specifico e quindi estrarre il contenuto del ZIP in una directory. Ecco i passaggi:

  1. Sostituisci "extension_id" con l'ID reale dell'estensione.
  2. Esegui i seguenti comandi:
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"

Usa il sito CRX Viewer

https://robwu.nl/crxviewer/

Usa l'estensione CRX Viewer

Un altro metodo comodo è usare Chrome Extension Source Viewer, che è un progetto open-source. Può essere installato dal Chrome Web Store. Il codice sorgente del viewer è disponibile nel suo GitHub repository.

Visualizza il sorgente di un'estensione installata localmente

Chrome extensions installate localmente possono anche essere ispezionate. Ecco come:

  1. Accedi alla directory del profilo locale di Chrome visitando chrome://version/ e individuando il campo "Profile Path".
  2. Naviga nella sottocartella Extensions/ all'interno della directory del profilo.
  3. Questa cartella contiene tutte le estensioni installate, tipicamente con il loro codice sorgente in un formato leggibile.

Per identificare le estensioni, puoi mappare i loro ID ai nomi:

  • Abilita Developer Mode nella pagina about:extensions per vedere gli ID di ciascuna estensione.
  • All'interno della cartella di ogni estensione, il file manifest.json contiene un campo name leggibile, che ti aiuta a identificare l'estensione.

Usa un archiviatore di file o un unpacker

Vai al Chrome Web Store e scarica l'estensione. Il file avrà estensione .crx. Cambia l'estensione del file da .crx a .zip. Usa qualsiasi archiviatore (come WinRAR, 7-Zip, ecc.) per estrarre il contenuto del file ZIP.

Usa Developer Mode in Chrome

Apri Chrome e vai su chrome://extensions/. Abilita "Developer mode" in alto a destra. Clicca su "Load unpacked extension...". Naviga nella directory della tua estensione. Questo non scarica il codice sorgente, ma è utile per visualizzare e modificare il codice di un'estensione già scaricata o sviluppata.

Dataset dei manifest delle estensioni Chrome

Per cercare di individuare estensioni del browser vulnerabili puoi usare il https://github.com/palant/chrome-extension-manifests-dataset e controllare i loro file manifest per possibili segnali di vulnerabilità. Ad esempio, per verificare estensioni con più di 25000 utenti, content_scripts e il permesso 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)

Tecnica stealth per backdoorare Chromium modificando direttamente le per-user Preferences e falsificando HMACs validi, facendo sì che il browser accetti e attivi un'estensione unpacked arbitraria senza prompt o flag.

Forced Extension Load Preferences Mac Forgery Windows

Lista di controllo per l'audit di sicurezza

Anche se le estensioni del browser hanno una superficie d'attacco limitata, alcune potrebbero contenere vulnerabilità o possibili miglioramenti di hardening. Quelle seguenti sono le più comuni:

  • Limitare il più possibile i permissions richiesti
  • Limitare il più possibile i host_permissions
  • Usare una content_security_policy forte
  • Limitare il più possibile il externally_connectable; se non è necessario e possibile, non lasciarlo per default, specificare {}
  • Se qui è menzionata una URL vulnerabile a XSS o a takeover, un attaccante potrà inviare messaggi direttamente ai background scripts. Bypass molto potente.
  • Limitare il più possibile i web_accessible_resources, anche lasciandoli vuoti se possibile.
  • Se i web_accessible_resources non sono vuoti, verificare la presenza di ClickJacking
  • Se avviene qualunque comunicazione dall'estensione alla pagina web, verificare la presenza di XSS vulnerabilità causate nella comunicazione.
  • Se vengono usati Post Messages, verificare le Post Message vulnerabilities.
  • Se i Content Script accedono ai dettagli del DOM, verificare che non stiano introducendo una XSS nel caso vengano modificati dalla pagina web
  • Porre particolare attenzione se questa comunicazione è coinvolta anche nella Content Script -> Background script communication
  • Se lo script di background comunica tramite native messaging, verificare che la comunicazione sia sicura e sanitizzata
  • Le informazioni sensibili non dovrebbero essere memorizzate all'interno del codice dell'estensione del browser
  • Le informazioni sensibili non dovrebbero essere memorizzate nella memoria dell'estensione del browser
  • Le informazioni sensibili non dovrebbero essere memorizzate nel file system senza protezione

Browser Extension Risks

  • L'app https://crxaminer.tech/ analizza alcuni dati come i permessi richiesti dall'estensione del browser per dare un livello di rischio nell'utilizzo dell'estensione.

Strumenti

Tarnish

  • Scarica qualsiasi estensione Chrome a partire da un link del Chrome Web Store fornito.
  • manifest.json viewer: mostra semplicemente una versione JSON-prettificata del manifest dell'estensione.
  • Fingerprint Analysis: rilevamento di web_accessible_resources e generazione automatica di JavaScript per il fingerprinting delle estensioni Chrome.
  • Potential Clickjacking Analysis: rilevamento di pagine HTML dell'estensione con la direttiva web_accessible_resources impostata. Queste possono essere potenzialmente vulnerabili a clickjacking a seconda dello scopo delle pagine.
  • Permission Warning(s) viewer: mostra una lista di tutti i warning dei permission prompt di Chrome che verranno visualizzati quando l'utente tenta di installare l'estensione.
  • Dangerous Function(s): mostra la posizione di funzioni pericolose che potrebbero essere sfruttate da un attaccante (es. funzioni come innerHTML, chrome.tabs.executeScript).
  • Entry Point(s): mostra i punti in cui l'estensione riceve input dall'utente/esterni. Questo è utile per comprendere la superficie d'attacco dell'estensione e cercare punti potenziali per inviare dati creati in modo malevolo all'estensione.
  • Sia gli scanner Dangerous Function(s) che Entry Point(s) forniscono per gli alert generati quanto segue:
    • Snippet di codice rilevante e riga che ha causato l'alert.
    • Descrizione del problema.
    • Un pulsante “View File” per visualizzare il file sorgente completo che contiene il codice.
    • Il path del file segnalato.
    • L'URI completo dell'estensione Chrome del file segnalato.
    • Il tipo di file, ad esempio Background Page script, Content Script, Browser Action, ecc.
    • Se la riga vulnerabile è in un file JavaScript, vengono mostrati i path di tutte le pagine in cui è incluso, oltre al tipo di queste pagine e lo stato di web_accessible_resource.
  • Content Security Policy (CSP) analyzer and bypass checker: individua le debolezze nella CSP dell'estensione e mette in luce eventuali modi per bypassare la CSP dovuti a CDN nella whitelist, ecc.
  • Known Vulnerable Libraries: utilizza Retire.js per controllare l'uso di librerie JavaScript note vulnerabili.
  • Scarica l'estensione e versioni formattate.
  • Scarica l'estensione originale.
  • Scarica una versione beautified dell'estensione (HTML e JavaScript auto-prettificati).
  • Caching automatico dei risultati della scansione: eseguire una scansione dell'estensione richiederà tempo la prima volta. Tuttavia la seconda volta, assumendo che l'estensione non sia stata aggiornata, sarà quasi immediato grazie ai risultati in cache.
  • URL di report collegabili, permette di condividere facilmente il report di un'estensione generato da Tarnish.

Neto

Project Neto è un package Python 3 concepito per analizzare e rivelare funzionalità nascoste di plugin ed estensioni del browser per browser noti come Firefox e Chrome. Automatizza il processo di unzip dei file confezionati per estrarre queste feature da risorse rilevanti in un'estensione come manifest.json, cartelle di localizzazione o file sorgente Javascript e HTML.

Riferimenti

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks