Metodologia Browser Extension Pentesting

Reading time: 27 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Podstawowe informacje

Rozszerzenia przeglądarki są pisane w JavaScript i ładowane przez przeglądarkę w tle. Mają swój DOM, ale mogą wchodzić w interakcję z DOM-ami innych stron. Oznacza to, że mogą naruszyć poufność, integralność i dostępność (CIA) innych serwisów.

Główne komponenty

Schematy rozszerzeń najlepiej wizualizować i składają się z trzech komponentów. Przyjrzyjmy się każdemu z nich szczegółowo.

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

Content Scripts

Każdy content script ma bezpośredni dostęp do DOM pojedynczej strony internetowej i w związku z tym jest narażony na potencjalnie złośliwe dane wejściowe. Jednak content script nie ma żadnych uprawnień poza możliwością wysyłania wiadomości do jądra rozszerzenia.

Extension Core

Extension core zawiera większość uprawnień/dostępu rozszerzenia, ale extension core może wchodzić w interakcję z zawartością webową jedynie poprzez XMLHttpRequest oraz content scripts. Ponadto extension core nie ma bezpośredniego dostępu do maszyny hosta.

Native Binary

Rozszerzenie może udostępniać natywny binarny plik, który może uzyskać dostęp do maszyny hosta z pełnymi uprawnieniami użytkownika. Natywny binarny plik komunikuje się z extension core za pośrednictwem standardowego Netscape Plugin Application Programming Interface (NPAPI) używanego przez Flash i inne wtyczki przeglądarki.

Granice

caution

Aby uzyskać pełne uprawnienia użytkownika, atakujący musi przekonać rozszerzenie do przekazania złośliwego wejścia ze skryptu treści do jądra rozszerzenia oraz z jądra rozszerzenia do natywnego pliku binarnego.

Każdy komponent rozszerzenia jest odseparowany od pozostałych przez silne granice ochronne. Każdy komponent działa w osobnym procesie systemu operacyjnego. Content scripts i extension cores działają w procesach sandbox niedostępnych dla większości usług systemu operacyjnego.

Co więcej, content scripts są oddzielone od powiązanych stron internetowych przez uruchamianie w osobnym stosie pamięci JavaScript. Content script i strona mają dostęp do tego samego underlying DOM, ale obie nigdy nie wymieniają wskaźników JavaScript, co zapobiega wyciekowi funkcjonalności JavaScript.

manifest.json

Rozszerzenie Chrome to po prostu folder ZIP z rozszerzeniem pliku .crx. Rdzeniem rozszerzenia jest plik manifest.json w katalogu głównym folderu, który określa strukturę, uprawnienia oraz inne opcje konfiguracyjne.

Przykład:

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

Skrypty zawartości są ładowane za każdym razem, gdy użytkownik przechodzi na pasującą stronę, w naszym przypadku każda strona pasująca do wyrażenia https://example.com/* i niepasująca do regexu *://*/*/business*. Wykonują się jak własne skrypty strony i mają dowolny dostęp do strony Document Object Model (DOM).

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

Aby uwzględnić lub wykluczyć więcej adresów URL, można także użyć include_globs i exclude_globs.

Poniższy przykładowy content script doda przycisk explain do strony, gdy the storage API zostanie użyte do pobrania wartości message ze storage rozszerzenia.

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

Gdy ten przycisk zostanie kliknięty, content script wysyła wiadomość do stron rozszerzenia za pomocą runtime.sendMessage() API. Wynika to z ograniczeń content script w bezpośrednim dostępie do API, przy czym storage jest jednym z nielicznych wyjątków. Dla funkcji wykraczających poza te wyjątki wiadomości są wysyłane do stron rozszerzenia, z którymi content scripts mogą się komunikować.

warning

W zależności od przeglądarki możliwości content script mogą się nieznacznie różnić. Dla przeglądarek opartych na Chromium lista możliwości jest dostępna w Chrome Developers documentation, a dla Firefox głównym źródłem jest MDN.
Warto też zaznaczyć, że content scripts mogą komunikować się ze background scripts, co pozwala im wykonywać działania i przekazywać odpowiedzi z powrotem.

