# Metodologija Browser Extension Pentesting
{{#include ../../banners/hacktricks-training.md}}
## Osnovne informacije
Browser ekstenzije su napisane u JavaScript i učitavaju se u pozadini pregledača. Imaju svoj [DOM](https://www.w3schools.com/js/js_htmldom.asp) ali mogu da interaguju sa DOM-ovima drugih sajtova. To znači da mogu ugroziti poverljivost, integritet i dostupnost (CIA).
## Glavne komponente
Izgled ekstenzije najbolje je vizuelizovati i sastoji se iz tri komponente. Pogledajmo svaku komponentu detaljno.
### **Skripte sadržaja (Content Scripts)**
Svaka skripta sadržaja ima direktan pristup DOM-u **jedne web stranice** i time je izložena **potencijalno zlonamernom unosu**. Međutim, skripta sadržaja nema dozvole osim mogućnosti slanja poruka jezgru ekstenzije.
### **Jezgro ekstenzije (Extension Core)**
Jezgro ekstenzije sadrži većinu privilegija/pristupa ekstenzije, ali jezgro ekstenzije može da interaguje sa web sadržajem samo putem [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) i content scripts. Takođe, jezgro ekstenzije nema direktan pristup host mašini.
### **Native Binary**
Ekstenzija dozvoljava nativni binarni fajl koji može da pristupi host mašini sa punim privilegijama korisnika. Nativni binarni fajl komunicira sa jezgrom ekstenzije kroz standardni Netscape Plugin Application Programming Interface ([NPAPI](https://en.wikipedia.org/wiki/NPAPI)) koji koriste Flash i drugi browser plug-inovi.
### Granice
> [!CAUTION]
> Da bi napadač dobio pune privilegije korisnika, mora ubediti ekstenziju da prosledi zlonamerni unos iz skripte sadržaja u jezgro ekstenzije i iz jezgra ekstenzije u nativni binarni fajl.
Svaka komponenta ekstenzije je odvojena od ostalih snažnim zaštitnim granicama. Svaka komponenta se izvršava u zasebnom procesu operativnog sistema. Skripte sadržaja i jezgra ekstenzije rade u sandbox procesima koji nisu dostupni većini servisa operativnog sistema.
Štaviše, skripte sadržaja su odvojene od povezanih web stranica tako što se izvršavaju u zasebnom JavaScript heap-u. Skripta sadržaja i web stranica imaju pristup istom osnovnom DOM-u, ali ta dva nikada ne razmenjuju JavaScript pokazivače, sprečavajući leaking JavaScript funkcionalnosti.
## **`manifest.json`**
Chrome ekstenzija je u suštini ZIP folder sa [.crx file extension](https://www.lifewire.com/crx-file-2620391). Jezgro ekstenzije je **`manifest.json`** fajl u korenu foldera, koji specificira izgled, dozvole i druge konfiguracione opcije.
Primer:
```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 se **učitavaju** kad god korisnik **navigira na odgovarajuću stranicu**, u našem slučaju bilo kojoj stranici koja odgovara izrazu **`https://example.com/*`** i koja ne odgovara regexu **`*://*/*/business*`**. Izvršavaju se **kao skripte same stranice** i imaju proizvoljan pristup stranici [Document Object Model (DOM)](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model).
```json
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],
```
Da biste uključili ili isključili više URL-ova, možete koristiti i **`include_globs`** i **`exclude_globs`**.
Ovo je primer content script-a koji će dodati dugme 'explain' na stranicu i koristiti [the storage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) da preuzme vrednost `message` iz skladišta ekstenzije.
```js
chrome.storage.local.get("message", (result) => {
let div = document.createElement("div")
div.innerHTML = result.message + " "
div.querySelector("button").addEventListener("click", () => {
chrome.runtime.sendMessage("explain")
})
document.body.appendChild(div)
})
```
Poruka se šalje na extension pages od strane content scripta kada se ovaj dugme klikne, kroz korišćenje [**runtime.sendMessage() API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage). To je zbog ograničenja content scripta u direktnom pristupu API-ima, pri čemu je `storage` među retkim izuzecima. Za funkcionalnosti koje prelaze ove izuzetke, poruke se šalju na extension pages sa kojima content scripts mogu da komuniciraju.
> [!WARNING]
> U zavisnosti od pregledača, mogućnosti content scripta se mogu blago razlikovati. Za Chromium-based browsers, lista mogućnosti je dostupna u [Chrome Developers documentation](https://developer.chrome.com/docs/extensions/mv3/content_scripts/#capabilities), a za Firefox, [MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#webextension_apis) služi kao glavni izvor.\
> Takođe je vredno napomenuti da content scripts imaju mogućnost komunikacije sa background scripts, što im omogućava da izvrše akcije i proslede odgovore nazad.
Za pregled i debugovanje content scripts u Chrome-u, meni Chrome developer tools može se pristupiti iz Options > More tools > Developer tools ILI pritiskom na Ctrl + Shift + I.
Kada se developer tools prikažu, kliknite na **Source tab**, potom na **Content Scripts** tab. Ovo omogućava posmatranje pokrenutih content scripts iz različitih ekstenzija i postavljanje breakpoints-a za praćenje toka izvršavanja.
### Injected content scripts
> [!TIP]
> Imajte na umu da **Content Scripts nisu obavezni** jer je takođe moguće **dinamički** **injektovati** skripte i **programatski ih injektovati** u web stranice putem **`tabs.executeScript`**. Ovo zapravo pruža više **granular controls**.
Za programatsko injektovanje content scripta, ekstenzija mora imati [host permissions](https://developer.chrome.com/docs/extensions/reference/permissions) za stranicu u koju će skripte biti injektovane. Ove dozvole mogu se obezbediti ili njihovim **zahtevanjem** u manifestu ekstenzije ili privremeno putem [**activeTab**](https://developer.chrome.com/docs/extensions/reference/manifest/activeTab).
#### Primer ekstenzije zasnovane na activeTab
```json:manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
```
- **Inject JS fajl na klik:**
```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"],
})
})
```
- **Inject a function** na klik:
```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,
})
})
```
#### Primer sa dozvolama za skriptovanje
```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" })
```
Da biste uključili ili isključili više URL-ova, moguće je koristiti **`include_globs`** i **`exclude_globs`**.
### Content Scripts `run_at`
Polje `run_at` kontroliše **kada se JavaScript fajlovi ubacuju u web stranicu**. Poželjna i podrazumevana vrednost je `"document_idle"`.
Moguće vrednosti su:
- **`document_idle`**: Kad god je moguće
- **`document_start`**: Nakon svih fajlova iz `css`, ali pre nego što se ostali delovi DOM-a konstruišu ili pre nego što se pokrene bilo koji drugi skript.
- **`document_end`**: Odmah nakon što je DOM kompletan, ali pre nego što podresursi poput slika i frejmova budu učitani.
#### Putem `manifest.json`
```json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
```
Putem **`service-worker.js`**
```javascript
chrome.scripting.registerContentScripts([
{
id: "test",
matches: ["https://*.example.com/*"],
runAt: "document_idle",
js: ["contentScript.js"],
},
])
```
### `background`
Poruke koje šalju content scripts primaju se na **background page**, koja ima centralnu ulogu u koordinaciji komponenti ekstenzije. Važno je da background page opstaje tokom celog životnog veka ekstenzije, radi diskretno bez direktne interakcije sa korisnikom. Ona poseduje sopstveni Document Object Model (DOM), što omogućava kompleksne interakcije i upravljanje stanjem.
**Key Points**:
- **Background Page Role:** Deluje kao centralni nervni centar ekstenzije, obezbeđujući komunikaciju i koordinaciju između različitih delova ekstenzije.
- **Persistence:** To je stalna komponenta, nevidljiva korisniku ali ključna za funkcionalnost ekstenzije.
- **Automatic Generation:** Ako nije eksplicitno definisana, browser će automatski kreirati background page. Ova automatski generisana stranica uključiće sve background skripte navedene u manifestu ekstenzije, osiguravajući neometan rad background zadataka ekstenzije.
> [!TIP]
> Pogodnost koju browser pruža automatskim generisanjem background page (kada nije eksplicitno deklarisana) osigurava da su sve neophodne background skripte integrisane i operativne, pojednostavljujući proces podešavanja ekstenzije.
Example background script:
```js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "explain") {
chrome.tabs.create({ url: "https://example.net/explanation" })
}
})
```
It uses [runtime.onMessage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage) to listen to messages. When an `"explain"` message is received, it uses [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) to open a page in a new tab.
Koristi [runtime.onMessage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage) za osluškivanje poruka. Kada se primi poruka `"explain"`, koristi [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) da otvori stranicu u novoj kartici.
To debug the background script you could go to the **extension details and inspect the service worker,** this will open the developer tools with the background script:
Da biste otklonili greške u background skripti, možete otići na **extension details and inspect the service worker,** što će otvoriti developer tools sa background skriptom:
### Options pages and other
Options pages and other
Browser extensions can contain various kinds of pages:
Browser ekstenzije mogu sadržati različite vrste stranica:
- **Action pages** are displayed in a **drop-down when the extension ico**n is clicked.
- Pages that the extension will **load in a new tab**.
- **Option Pages**: This page displays on top of the extension when clicked. In the previous manifest In my case I was able to access this page in `chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca` or clicking:
- **Action pages** se prikazuju u obliku **drop-down menija kada se klikne ikona ekstenzije**.
- Stranice koje ekstenzija učitava u **novoj kartici**.
- **Option Pages**: Ova stranica se prikazuje iznad ekstenzije kada se klikne. U mom slučaju, u starom manifestu uspeo sam da pristupim ovoj stranici preko `chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca` ili klikom:
Note that these pages aren't persistent like background pages as they load dynamically content on necessity. Despite this, they share certain capabilities with the background page:
Imajte na umu da ove stranice nisu persistentne kao background pages — učitavaju se dinamički po potrebi. Uprkos tome, dele određene mogućnosti sa background page:
- **Communication with Content Scripts:** Similar to the background page, these pages can receive messages from content scripts, facilitating interaction within the extension.
- **Access to Extension-Specific APIs:** These pages enjoy comprehensive access to extension-specific APIs, subject to the permissions defined for the extension.
- **Komunikacija sa Content Scripts:** Slično background page, ove stranice mogu primati poruke od content scripts, olakšavajući interakciju unutar ekstenzije.
- **Pristup extension-specific APIs:** Ove stranice imaju širi pristup extension-specific APIs, u okviru permisija definisanih za ekstenziju.
### `permissions` & `host_permissions`
### `permissions` & `host_permissions`
**`permissions`** and **`host_permissions`** are entries from the `manifest.json` that will indicate **which permissions** the browser extensions has (storage, location...) and in **which web pages**.
Pošto su browser ekstenzije često vrlo privilegovane, maliciozna ekstenzija ili kompromitovana ekstenzija može napadaču omogućiti različite načine za krađu osetljivih informacija i nadgledanje korisnika.
Proverite kako ova podešavanja funkcionišu i kako se mogu zloupotrebiti u:
**`permissions`** i **`host_permissions`** su unosi u `manifest.json` koji ukazuju **koje permisije** ekstenzija zahteva (storage, location...) i **na kojim web stranicama**.
Pošto su browser ekstenzije često veoma privilegovane, maliciozna ili kompromitovana ekstenzija može napadaču omogućiti različite načine za krađu osetljivih informacija i nadgledanje korisnika.
Check how these settings work and how they could get abused in:
Pogledajte kako ova podešavanja funkcionišu i kako se mogu zloupotrebiti u:
{{#ref}}
browext-permissions-and-host_permissions.md
{{#endref}}
### `content_security_policy`
### `content_security_policy`
A **content security policy** can be declared also inside the `manifest.json`. If there is one defined, it could be **vulnerable**.
U `manifest.json` se može deklarisati i **content security policy**. Ako je definisana, ona može biti **ranjiva**.
The default setting for browser extension pages is rather restrictive:
Podrazumevana podešavanja za stranice ekstenzije su prilično restriktivna:
```bash
script-src 'self'; object-src 'self';
```
Za više informacija o CSP i potencijalnim bypass-ima pogledajte:
{{#ref}}
../content-security-policy-csp-bypass/
{{#endref}}
### `web_accessible_resources`
Da bi web stranica mogla da pristupi stranici Browser Extension-a, na primer `.html` stranici, ta stranica mora biti navedena u polju **`web_accessible_resources`** u `manifest.json`.\
Na primer:
```javascript
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
```
Ove stranice su dostupne na URL-ovima poput:
```
chrome-extension:///message.html
```
In public extensions the **extension-id is accesible**:
Although, if the `manifest.json` parameter **`use_dynamic_url`** is used, this **id can be dynamic**.
> [!TIP]
> Imajte na umu da čak i ako je neka stranica ovde pomenuta, ona može biti **zaštićena protiv ClickJacking** zahvaljujući **Content Security Policy**. Zato je potrebno proveriti i to (frame-ancestors section) pre nego što potvrdite da je ClickJacking napad moguć.
Being allowed to access these pages make these pages **potentially vulnerable ClickJacking**:
{{#ref}}
browext-clickjacking.md
{{#endref}}
> [!TIP]
> Dozvoljavanje da se ove stranice učitavaju samo od strane ekstenzije, a ne sa nasumičnih URL-ova, može sprečiti ClickJacking napade.
> [!CAUTION]
> Imajte na umu da stranice iz **`web_accessible_resources`** i druge stranice ekstenzije takođe mogu da **kontaktiraju background scripts**. Dakle, ako je jedna od ovih stranica ranjiva na **XSS**, to može otvoriti veću ranjivost.
>
> Štaviše, napomena da možete otvoriti samo stranice navedene u **`web_accessible_resources`** unutar iframe-ova, ali iz novog taba je moguće pristupiti bilo kojoj stranici u ekstenziji znajući extension ID. Dakle, ako se pronađe XSS koji zloupotrebljava iste parametre, mogao bi se iskoristiti čak i ako stranica nije konfigurirana u **`web_accessible_resources`**.
### `externally_connectable`
A per the [**docs**](https://developer.chrome.com/docs/extensions/reference/manifest/externally-connectable), The `"externally_connectable"` manifest property declares **which extensions and web pages can connect** to your extension via [runtime.connect](https://developer.chrome.com/docs/extensions/reference/runtime#method-connect) and [runtime.sendMessage](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage).
- If the **`externally_connectable`** key is **not** declared in your extension's manifest or it's declared as **`"ids": ["*"]`**, **all extensions can connect, but no web pages can connect**.
- If **specific IDs are specified**, like in `"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]`, **only those applications** can connect.
- If **matches** are specified, those web apps will be able to connect:
```json
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
```
- If it's specified as empty: **`"externally_connectable": {}`**, nijedan app ili web neće moći da se poveže.
Što je manje **extensions i URLs** ovde navedeno, to će površina napada biti manja.
> [!CAUTION]
> Ako je na strani naveden web page **vulnerable to XSS or takeover** u **`externally_connectable`**, napadač će moći da **send messages directly to the background script**, potpuno zaobišavši Content Script i njegov CSP.
>
> Dakle, ovo je **veoma jak bypass**.
>
> Štaviše, ako klijent instalira zlonamerni extension, čak i ako mu nije dozvoljeno da komunicira sa ranjivim extension-om, mogao bi da injektuje **XSS data in an allowed web page** ili da zloupotrebi **`WebRequest`** ili **`DeclarativeNetRequest`** API-je da manipuliše request-ovima na ciljanom domenu menjajući zahtev stranice za **JavaScript file**. (Imajte na umu da CSP na ciljanoj stranici može da spreči ove napade). Ova ideja dolazi [**from this writeup**](https://www.darkrelay.com/post/opera-zero-day-rce-vulnerability).
## Communication summary
### Extension <--> WebApp
Za komunikaciju između content script-a i web stranice obično se koriste post messages. Zato ćete u web aplikaciji obično naći pozive funkciji **`window.postMessage`** i u content script-u listenere kao **`window.addEventListener`**. Ipak, treba napomenuti da extension takođe može **communicate with the web application sending a Post Message** (i samim tim web treba da to očekuje) ili jednostavno naterati web da učita novi script.
### Inside the extension
Obično se funkcija **`chrome.runtime.sendMessage`** koristi za slanje poruke unutar extension-a (obično obrađuje `background` script) i da bi se primila i obradila, deklariše se listener pozivom **`chrome.runtime.onMessage.addListener`**.
Takođe je moguće koristiti **`chrome.runtime.connect()`** da se uspostavi persistent connection umesto slanja pojedinačnih poruka; moguće ga je koristiti za **send** i **receive** **messages** kao u sledećem primeru:
chrome.runtime.connect() example
```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
})
```
Takođe je moguće slati poruke iz background skripta do content skripta koji se nalazi u određenom tab-u pozivanjem **`chrome.tabs.sendMessage`**, gde ćete morati da navedete **ID of the tab** kome se poruka šalje.
### Iz dozvoljenih `externally_connectable` ka ekstenziji
**Web aplikacije i eksterni browser extensions koji su dozvoljeni** u konfiguraciji `externally_connectable` mogu slati zahteve koristeći :
```javascript
chrome.runtime.sendMessage(extensionId, ...
```
Gde je potrebno pomenuti **extension ID**.
### Native Messaging
Moguće je da background scripts komuniciraju sa binaries u sistemu, što može dovesti do **kritičnih ranjivosti kao što su RCEs** ako ova komunikacija nije pravilno osigurana. [Više o tome kasnije](#native-messaging).
```javascript
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
```
## Web **↔︎** Content Script komunikacija
Okruženja u kojima rade **content scripts** i u kojima postoje host stranice su **odvojena** jedno od drugog, obezbeđujući **izolaciju**. Uprkos toj izolaciji, oba imaju mogućnost interakcije sa **Document Object Model (DOM)** stranice, koji je zajednički resurs. Da bi host stranica ostvarila komunikaciju sa **content script**, ili indirektno sa ekstenzijom preko **content script**, potrebno je koristiti **DOM** koji je dostupan obema stranama kao kanal komunikacije.
### Post Messages
```javascript:content-script.js
// This is like "chrome.runtime.sendMessage" but to maintain the connection
var port = chrome.runtime.connect()
window.addEventListener(
"message",
(event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return
}
if (event.data.type && event.data.type === "FROM_PAGE") {
console.log("Content script received: " + event.data.text)
// Forward the message to the background script
port.postMessage(event.data.text)
}
},
false
)
```
```javascript:example.js
document.getElementById("theButton").addEventListener(
"click",
() => {
window.postMessage(
{ type: "FROM_PAGE", text: "Hello from the webpage!" },
"*"
)
},
false
)
```
Sigurna Post Message komunikacija treba da proveri autentičnost primljene poruke; to se može uraditi proverom:
- **`event.isTrusted`**: Ovo je True samo ako je događaj pokrenut akcijom korisnika
- content script može očekivati poruku samo ako korisnik izvrši neku radnju
- **origin domain**: može očekivati poruku samo sa dozvoljene liste domena.
- Ako se koristi regex, budite veoma oprezni
- **Source**: `received_message.source !== window` možete koristiti da proverite da li je poruka **iz istog prozora** u kome Content Script osluškuje.
Prethodne provere, čak i ako su izvršene, mogu biti ranjive, zato proverite na sledećoj stranici **potencijalne Post Message bypasses**:
{{#ref}}
../postmessage-vulnerabilities/
{{#endref}}
### Iframe
Još jedan mogući način komunikacije može biti preko **Iframe URLs**, primer možete naći u:
{{#ref}}
browext-xss-example.md
{{#endref}}
### DOM
Ovo nije "tačno" način komunikacije, ali **web i content script će imati pristup web DOM-u**. Dakle, ako **content script** čita neke informacije iz njega, **verujući web DOM-u**, web može **izmeniti ove podatke** (jer webu se ne treba verovati, ili zato što je web ranjiv na XSS) i **kompromitovati Content Script**.
Takođe možete naći primer **DOM based XSS to compromise a browser extension** u:
{{#ref}}
browext-xss-example.md
{{#endref}}
## Komunikacija Content Script **↔︎** Background Script
Content Script može koristiti funkcije [**runtime.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/runtime#method-sendMessage) **ili** [**tabs.sendMessage()**](https://developer.chrome.com/docs/extensions/reference/tabs#method-sendMessage) da pošalje **one-time JSON-serializable** poruku.
Za rukovanje **response**, koristite vraćeni **Promise**. Ipak, radi kompatibilnosti unazad, i dalje možete proslediti **callback** kao poslednji argument.
Slanje zahteva iz **content script** izgleda ovako:
```javascript
;(async () => {
const response = await chrome.runtime.sendMessage({ greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()
```
Slanje zahteva iz **extension** (obično iz **background script**). Primer kako poslati poruku content script-u u izabranom tab-u:
```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)
})()
```
Na strani koja prima, treba da postavite [**runtime.onMessage**](https://developer.chrome.com/docs/extensions/reference/runtime#event-onMessage) **event listener** da obradite poruku. Ovo izgleda isto u content scriptu ili na extension page-u.
```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" })
})
```
U prikazanom primeru, **`sendResponse()`** je izvršen sinhrono. Da biste izmenili `onMessage` event handler da asinhrono izvršava `sendResponse()`, neophodno je uključiti `return true;`.
Bitno je napomenuti da u scenarijima gde više stranica prima `onMessage` događaje, **prva stranica koja izvrši `sendResponse()`** za određeni događaj biće jedina koja može efikasno dostaviti odgovor. Svaki naknadni odgovor na isti događaj neće biti uzet u obzir.
Prilikom izrade novih ekstenzija, treba davati prednost promises umesto callbacks. Što se tiče upotrebe callbacks, funkcija `sendResponse()` važi samo ako se izvrši direktno u sinhronom kontekstu, ili ako event handler označi asinhronu operaciju vraćanjem `true`. Ako nijedan od handlera ne vrati `true` ili ako je funkcija `sendResponse()` uklonjena iz memorije (garbage-collected), callback povezan sa `sendMessage()` biće po defaultu pozvan.
## Native Messaging
Ekstenzije pregledača takođe omogućavaju komunikaciju sa **binaries in the system via stdin**. Aplikacija mora instalirati json koji to navodi u json fajlu poput:
```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/"]
}
```
Gde je `name` string koji se prosleđuje funkcijama [`runtime.connectNative()`](https://developer.chrome.com/docs/extensions/reference/api/runtime#method-connectNative) ili [`runtime.sendNativeMessage()`](https://developer.chrome.com/docs/extensions/reference/api/runtime#method-sendNativeMessage) da bi se komuniciralo sa aplikacijom iz pozadinskih skripti ekstenzije preglednika. `path` je putanja do binarnog fajla, postoji samo 1 važeći `type` koji je stdio (koristi stdin i stdout), a `allowed_origins` označavaju ekstenzije koje mogu da mu pristupe (i ne mogu imati wildcard).
Chrome/Chromium će tražiti ovaj json u nekim Windows registry-ima i nekim putanjama na macOS i Linuxu (više informacija u [**docs**](https://developer.chrome.com/docs/extensions/develop/concepts/native-messaging)).
> [!TIP]
> Ekstenzija preglednika takođe mora imati deklarisanu `nativeMessaing` permission da bi mogla da koristi ovu komunikaciju.
Ovako izgleda kod pozadinske skripte koja šalje poruke native aplikaciji:
```javascript
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
```
In [**ovom blog postu**](https://spaceraccoon.dev/universal-code-execution-browser-extensions/), predložen je ranjiv obrazac koji zloupotrebljava native messages:
1. Ekstenzija pregledača ima wildcard pattern za content script.
2. Content script prosleđuje `postMessage` poruke background skriptu koristeći `sendMessage`.
3. Background skripta prosleđuje poruku native aplikaciji koristeći `sendNativeMessage`.
4. Native aplikacija nebezbedno obrađuje poruku, što može dovesti do izvršenja koda.
I u okviru toga objašnjen je primer kako se sa bilo koje stranice može ostvariti RCE zloupotrebom ekstenzije pregledača.
## Sensitive Information in Memory/Code/Clipboard
Ako ekstenzija pregledača čuva **osetljive informacije u svojoj memoriji**, one mogu biti **izvučene** (posebno na Windows mašinama) i **pretražene** u potrazi za tim podacima.
Stoga memorija ekstenzije pregledača **ne treba da se smatra sigurnom**, i **osetljive informacije** kao što su kredencijali ili mnemoničke fraze **ne bi trebalo da se čuvaju**.
Naravno, nemojte **staviti osetljive informacije u kod**, jer će one biti **javno dostupne**.
Da biste dobili memory dump iz browsera možete dumpovati memoriju procesa ili otići u podešavanja ekstenzije pregledača, kliknuti na **`Inspect pop-up`** -> u **`Memory`** sekciji -> **`Take a snaphost`** i pritisnuti **`CTRL+F`** da pretražite snapshot za osetljive informacije.
Pored toga, izrazito osetljive informacije kao mnemonik ključevi ili lozinke **ne bi trebalo dozvoliti da se kopiraju u clipboard** (ili bar ukloniti iz clipboarda nakon nekoliko sekundi), jer će tada procesi koji nadgledaju clipboard moći da ih dobiju.
## Loading an Extension in the Browser
1. **Preuzmite** ekstenziju pregledača i raspakujte
2. Idite na **`chrome://extensions/`** i **omogućite** `Developer Mode`
3. Kliknite na dugme **`Load unpacked`**
U **Firefox** odete na **`about:debugging#/runtime/this-firefox`** i kliknete na dugme **`Load Temporary Add-on`**.
## Getting the source code from the store
Izvorni kod Chrome ekstenzije može se dobiti na više načina. Ispod su detaljna objašnjenja i uputstva za svaku opciju.
### Download Extension as ZIP via Command Line
Izvorni kod Chrome ekstenzije može se preuzeti kao ZIP fajl korišćenjem komandne linije. To podrazumeva korišćenje `curl` da se preuzme ZIP fajl sa specifičnog URL-a i zatim izdvoje sadržaji ZIP fajla u direktorijum. Evo koraka:
1. Zamenite `"extension_id"` stvarnim ID-jem ekstenzije.
2. Pokrenite sledeće komande:
```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"
```
### Koristite CRX Viewer veb-sajt
[https://robwu.nl/crxviewer/](https://robwu.nl/crxviewer/)
### Koristite CRX Viewer ekstenziju
Još jedan zgodan metod je korišćenje Chrome Extension Source Viewer, koji je projekat otvorenog koda. Može se instalirati iz [Chrome Web Store](https://chrome.google.com/webstore/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin?hl=en). Izvorni kod viewer-a je dostupan u njegovom [GitHub repository](https://github.com/Rob--W/crxviewer).
### Pregled izvora lokalno instalirane ekstenzije
Chrome ekstenzije instalirane lokalno takođe se mogu pregledati. Evo kako:
1. Pristupite lokalnom Chrome profilu tako što ćete otići na `chrome://version/` i pronaći polje "Profile Path".
2. Idite u podfolder `Extensions/` unutar direktorijuma profila.
3. Ovaj folder sadrži sve instalirane ekstenzije, obično sa izvornim kodom u čitljivom formatu.
Da biste identifikovali ekstenzije, možete mapirati njihove ID-e na nazive:
- Omogućite Developer Mode na stranici `about:extensions` da biste videli ID-e svake ekstenzije.
- U folderu svake ekstenzije, fajl `manifest.json` sadrži čitljivo polje `name`, koje pomaže pri identifikaciji ekstenzije.
### Koristite arhiver ili alat za raspakivanje
Idite na Chrome Web Store i preuzmite ekstenziju. Fajl će imati `.crx` ekstenziju. Promenite ekstenziju fajla iz `.crx` u `.zip`. Koristite bilo koji arhiver (npr. WinRAR, 7-Zip, itd.) da izdvojite sadržaj ZIP fajla.
### Koristite Developer Mode u Chrome-u
Otvorite Chrome i idite na `chrome://extensions/`. Uključite "Developer mode" u gornjem desnom uglu. Kliknite na "Load unpacked extension...". Navigirajte do direktorijuma vaše ekstenzije. Ovo ne preuzima izvorni kod, ali je korisno za pregled i modifikaciju koda već preuzete ili razvijene ekstenzije.
## Dataset manifesta Chrome ekstenzija
Da biste pokušali uočiti ranjive browser ekstenzije možete koristiti [https://github.com/palant/chrome-extension-manifests-dataset](https://github.com/palant/chrome-extension-manifests-dataset) i proveriti njihove manifest fajlove za potencijalne znake ranjivosti. Na primer, da biste proverili ekstenzije sa više od 25000 korisnika, `content_scripts` i permisijom `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)
Prikrivena tehnika za ubacivanje backdoora u Chromium direktnim izmenama per-user Preferences i falsifikovanjem validnih HMACs, zbog čega pregledač prihvati i aktivira proizvoljan unpacked extension bez promptova ili flagova.
{{#ref}}
forced-extension-load-preferences-mac-forgery-windows.md
{{#endref}}
## Kontrolna lista sigurnosnog audita
Iako Browser Extensions imaju **limited attack surface**, neke od njih mogu sadržati **vulnerabilities** ili **potential hardening improvements**. Sledeće su najčešće:
- [ ] **Limit** as much as possible requested **`permissions`**
- [ ] **Limit** as much as possible **`host_permissions`**
- [ ] Use a **strong** **`content_security_policy`**
- [ ] **Limit** as much as possible the **`externally_connectable`**, if none is needed and possible, do not leave it by default, specify **`{}`**
- [ ] If **URL vulnerable to XSS or to takeover** is mentioned here, an attacker will be able to **send messages to the background scripts directly**. Very powerful bypass.
- [ ] **Limit** as much as possible the **`web_accessible_resources`**, even empty if possible.
- [ ] If **`web_accessible_resources`** is not none, check for [**ClickJacking**](browext-clickjacking.md)
- [ ] If any **communication** occurs from the **extension** to the **web page**, [**check for XSS**](browext-xss-example.md) **vulnerabilities** caused in the communication.
- [ ] If Post Messages are used, check for [**Post Message vulnerabilities**](../postmessage-vulnerabilities/index.html)**.**
- [ ] If the **Content Script access DOM details**, check that they **aren't introducing a XSS** if they get **modified** by the web
- [ ] Make a special emphasis if this communication is also involved in the **Content Script -> Background script communication**
- [ ] If the background script is communicating via **native messaging** check the communication is secure and sanitized
- [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **code**
- [ ] **Sensitive information shouldn't be stored** inside the Browser Extension **memory**
- [ ] **Sensitive information shouldn't be stored** inside the **file system unprotected**
## Browser Extension Risks
- The app [https://crxaminer.tech/](https://crxaminer.tech/) analyzes some data like the permissions browser extension requests to give a risk level of using the browser extension.
## Alati
### [**Tarnish**](https://thehackerblog.com/tarnish/)
- Pulls any Chrome extension from a provided Chrome webstore link.
- [**manifest.json**](https://developer.chrome.com/extensions/manifest) **viewer**: simply displays a JSON-prettified version of the extension’s manifest.
- **Fingerprint Analysis**: Detection of [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) and automatic generation of Chrome extension fingerprinting JavaScript.
- **Potential Clickjacking Analysis**: Detection of extension HTML pages with the [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) directive set. These are potentially vulnerable to clickjacking depending on the purpose of the pages.
- **Permission Warning(s) viewer**: which shows a list of all the Chrome permission prompt warnings which will be displayed upon a user attempting to install the extension.
- **Dangerous Function(s)**: shows the location of dangerous functions which could potentially be exploited by an attacker (e.g. functions such as innerHTML, chrome.tabs.executeScript).
- **Entry Point(s)**: shows where the extension takes in user/external input. This is useful for understanding an extension’s surface area and looking for potential points to send maliciously-crafted data to the extension.
- Both the Dangerous Function(s) and Entry Point(s) scanners have the following for their generated alerts:
- Relevant code snippet and line that caused the alert.
- Description of the issue.
- A “View File” button to view the full source file containing the code.
- The path of the alerted file.
- The full Chrome extension URI of the alerted file.
- The type of file it is, such as a Background Page script, Content Script, Browser Action, etc.
- If the vulnerable line is in a JavaScript file, the paths of all of the pages where it is included as well as these page’s type, and [web_accessible_resource](https://developer.chrome.com/extensions/manifest/web_accessible_resources) status.
- **Content Security Policy (CSP) analyzer and bypass checker**: This will point out weaknesses in your extension’s CSP and will also illuminate any potential ways to bypass your CSP due to whitelisted CDNs, etc.
- **Known Vulnerable Libraries**: This uses [Retire.js](https://retirejs.github.io/retire.js/) to check for any usage of known-vulnerable JavaScript libraries.
- Download extension and formatted versions.
- Download the original extension.
- Download a beautified version of the extension (auto prettified HTML and JavaScript).
- Automatic caching of scan results, running an extension scan will take a good amount of time the first time you run it. However the second time, assuming the extension hasn’t been updated, will be almost instant due to the results being cached.
- Linkable Report URLs, easily link someone else to an extension report generated by tarnish.
### [Neto](https://github.com/elevenpaths/neto)
Project Neto is a Python 3 package conceived to analyse and unravel hidden features of browser plugins and extensions for well-known browsers such as Firefox and Chrome. It automates the process of unzipping the packaged files to extract these features from relevant resources in a extension like `manifest.json`, localization folders or Javascript and HTML source files.
## Reference
- **Thanks to** [**@naivenom**](https://twitter.com/naivenom) **for the help with this methodology**
- [https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing](https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing)
- [https://palant.info/2022/08/10/anatomy-of-a-basic-extension/](https://palant.info/2022/08/10/anatomy-of-a-basic-extension/)
- [https://palant.info/2022/08/24/attack-surface-of-extension-pages/](https://palant.info/2022/08/24/attack-surface-of-extension-pages/)
- [https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/](https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/)
- [https://help.passbolt.com/assets/files/PBL-02-report.pdf](https://help.passbolt.com/assets/files/PBL-02-report.pdf)
- [https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts)
- [https://developer.chrome.com/docs/extensions/mv2/background-pages](https://developer.chrome.com/docs/extensions/mv2/background-pages)
- [https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/](https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/)
- [https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0](https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0)
{{#include ../../banners/hacktricks-training.md}}