Aby przeglądać i debugować content scripts w Chrome, menu Chrome developer tools można otworzyć przez Options > More tools > Developer tools lub przez naciśnięcie Ctrl + Shift + I.

Po otwarciu developer tools należy kliknąć zakładkę Source tab, a następnie Content Scripts. Umożliwia to obserwację uruchomionych content scripts z różnych rozszerzeń oraz ustawianie breakpointów w celu śledzenia przebiegu wykonania.

Wstrzyknięte content scripts

tip

Należy pamiętać, że Content Scripts aren't mandatory — możliwe jest również dynamiczne wstrzykiwanie skryptów oraz programatyczne wstrzykiwanie ich na strony WWW za pomocą tabs.executeScript. Daje to w rzeczywistości bardziej precyzyjną kontrolę.

Aby programatycznie wstrzyknąć content script, rozszerzenie musi posiadać host permissions dla strony, do której mają być wstrzyknięte skrypty. Uprawnienia te można uzyskać albo poprzez zadeklarowanie ich w manifeście rozszerzenia, albo tymczasowo za pomocą activeTab.

Przykład rozszerzenia opartego na activeTab

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Inject plik JS po kliknięciu:
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"],
})
})
  • Wstrzyknij funkcję przy kliknięciu:
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,
})
})

Przykład z uprawnieniami do skryptowania

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

Aby uwzględnić lub wykluczyć więcej adresów URL, można również użyć include_globs i exclude_globs.

Skrypty treści run_at

Pole run_at kontroluje kiedy pliki JavaScript są wstrzykiwane na stronę. Preferowaną i domyślną wartością jest "document_idle".

Dostępne wartości to:

  • document_idle: gdy tylko możliwe
  • document_start: po plikach z css, ale zanim zostanie zbudowany jakikolwiek inny DOM lub zanim zostanie uruchomiony jakikolwiek inny skrypt.
  • document_end: bezpośrednio po ukończeniu DOM, ale zanim załadują się subresources takie jak obrazy i ramki.

Za pomocą manifest.json

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

Za pomocą service-worker.js

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

background

Wiadomości wysyłane przez content scripts są odbierane przez stronę w tle, która pełni centralną rolę w koordynacji komponentów rozszerzenia. Co istotne, strona w tle utrzymuje się przez cały czas działania rozszerzenia, działając dyskretnie bez bezpośredniej interakcji z użytkownikiem. Posiada własny Document Object Model (DOM), co umożliwia złożone interakcje i zarządzanie stanem.

Najważniejsze punkty:

  • Rola strony w tle: Działa jak centrum nerwowe rozszerzenia, zapewniając komunikację i koordynację między jego częściami.
  • Trwałość: Jest elementem stale obecnym, niewidocznym dla użytkownika, ale kluczowym dla działania rozszerzenia.
  • Automatyczne generowanie: Jeśli nie jest jawnie zdefiniowana, przeglądarka automatycznie utworzy stronę w tle. Taka automatycznie wygenerowana strona będzie zawierać wszystkie background scripts określone w manifeście rozszerzenia, zapewniając płynne działanie zadań w tle rozszerzenia.

tip

Wygoda zapewniona przez przeglądarkę poprzez automatyczne generowanie strony w tle (gdy nie jest ona jawnie zadeklarowana) sprawia, że wszystkie niezbędne background scripts są zintegrowane i operacyjne, upraszczając proces konfiguracji rozszerzenia.

Przykładowy background script:

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

Używa runtime.onMessage API do nasłuchiwania wiadomości. Gdy zostanie odebrana wiadomość "explain", używa tabs API aby otworzyć stronę w nowej karcie.

Aby debugować background script możesz przejść do extension details and inspect the service worker, co otworzy developer tools z background script:

Options pages and other

Browser extensions mogą zawierać różne rodzaje stron:

  • Action pages are displayed in a drop-down when the extension icon is clicked.
  • Pages that the extension will load in a new tab.
  • Option Pages: Ta strona wyświetla się nad interfejsem rozszerzenia po kliknięciu. W poprzednim manifest In my case I was able to access this page in chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca or clicking:

Zwróć uwagę, że te strony nie są persistent jak background pages, ponieważ ładują zawartość dynamicznie w razie potrzeby. Mimo to, dzielą pewne możliwości z background page:

  • Communication with Content Scripts: Podobnie jak background page, te strony mogą otrzymywać wiadomości od content scripts, ułatwiając interakcję w ramach rozszerzenia.
  • Access to Extension-Specific APIs: Te strony mają pełny dostęp do extension-specific APIs, zgodnie z uprawnieniami zdefiniowanymi dla rozszerzenia.

permissions & host_permissions

permissions i host_permissions to wpisy w manifest.json, które wskazują, które permissions posiada rozszerzenie (storage, location...) oraz na których stronach WWW.

Ponieważ rozszerzenia przeglądarki mogą być bardzo privileged, złośliwe rozszerzenie lub takie, które zostało skompromitowane, może umożliwić atakującemu różne sposoby kradzieży wrażliwych informacji i szpiegowania użytkownika.

Sprawdź, jak te ustawienia działają i jak mogą zostać nadużyte w:

BrowExt - permissions & host_permissions

content_security_policy

A content security policy może być zadeklarowana również w manifest.json. Jeśli jest zdefiniowana, może być vulnerable.

Domyślne ustawienie dla stron rozszerzeń przeglądarki jest dość restrykcyjne:

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

Aby uzyskać więcej informacji o CSP i potencjalnych obejściach zobacz:

Content Security Policy (CSP) Bypass

web_accessible_resources

Aby strona internetowa mogła uzyskać dostęp do strony rozszerzenia przeglądarki, na przykład pliku .html, ta strona musi być wymieniona w polu web_accessible_resources w manifest.json.
Na przykład:

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

Te strony są dostępne pod adresami URL takimi jak:

chrome-extension://<extension-id>/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

Note that even if a page is mentioned here, it might be protected against ClickJacking thanks to the Content Security Policy. So you also need to check it (frame-ancestors section) before confirming a ClickJacking attack is possible.

Being allowed to access these pages make these pages potentially vulnerable ClickJacking:

BrowExt - ClickJacking

tip

Allowing these pages to be loaded only by the extension and not by random URLs could prevent ClickJacking attacks.

caution

Note that the pages from web_accessible_resources and other pages of the extension are also capable of contacting background scripts. So if one of these pages is vulnerable to XSS it could open a bigger vulnerability.

Moreover, note that you can only open pages indicated in web_accessible_resources inside iframes, but from a new tab it's possible to access any page in the extension knowing the extension ID. Therefore, if an XSS is found abusing same parameters, it could be abused even if the page isn't configured in web_accessible_resources.

externally_connectable

A per the docs, The "externally_connectable" manifest property declares which extensions and web pages can connect to your extension via runtime.connect and runtime.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": {}, no app or web will be able to connect.

Im mniej wskazanych tutaj rozszerzeń i URL-i, tym mniejsza będzie powierzchnia ataku.

caution

If a web page vulnerable to XSS or takeover is indicated in externally_connectable, an attacker will be able to send messages directly to the background script, completely bypassing the Content Script and its CSP.

Therefore, this is a very powerful bypass.

Moreover, if the client installs a rouge extension, even if it isn't allowed to communicate with the vulnerable extension, it could inject XSS data in an allowed web page or abuse WebRequest or DeclarativeNetRequest APIs to manipulate requests on a targeted domain altering a page's request for a JavaScript file. (Note that CSP on the targeted page could prevent these attacks). This idea comes from this writeup.

Communication summary

Extension <--> WebApp

Aby komunikować się między content script a stroną webową, zwykle używa się post messages. W aplikacji webowej zazwyczaj znajdziesz wywołania funkcji window.postMessage, a w content script nasłuchiwacze takie jak window.addEventListener. Zauważ jednak, że rozszerzenie może też komunikować się z aplikacją webową wysyłając Post Message (i w związku z tym strona powinna się tego spodziewać) lub po prostu spowodować załadowanie nowego skryptu przez stronę.

Inside the extension

Zazwyczaj do wysyłania wiadomości wewnątrz rozszerzenia używana jest funkcja chrome.runtime.sendMessage (zwykle obsługiwana przez background script), a aby ją odbierać i obsługiwać, deklaruje się listener wywołujący chrome.runtime.onMessage.addListener.

Można też użyć chrome.runtime.connect() do utworzenia stałego połączenia zamiast wysyłać pojedyncze wiadomości; można go użyć do wysyłania i odbierania wiadomości, jak w poniższym przykładzie:

chrome.runtime.connect() przykład
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
})

Jest też możliwe wysyłanie wiadomości ze skryptu w tle do skryptu treści znajdującego się w konkretnej karcie, wywołując chrome.tabs.sendMessage, gdzie musisz wskazać ID karty, do której chcesz wysłać wiadomość.

Z dozwolonych externally_connectable do rozszerzenia

Aplikacje webowe i zewnętrzne rozszerzenia przeglądarki dozwolone w konfiguracji externally_connectable mogą wysyłać żądania używając :

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

Tam, gdzie trzeba wspomnieć o extension ID.

Native Messaging

Skrypty w tle mogą komunikować się z plikami binarnymi w systemie, co może być podatne na krytyczne podatności, takie jak RCEs, jeśli ta komunikacja nie jest właściwie zabezpieczona. Więcej na ten temat później.

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

Web ↔︎ Komunikacja z Content Script

Środowiska, w których działają content scripts, oraz te, w których istnieją strony hostujące, są od siebie oddzielone, zapewniając izolację. Mimo tej izolacji obie strony mają możliwość interakcji z Model Obiektu Dokumentu (DOM) — wspólnym zasobem. Aby strona hostująca mogła komunikować się z content script, lub pośrednio z rozszerzeniem przez content script, musi użyć DOM dostępnego dla obu stron jako kanału komunikacji.

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
)

A secure Post Message communication should check the authenticity of the received message, this can be done checking:

  • event.isTrusted: This is True only if the event was triggered by a users action
  • The Content Script might expecting a message only if the user performs some action
  • origin domain: może oczekiwać wiadomości tylko z dozwolonej listy domen.
  • If a regex is used, be very careful
  • Source: received_message.source !== window can be used to check if the message was from the same window where the Content Script is listening.

Powyższe kontrole, nawet jeśli są wdrożone, mogą być podatne, więc sprawdź na następującej stronie potential Post Message bypasses:

PostMessage Vulnerabilities

Iframe

Innym możliwym sposobem komunikacji mogą być Iframe URLs, przykład znajdziesz w:

BrowExt - XSS Example

DOM

To nie jest "dokładnie" sposób komunikacji, ale web i Content Script będą mieć dostęp do web DOM. Jeśli Content Script odczytuje z niego jakieś informacje i ufa web DOM, web może zmodyfikować te dane (ponieważ web nie powinien być zaufany, lub ponieważ web jest podatny na XSS) i skompro-mitować Content Script.

Znajdziesz też przykład DOM based XSS to compromise a browser extension w:

BrowExt - XSS Example

Content Script ↔︎ Background Script Communication

A Content Script can use the functions runtime.sendMessage() or tabs.sendMessage() to send a one-time JSON-serializable message.

To handle the response, use the returned Promise. Although, for backward compatibility, you can still pass a callback as the last argument.

Sending a request from a content script looks like this:

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

Wysyłanie żądania z extension (zazwyczaj background script). Przykład wysyłania wiadomości do content script w wybranej karcie:

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

Po stronie odbiorcy musisz skonfigurować runtime.onMessage event listener, aby obsłużyć wiadomość. Wygląda to tak samo zarówno w content script, jak i na 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" })
})

W wyróżnionym przykładzie sendResponse() zostało wywołane synchronicznie. Aby zmodyfikować handler zdarzenia onMessage tak, by sendResponse() wykonywało się asynchronicznie, konieczne jest dodanie return true;.

Ważne jest, że w scenariuszach, gdzie wiele stron ma otrzymywać zdarzenia onMessage, pierwsza strona, która wywoła sendResponse() dla określonego zdarzenia, będzie jedyną, która faktycznie będzie mogła dostarczyć odpowiedź. Wszelkie kolejne odpowiedzi na to samo zdarzenie nie będą brane pod uwagę.

Podczas tworzenia nowych rozszerzeń zaleca się używanie promises zamiast callbacks. W kontekście używania callbacks, funkcja sendResponse() jest uznawana za ważną tylko wtedy, gdy zostanie wywołana bezpośrednio w kontekście synchronicznym albo gdy handler zdarzenia sygnalizuje operację asynchroniczną poprzez zwrócenie true. Jeśli żaden z handlerów nie zwróci true lub jeśli funkcja sendResponse() zostanie usunięta z pamięci (garbage-collected), callback powiązany z sendMessage() zostanie domyślnie wywołany.

Native Messaging

Rozszerzenia przeglądarki umożliwiają również komunikację z binaries in the system via stdin. Aplikacja musi zainstalować plik json wskazujący to w formacie json podobnym do:

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

Gdzie name to string przekazywany do runtime.connectNative() lub runtime.sendNativeMessage() w celu komunikacji z aplikacją z background scripts rozszerzenia przeglądarki. path to ścieżka do binarki, istnieje tylko 1 poprawny type — stdio (używa stdin i stdout), a allowed_origins wskazują rozszerzenia, które mogą uzyskać do niego dostęp (i nie mogą zawierać wildcard).

Chrome/Chromium będzie szukać tego json w rejestrze Windows oraz w niektórych ścieżkach w macOS i Linux (więcej informacji w docs).

tip

Rozszerzenie przeglądarki musi również zadeklarować uprawnienie nativeMessaing, aby móc korzystać z tej komunikacji.

Poniżej przykład kodu background script wysyłającego wiadomości do native application:

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

In this blog post, zaproponowano podatny wzorzec wykorzystujący native messages:

  1. Browser extension ma wzorzec wildcard dla content script.
  2. Content script przekazuje wiadomości postMessage do background script używając sendMessage.
  3. Background script przekazuje wiadomość do native application używając sendNativeMessage.
  4. Native application niebezpiecznie obsługuje wiadomość, co prowadzi do code execution.

W nim znajduje się przykład wyjaśniający, jak z dowolnej strony przejść do RCE wykorzystując browser extension.

Sensitive Information in Memory/Code/Clipboard

Jeśli Browser Extension przechowuje wrażliwe informacje w swojej pamięci, mogą one zostać zrzucane (szczególnie na maszynach z Windows) i przeszukane w celu odnalezienia tych informacji.

Dlatego pamięć Browser Extension nie powinna być uważana za bezpieczną, a wrażliwe informacje takie jak dane uwierzytelniające czy mnemonic phrases nie powinny być w niej przechowywane.

Oczywiście, nie umieszczaj wrażliwych informacji w kodzie, ponieważ będzie on publiczny.

Aby zrzucić pamięć z przeglądarki możesz zrzucić pamięć procesu lub przejść do ustawień rozszerzenia przeglądarki, kliknąć Inspect pop-up -> w sekcji Memory -> Take a snaphost i użyć CTRL+F, aby wyszukać w snapshotcie wrażliwe informacje.

Co więcej, wysoce wrażliwe informacje jak mnemonic keys czy hasła nie powinny być możliwe do skopiowania do clipboard (albo przynajmniej usuń je ze schowka po kilku sekundach), ponieważ procesy monitorujące clipboard będą mogły je przechwycić.

Loading an Extension in the Browser

  1. Download the Browser Extension & unzipped
  2. Go to chrome://extensions/ and enable the Developer Mode
  3. Click the Load unpacked button

W Firefox przejdź do about:debugging#/runtime/this-firefox i kliknij przycisk Load Temporary Add-on.

Getting the source code from the store

Kod źródłowy rozszerzenia Chrome można uzyskać na kilka sposobów. Poniżej znajdują się szczegółowe wyjaśnienia i instrukcje dla każdej opcji.

Download Extension as ZIP via Command Line

Kod źródłowy rozszerzenia Chrome można pobrać jako plik ZIP przy użyciu wiersza poleceń. Polega to na użyciu curl do pobrania pliku ZIP z określonego URL, a następnie rozpakowaniu zawartości ZIP do katalogu. Oto kroki:

  1. Zastąp "extension_id" rzeczywistym ID rozszerzenia.
  2. Wykonaj następujące polecenia:
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"

Użyj strony CRX Viewer

https://robwu.nl/crxviewer/

Użyj rozszerzenia CRX Viewer

Inną wygodną metodą jest użycie Chrome Extension Source Viewer, który jest projektem otwartoźródłowym. Można go zainstalować z Chrome Web Store. Kod źródłowy przeglądarki jest dostępny w jego GitHub repository.

Wyświetlanie źródła lokalnie zainstalowanego rozszerzenia

Lokalnie zainstalowane rozszerzenia Chrome można również przejrzeć. Oto jak:

  1. Uzyskaj dostęp do lokalnego katalogu profilu Chrome, odwiedzając chrome://version/ i znajdując pole "Profile Path".
  2. Przejdź do podfolderu Extensions/ w katalogu profilu.
  3. Ten folder zawiera wszystkie zainstalowane rozszerzenia, zazwyczaj z ich kodem źródłowym w czytelnym formacie.

Aby zidentyfikować rozszerzenia, możesz odwzorować ich ID na nazwy:

  • Włącz Developer Mode na stronie about:extensions, aby zobaczyć ID każdego rozszerzenia.
  • W każdym folderze rozszerzenia plik manifest.json zawiera czytelne pole name, co pomaga zidentyfikować rozszerzenie.

Użyj archiwizera plików lub rozpakowywacza

Przejdź do Chrome Web Store i pobierz rozszerzenie. Plik będzie miał rozszerzenie .crx. Zmień rozszerzenie pliku z .crx na .zip. Użyj dowolnego archiwizera (np. WinRAR, 7-Zip itp.), aby wypakować zawartość pliku ZIP.

Użyj Developer Mode w Chrome

Otwórz Chrome i przejdź do chrome://extensions/. Włącz "Developer mode" w prawym górnym rogu. Kliknij na "Load unpacked extension...". Przejdź do katalogu z rozszerzeniem. To nie pobiera kodu źródłowego, ale jest przydatne do przeglądania i modyfikowania kodu już pobranego lub rozwijanego rozszerzenia.

Zbiór manifestów rozszerzeń Chrome

Aby spróbować odnaleźć podatne rozszerzenia przeglądarki, możesz użyć https://github.com/palant/chrome-extension-manifests-dataset i sprawdzić ich pliki manifestu w poszukiwaniu potencjalnych oznak podatności. Na przykład, aby sprawdzić rozszerzenia z ponad 25000 użytkowników, content_scripts i uprawnieniem nativeMessaing:

bash
# Query example from https://spaceraccoon.dev/universal-code-execution-browser-extensions/
node query.js -f "metadata.user_count > 250000" "manifest.content_scripts?.length > 0 && manifest.permissions?.includes('nativeMessaging')"

Post-exploitation: Forced extension load & persistence (Windows)

Stealthy technika pozwalająca wstawić backdoor do Chromium poprzez bezpośrednią edycję per-user Preferences i sfałszowanie prawidłowych HMACs, co powoduje, że przeglądarka zaakceptuje i aktywuje dowolne unpacked extension bez monitów ani flag.

Forced Extension Load Preferences Mac Forgery Windows

Security Audit Checklist

Mimo że Browser Extensions mają ograniczoną powierzchnię ataku, niektóre z nich mogą zawierać vulnerabilities lub możliwości wzmocnienia. Poniższe punkty są najczęściej spotykane:

  • 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
  • If any communication occurs from the extension to the web page, check for XSS vulnerabilities caused in the communication.
  • If Post Messages are used, check for Post Message vulnerabilities.
  • 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/ analyzes some data like the permissions browser extension requests to give a risk level of using the browser extension.

Tools

Tarnish

  • Pulls any Chrome extension from a provided Chrome webstore link.
  • manifest.json viewer: simply displays a JSON-prettified version of the extension’s manifest.
  • Fingerprint Analysis: Detection of web_accessible_resources and automatic generation of Chrome extension fingerprinting JavaScript.
  • Potential Clickjacking Analysis: Detection of extension HTML pages with the 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 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 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

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.

References

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks