44 KiB
Metodologia Pentestingu Rozszerzeń Przeglądarki
{% hint style="success" %}
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)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na GitHubie.
Podstawowe Informacje
Rozszerzenia przeglądarki są napisane w JavaScript i ładowane przez przeglądarkę w tle. Mają swój DOM, ale mogą wchodzić w interakcje z DOM-ami innych stron. Oznacza to, że mogą naruszać poufność, integralność i dostępność (CIA) innych stron.
Główne Komponenty
Układy rozszerzeń wyglądają najlepiej, gdy są wizualizowane i składają się z trzech komponentów. Przyjrzyjmy się każdemu komponentowi dokładniej.
Skrypty Treści
Każdy skrypt treści ma bezpośredni dostęp do DOM pojedynczej strony internetowej i jest narażony na potencjalnie złośliwe dane wejściowe. Jednak skrypt treści nie ma żadnych uprawnień poza możliwością wysyłania wiadomości do rdzenia rozszerzenia.
Rdzeń Rozszerzenia
Rdzeń rozszerzenia zawiera większość uprawnień/dostępu rozszerzenia, ale rdzeń rozszerzenia może wchodzić w interakcje z treścią internetową tylko za pośrednictwem XMLHttpRequest i skryptów treści. Ponadto rdzeń rozszerzenia nie ma bezpośredniego dostępu do maszyny gospodarza.
Natychmiastowy Plik Binarny
Rozszerzenie pozwala na natychmiastowy plik binarny, który może uzyskać dostęp do maszyny gospodarza z pełnymi uprawnieniami użytkownika. Natychmiastowy plik binarny wchodzi w interakcje z rdzeniem rozszerzenia za pośrednictwem standardowego interfejsu programowania aplikacji Netscape Plugin (NPAPI), używanego przez Flash i inne wtyczki przeglądarki.
Granice
{% hint style="danger" %} Aby uzyskać pełne uprawnienia użytkownika, atakujący musi przekonać rozszerzenie do przekazania złośliwych danych wejściowych ze skryptu treści do rdzenia rozszerzenia, a następnie z rdzenia rozszerzenia do natychmiastowego pliku binarnego. {% endhint %}
Każdy komponent rozszerzenia jest oddzielony od siebie przez silne granice ochronne. Każdy komponent działa w osobnym procesie systemu operacyjnego. Skrypty treści i rdzenie rozszerzeń działają w procesach piaskownicy, niedostępnych dla większości usług systemu operacyjnego.
Co więcej, skrypty treści są oddzielone od swoich powiązanych stron internetowych przez działanie w osobnym stosie JavaScript. Skrypt treści i strona internetowa mają dostęp do tego samego podstawowego 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 .crx file extension. Rdzeń rozszerzenia to plik manifest.json
w katalogu głównym folderu, który określa układ, uprawnienia i inne opcje konfiguracyjne.
Przykład:
{
"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 nawiguje do pasującej strony, w naszym przypadku każda strona pasująca do wyrażenia https://example.com/*
i niepasująca do wyrażenia regex *://*/*/business*
. Wykonują się jak własne skrypty strony i mają dowolny dostęp do Modelu Obiektów Dokumentu (DOM) strony.
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],
Aby dodać lub wykluczyć więcej adresów URL, można również użyć include_globs
i exclude_globs
.
To jest przykład skryptu zawartości, który doda przycisk wyjaśnienia do strony, gdy API storage zostanie użyte do pobrania wartości message
z pamięci rozszerzenia.
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);
});
Wiadomość jest wysyłana do stron rozszerzenia przez skrypt treści, gdy ten przycisk jest klikany, poprzez wykorzystanie runtime.sendMessage() API. Wynika to z ograniczenia skryptu treści w bezpośrednim dostępie do API, przy czym storage
jest jednym z nielicznych wyjątków. Dla funkcjonalności wykraczających poza te wyjątki, wiadomości są wysyłane do stron rozszerzenia, z którymi skrypty treści mogą komunikować się.
{% hint style="warning" %}
W zależności od przeglądarki, możliwości skryptu treści mogą się nieco różnić. Dla przeglądarek opartych na Chromium, lista możliwości jest dostępna w dokumentacji Chrome Developers, a dla Firefox, MDN służy jako główne źródło.
Warto również zauważyć, że skrypty treści mają możliwość komunikacji z skryptami w tle, co umożliwia im wykonywanie działań i przekazywanie odpowiedzi z powrotem.
{% endhint %}
Aby wyświetlić i debugować skrypty treści w Chrome, menu narzędzi dewelopera można otworzyć z Opcje > Więcej narzędzi > Narzędzia dewelopera LUB naciskając Ctrl + Shift + I.
Po wyświetleniu narzędzi dewelopera, należy kliknąć na zakładkę Źródło, a następnie na zakładkę Skrypty treści. Umożliwia to obserwację działających skryptów treści z różnych rozszerzeń oraz ustawienie punktów przerwania w celu śledzenia przepływu wykonania.
Wstrzyknięte skrypty treści
{% hint style="success" %}
Zauważ, że Skrypty Treści nie są obowiązkowe, ponieważ możliwe jest również dynamiczne wstrzykiwanie skryptów oraz programowe wstrzykiwanie ich na stronach internetowych za pomocą tabs.executeScript
. To w rzeczywistości zapewnia bardziej szczegółową kontrolę.
{% endhint %}
Aby programowo wstrzyknąć skrypt treści, rozszerzenie musi mieć uprawnienia hosta dla strony, do której skrypty mają być wstrzykiwane. Uprawnienia te mogą być zabezpieczone albo przez zażądanie ich w manifeście rozszerzenia, albo tymczasowo poprzez activeTab.
Przykład rozszerzenia opartego na activeTab
{% code title="manifest.json" %}
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
{% endcode %}
- Wstrzyknij plik JS po kliknięciu:
// 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ę po kliknięciu:
//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 skryptów
// 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 dodać lub wykluczyć więcej adresów URL, można również użyć include_globs
i exclude_globs
.
Skrypty zawartości run_at
Pole run_at
kontroluje kiedy pliki JavaScript są wstrzykiwane do strony internetowej. Preferowana i domyślna wartość to "document_idle"
.
Możliwe wartości to:
document_idle
: Kiedy tylko to możliwedocument_start
: Po załadowaniu jakichkolwiek plików zcss
, ale przed skonstruowaniem jakiegokolwiek innego DOM lub uruchomieniem jakiegokolwiek innego skryptu.document_end
: Natychmiast po zakończeniu DOM, ale przed załadowaniem subzasobów, takich jak obrazy i ramki.
Poprzez manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
Via service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.example.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
background
Wiadomości wysyłane przez skrypty zawartości są odbierane przez stronę tła, która odgrywa centralną rolę w koordynowaniu komponentów rozszerzenia. Co ważne, strona tła utrzymuje się przez cały czas życia rozszerzenia, działając dyskretnie bez bezpośredniej interakcji użytkownika. Posiada własny Model Obiektów Dokumentu (DOM), co umożliwia złożone interakcje i zarządzanie stanem.
Kluczowe punkty:
- Rola strony tła: Działa jako centrum nerwowe dla rozszerzenia, zapewniając komunikację i koordynację między różnymi częściami rozszerzenia.
- Trwałość: To zawsze obecny byt, niewidoczny dla użytkownika, ale integralny dla funkcjonalności rozszerzenia.
- Automatyczne generowanie: Jeśli nie jest wyraźnie zdefiniowane, przeglądarka automatycznie utworzy stronę tła. Ta automatycznie generowana strona będzie zawierać wszystkie skrypty tła określone w manifeście rozszerzenia, zapewniając płynne działanie zadań tła rozszerzenia.
{% hint style="success" %} Wygoda zapewniana przez przeglądarkę w automatycznym generowaniu strony tła (gdy nie jest wyraźnie zadeklarowana) zapewnia, że wszystkie niezbędne skrypty tła są zintegrowane i działają, upraszczając proces konfiguracji rozszerzenia. {% endhint %}
Przykładowy skrypt tła:
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 otrzymana zostanie wiadomość "explain"
, używa tabs API do otwarcia strony w nowej karcie.
Aby debugować skrypt w tle, możesz przejść do szczegółów rozszerzenia i zbadać serwis worker, co otworzy narzędzia deweloperskie z skryptem w tle:
Strony opcji i inne
Rozszerzenia przeglądarki mogą zawierać różne rodzaje stron:
- Strony akcji są wyświetlane w rozwijanym menu po kliknięciu na ikonę rozszerzenia.
- Strony, które rozszerzenie załaduje w nowej karcie.
- Strony opcji: Ta strona wyświetla się na górze rozszerzenia po kliknięciu. W poprzednim manifeście w moim przypadku mogłem uzyskać dostęp do tej strony w
chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca
lub klikając:
Zauważ, że te strony nie są trwałe jak strony w tle, ponieważ ładują dynamicznie treści w zależności od potrzeb. Mimo to, dzielą pewne możliwości z stroną w tle:
- Komunikacja z skryptami treści: Podobnie jak strona w tle, te strony mogą otrzymywać wiadomości od skryptów treści, co ułatwia interakcję w ramach rozszerzenia.
- Dostęp do specyficznych API rozszerzenia: Te strony mają pełny dostęp do specyficznych API rozszerzenia, w zależności od uprawnień zdefiniowanych dla rozszerzenia.
permissions
& host_permissions
permissions
i host_permissions
to wpisy z manifest.json
, które wskazują jakie uprawnienia ma rozszerzenie przeglądarki (przechowywanie, lokalizacja...) oraz na jakich stronach internetowych.
Ponieważ rozszerzenia przeglądarki mogą być tak uprzywilejowane, złośliwe lub skompromitowane mogłyby 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ą być nadużywane w:
{% content-ref url="browext-permissions-and-host_permissions.md" %} browext-permissions-and-host_permissions.md {% endcontent-ref %}
content_security_policy
Polityka bezpieczeństwa treści może być również zadeklarowana w manifest.json
. Jeśli jest zdefiniowana, może być wrażliwa.
Domyślne ustawienie dla stron rozszerzeń przeglądarki jest dość restrykcyjne:
script-src 'self'; object-src 'self';
Aby strona internetowa mogła uzyskać dostęp do strony rozszerzenia przeglądarki, na przykład strony .html
, ta strona musi być wymieniona w polu web_accessible_resources
pliku manifest.json
.
Na przykład:
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Te strony są dostępne pod adresem URL jak:
chrome-extension://<extension-id>/message.html
W publicznych rozszerzeniach extension-id jest dostępny:
Jednakże, jeśli parametr manifest.json
use_dynamic_url
jest używany, to id może być dynamiczne.
{% hint style="success" %} Zauważ, że nawet jeśli strona jest tutaj wymieniona, może być chroniona przed ClickJacking dzięki Content Security Policy. Dlatego musisz to również sprawdzić (sekcja frame-ancestors) przed potwierdzeniem, że atak ClickJacking jest możliwy. {% endhint %}
Dopuszczenie dostępu do tych stron sprawia, że te strony są potencjalnie podatne na ClickJacking:
{% content-ref url="browext-clickjacking.md" %} browext-clickjacking.md {% endcontent-ref %}
{% hint style="success" %} Zezwolenie na ładowanie tych stron tylko przez rozszerzenie, a nie przez losowe adresy URL, może zapobiec atakom ClickJacking. {% endhint %}
{% hint style="danger" %}
Zauważ, że strony z web_accessible_resources
oraz inne strony rozszerzenia są również zdolne do kontaktowania się z skryptami w tle. Jeśli jedna z tych stron jest podatna na XSS, może to otworzyć większą lukę.
Ponadto, zauważ, że możesz otworzyć tylko strony wskazane w web_accessible_resources
wewnątrz iframe, ale z nowej karty możliwe jest uzyskanie dostępu do dowolnej strony w rozszerzeniu, znając ID rozszerzenia. Dlatego, jeśli XSS zostanie znalezione, wykorzystując te same parametry, może być nadużyte, nawet jeśli strona nie jest skonfigurowana w web_accessible_resources
.
{% endhint %}
externally_connectable
Zgodnie z dokumentacją, właściwość manifestu "externally_connectable"
deklaruje które rozszerzenia i strony internetowe mogą łączyć się z Twoim rozszerzeniem za pomocą runtime.connect i runtime.sendMessage.
- Jeśli klucz
externally_connectable
nie jest zadeklarowany w manifeście Twojego rozszerzenia lub jest zadeklarowany jako"ids": ["*"]
, wszystkie rozszerzenia mogą się łączyć, ale żadne strony internetowe nie mogą się łączyć. - Jeśli określone ID są podane, jak w
"ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
, tylko te aplikacje mogą się łączyć. - Jeśli podano dopasowania, te aplikacje webowe będą mogły się łączyć:
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
- Jeśli jest określone jako puste:
"externally_connectable": {}
, żadna aplikacja ani strona internetowa nie będą mogły się połączyć.
Im mniej rozszerzeń i adresów URL wskazanych tutaj, tym mniejsza powierzchnia ataku.
{% hint style="danger" %}
Jeśli strona internetowa wrażliwa na XSS lub przejęcie jest wskazana w externally_connectable
, atakujący będzie mógł wysyłać wiadomości bezpośrednio do skryptu w tle, całkowicie omijając skrypt treści i jego CSP.
Dlatego jest to bardzo potężne obejście.
Co więcej, jeśli klient zainstaluje złośliwe rozszerzenie, nawet jeśli nie jest dozwolone do komunikacji z wrażliwym rozszerzeniem, może wstrzyknąć dane XSS w dozwolonej stronie internetowej lub nadużyć WebRequest
lub DeclarativeNetRequest
API, aby manipulować żądaniami na docelowej domenie, zmieniając żądanie strony dla pliku JavaScript. (Zauważ, że CSP na docelowej stronie może zapobiec tym atakom). Ten pomysł pochodzi z tego opisu.
{% endhint %}
Podsumowanie komunikacji
Rozszerzenie <--> WebApp
Aby komunikować się między skryptem treści a stroną internetową, zazwyczaj używane są wiadomości post. Dlatego w aplikacji internetowej zazwyczaj znajdziesz wywołania funkcji window.postMessage
oraz w skrypcie treści nasłuchiwacze, takie jak window.addEventListener
. Należy jednak zauważyć, że rozszerzenie może również komunikować się z aplikacją internetową, wysyłając wiadomość Post (a zatem strona powinna się tego spodziewać) lub po prostu sprawić, by strona załadowała nowy skrypt.
Wewnątrz rozszerzenia
Zazwyczaj funkcja chrome.runtime.sendMessage
jest używana do wysyłania wiadomości wewnątrz rozszerzenia (zazwyczaj obsługiwana przez skrypt background
), a aby ją odebrać i obsłużyć, deklarowany jest nasłuchiwacz wywołujący chrome.runtime.onMessage.addListener
.
Możliwe jest również użycie chrome.runtime.connect()
, aby mieć stałe połączenie zamiast wysyłania pojedynczych 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 });
</details>
Możliwe jest również wysyłanie wiadomości z skryptu w tle do skryptu treści znajdującego się w określonej karcie, wywołując **`chrome.tabs.sendMessage`**, gdzie będziesz musiał wskazać **ID karty**, do której chcesz wysłać wiadomość.
### Z dozwolonego `externally_connectable` do rozszerzenia
**Aplikacje internetowe i zewnętrzne rozszerzenia przeglądarki dozwolone** w konfiguracji `externally_connectable` mogą wysyłać żądania za pomocą:
```javascript
chrome.runtime.sendMessage(extensionId, ...
Gdzie to konieczne, aby wspomnieć o identyfikatorze rozszerzenia.
Native Messaging
Możliwe jest, aby skrypty w tle komunikowały się z binariami w systemie, które mogą być narażone na krytyczne luki, takie jak RCE jeśli ta komunikacja nie jest odpowiednio zabezpieczona. Więcej na ten temat później.
chrome.runtime.sendNativeMessage(
'com.my_company.my_application',
{text: 'Hello'},
function (response) {
console.log('Received ' + response);
}
);
Web ↔︎ Komunikacja Skryptów Treści
Środowiska, w których działają skrypty treści, oraz miejsca hosta są oddzielone od siebie, co zapewnia izolację. Pomimo tej izolacji, oba mają zdolność interakcji z Model Obiektów Dokumentu (DOM) strony, wspólnym zasobem. Aby strona hosta mogła nawiązać komunikację z skryptem treści, lub pośrednio z rozszerzeniem przez skrypt treści, konieczne jest wykorzystanie DOM, który jest dostępny dla obu stron jako kanał komunikacyjny.
Wiadomości Post
{% code title="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);
{% endcode %}
{% code title="example.js" %}
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
{% endcode %}
Bezpieczna komunikacja Post Message powinna sprawdzać autentyczność otrzymanej wiadomości, co można zrobić, sprawdzając:
event.isTrusted
: To jest True tylko wtedy, gdy zdarzenie zostało wywołane przez akcję użytkownika- Skrypt treści może oczekiwać wiadomości tylko wtedy, gdy użytkownik wykona jakąś akcję
- domena źródłowa: może oczekiwać wiadomości tylko z dozwolonej listy domen.
- Jeśli używana jest wyrażenie regularne, bądź bardzo ostrożny
- Źródło:
received_message.source !== window
może być użyte do sprawdzenia, czy wiadomość była z tego samego okna, w którym skrypt treści nasłuchuje.
Poprzednie kontrole, nawet jeśli są przeprowadzane, mogą być podatne, więc sprawdź na następującej stronie potencjalne obejścia Post Message:
{% content-ref url="../postmessage-vulnerabilities/" %} postmessage-vulnerabilities {% endcontent-ref %}
Iframe
Innym możliwym sposobem komunikacji mogą być adresy URL Iframe, przykład można znaleźć w:
{% content-ref url="browext-xss-example.md" %} browext-xss-example.md {% endcontent-ref %}
DOM
To nie jest "dokładnie" sposób komunikacji, ale sieć i skrypt treści będą miały dostęp do DOM sieci. Więc, jeśli skrypt treści odczytuje jakieś informacje z niego, ufając DOM sieci, sieć mogłaby zmodyfikować te dane (ponieważ sieć nie powinna być ufana, lub ponieważ sieć jest podatna na XSS) i skompromentować skrypt treści.
Możesz również znaleźć przykład XSS opartego na DOM, aby skompromitować rozszerzenie przeglądarki w:
{% content-ref url="browext-xss-example.md" %} browext-xss-example.md {% endcontent-ref %}
Komunikacja Skryptu Treści ↔︎ Skryptu Tła
Skrypt treści może używać funkcji runtime.sendMessage() lub tabs.sendMessage(), aby wysłać jednorazową wiadomość serializowalną w formacie JSON.
Aby obsłużyć odpowiedź, użyj zwróconego Promise. Chociaż, dla zachowania zgodności wstecznej, nadal możesz przekazać callback jako ostatni argument.
Wysyłanie żądania z skryptu treści wygląda tak:
(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 rozszerzenia (zwykle skryptu w tle). Przykład, jak wysłać wiadomość do skryptu treści w wybranej karcie:
// 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 odbierającej stronie musisz ustawić runtime.onMessage nasłuchiwacz zdarzeń, aby obsłużyć wiadomość. Wygląda to tak samo z poziomu skryptu treści lub strony rozszerzenia.
// 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ł wykonany w sposób synchroniczny. Aby zmodyfikować obsługę zdarzenia onMessage
do asynchronicznego wykonania sendResponse()
, konieczne jest dodanie return true;
.
Ważnym rozważeniem jest to, że w scenariuszach, w których wiele stron ma odbierać zdarzenia onMessage
, pierwsza strona, która wykona sendResponse()
dla konkretnego zdarzenia, będzie jedyną, która skutecznie dostarczy odpowiedź. Jakiekolwiek kolejne odpowiedzi na to samo zdarzenie nie będą brane pod uwagę.
Podczas tworzenia nowych rozszerzeń, preferencje powinny być skierowane ku obietnicom zamiast do callbacków. Jeśli chodzi o użycie callbacków, funkcja sendResponse()
jest uznawana za ważną tylko wtedy, gdy jest wykonywana bezpośrednio w kontekście synchronicznym lub jeśli obsługa zdarzenia wskazuje na operację asynchroniczną, zwracając true
. Jeśli żaden z handlerów nie zwróci true
lub jeśli funkcja sendResponse()
zostanie usunięta z pamięci (zbierana przez garbage collector), callback związany z funkcją sendMessage()
zostanie wywołany domyślnie.
Native Messaging
Rozszerzenia przeglądarki umożliwiają również komunikację z binariami w systemie za pomocą stdin. Aplikacja musi zainstalować plik json wskazujący to w formacie json jak:
{
"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 ciąg przekazywany do runtime.connectNative()
lub runtime.sendNativeMessage()
w celu komunikacji z aplikacją z tła skryptów rozszerzenia przeglądarki. path
to ścieżka do pliku binarnego, istnieje tylko 1 ważny type
, którym jest stdio (użyj stdin i stdout), a allowed_origins
wskazuje rozszerzenia, które mogą uzyskać do niego dostęp (i nie mogą mieć znaku wieloznacznego).
Chrome/Chromium będzie szukać tego json w niektórych rejestrach systemu Windows oraz w niektórych ścieżkach w macOS i Linux (więcej informacji w dokumentacji).
{% hint style="success" %}
Rozszerzenie przeglądarki również potrzebuje uprawnienia nativeMessaing
zadeklarowanego, aby móc korzystać z tej komunikacji.
{% endhint %}
Tak wygląda kod niektórego skryptu tła wysyłającego wiadomości do aplikacji natywnej:
chrome.runtime.sendNativeMessage(
'com.my_company.my_application',
{text: 'Hello'},
function (response) {
console.log('Received ' + response);
}
);
W tym wpisie na blogu zaproponowano podatny wzór wykorzystujący natywne wiadomości:
- Rozszerzenie przeglądarki ma wzór z użyciem symbolu wieloznacznego dla skryptu treści.
- Skrypt treści przesyła wiadomości
postMessage
do skryptu w tle za pomocąsendMessage
. - Skrypt w tle przesyła wiadomość do aplikacji natywnej za pomocą
sendNativeMessage
. - Aplikacja natywna niebezpiecznie obsługuje wiadomość, co prowadzi do wykonania kodu.
A w jego wnętrzu wyjaśniono przykład przechodzenia z dowolnej strony do RCE wykorzystując rozszerzenie przeglądarki.
Wrażliwe informacje w pamięci/kodzie/clipboard
Jeśli Rozszerzenie Przeglądarki przechowuje wrażliwe informacje w swojej pamięci, mogą one być zrzucane (szczególnie na maszynach z systemem Windows) i wyszukiwane w tych informacjach.
Dlatego pamięć Rozszerzenia Przeglądarki nie powinna być uważana za bezpieczną, a wrażliwe informacje, takie jak dane logowania czy frazy mnemoniczne, nie powinny być przechowywane.
Oczywiście, nie umieszczaj wrażliwych informacji w kodzie, ponieważ będą one publiczne.
Aby zrzucić pamięć z przeglądarki, możesz zrzucić pamięć procesu lub przejść do ustawień rozszerzenia przeglądarki, klikając Inspect pop-up
-> W sekcji Memory
-> Take a snapshot
i CTRL+F
, aby wyszukać w zrzucie wrażliwe informacje.
Ponadto, wysoce wrażliwe informacje, takie jak klucze mnemoniczne czy hasła, nie powinny mieć możliwości kopiowania do schowka (lub przynajmniej powinny być usuwane ze schowka w ciągu kilku sekund), ponieważ wtedy procesy monitorujące schowek będą mogły je uzyskać.
Ładowanie rozszerzenia w przeglądarce
- Pobierz Rozszerzenie Przeglądarki i rozpakuj je.
- Przejdź do
chrome://extensions/
i włączTryb dewelopera
. - Kliknij przycisk
Load unpacked
.
W Firefoxie przejdź do about:debugging#/runtime/this-firefox
i kliknij przycisk Load Temporary Add-on
.
Uzyskiwanie kodu źródłowego ze sklepu
Kod źródłowy rozszerzenia Chrome można uzyskać na różne sposoby. Poniżej znajdują się szczegółowe wyjaśnienia i instrukcje dla każdej opcji.
Pobierz rozszerzenie jako ZIP za pomocą wiersza poleceń
Kod źródłowy rozszerzenia Chrome można pobrać jako plik ZIP za pomocą wiersza poleceń. Wymaga to użycia curl
, aby pobrać plik ZIP z określonego adresu URL, a następnie wyodrębnić zawartość pliku ZIP do katalogu. Oto kroki:
- Zastąp
"extension_id"
rzeczywistym ID rozszerzenia. - Wykonaj następujące polecenia:
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
Użyj rozszerzenia CRX Viewer
Inną wygodną metodą jest użycie Chrome Extension Source Viewer, który jest projektem open-source. Można go zainstalować z Chrome Web Store. Kod źródłowy przeglądarki jest dostępny w jej repozytorium GitHub.
Wyświetl źródło lokalnie zainstalowanego rozszerzenia
Rozszerzenia Chrome zainstalowane lokalnie można również sprawdzić. Oto jak:
- Uzyskaj dostęp do lokalnego katalogu profilu Chrome, odwiedzając
chrome://version/
i lokalizując pole "Profile Path". - Przejdź do podfolderu
Extensions/
w katalogu profilu. - Ten folder zawiera wszystkie zainstalowane rozszerzenia, zazwyczaj z ich kodem źródłowym w czytelnym formacie.
Aby zidentyfikować rozszerzenia, możesz powiązać ich identyfikatory z nazwami:
- Włącz tryb dewelopera na stronie
about:extensions
, aby zobaczyć identyfikatory każdego rozszerzenia. - W każdym folderze rozszerzenia plik
manifest.json
zawiera czytelne polename
, co pomaga w identyfikacji rozszerzenia.
Użyj archiwizera plików lub dekompresora
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 plików (takiego jak WinRAR, 7-Zip itp.), aby wyodrębnić zawartość pliku ZIP.
Użyj trybu dewelopera w Chrome
Otwórz Chrome i przejdź do chrome://extensions/
. Włącz "Tryb dewelopera" w prawym górnym rogu. Kliknij "Załaduj rozpakowane rozszerzenie...". Przejdź do katalogu swojego rozszerzenia. To nie pobiera kodu źródłowego, ale jest przydatne do przeglądania i modyfikowania kodu już pobranego lub opracowanego rozszerzenia.
Zbiór manifestów rozszerzeń Chrome
Aby spróbować zidentyfikować podatne rozszerzenia przeglądarki, możesz użyć https://github.com/palant/chrome-extension-manifests-dataset i sprawdzić ich pliki manifestów pod kątem potencjalnych oznak podatności. Na przykład, aby sprawdzić rozszerzenia z więcej niż 25000 użytkowników, content_scripts
i uprawnienie nativeMessaing
:
{% code overflow="wrap" %}
# 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')"
{% endcode %}
Lista kontrolna audytu bezpieczeństwa
Mimo że rozszerzenia przeglądarki mają ograniczoną powierzchnię ataku, niektóre z nich mogą zawierać luki lub możliwości wzmocnienia. Oto najczęstsze z nich:
- Ogranicz jak najbardziej wymagane
permissions
- Ogranicz jak najbardziej
host_permissions
- Użyj silnej
content_security_policy
- Ogranicz jak najbardziej
externally_connectable
, jeśli nie jest potrzebne i możliwe, nie zostawiaj domyślnie, określ{}
- Jeśli URL podatny na XSS lub przejęcie jest tutaj wymieniony, atakujący będzie mógł wysyłać wiadomości do skryptów w tle bezpośrednio. Bardzo potężne obejście.
- Ogranicz jak najbardziej
web_accessible_resources
, nawet puste, jeśli to możliwe. - Jeśli
web_accessible_resources
nie jest puste, sprawdź ClickJacking - Jeśli jakakolwiek komunikacja zachodzi z rozszerzenia do strony internetowej, sprawdź XSS luki spowodowane w komunikacji.
- Jeśli używane są Post Messages, sprawdź luki w Post Message.
- Jeśli Content Script ma dostęp do szczegółów DOM, sprawdź, czy nie wprowadza XSS, jeśli zostanie zmodyfikowany przez stronę internetową.
- Zwróć szczególną uwagę, jeśli ta komunikacja jest również zaangażowana w komunikację Content Script -> skrypt w tle
- Jeśli skrypt w tle komunikuje się za pomocą native messaging, sprawdź, czy komunikacja jest bezpieczna i oczyszczona
- Wrażliwe informacje nie powinny być przechowywane wewnątrz kodu rozszerzenia przeglądarki
- Wrażliwe informacje nie powinny być przechowywane wewnątrz pamięci rozszerzenia przeglądarki
- Wrażliwe informacje nie powinny być przechowywane w systemie plików bez ochrony
Narzędzia
Tarnish
- Pobiera dowolne rozszerzenie Chrome z podanego linku do sklepu Chrome.
- manifest.json widok: po prostu wyświetla wersję JSON w formacie prettified manifestu rozszerzenia.
- Analiza odcisków palców: Wykrywanie web_accessible_resources i automatyczne generowanie JavaScript do odcisków palców rozszerzenia Chrome.
- Potencjalna analiza Clickjacking: Wykrywanie stron HTML rozszerzenia z ustawioną dyrektywą web_accessible_resources. Te mogą być potencjalnie podatne na clickjacking w zależności od celu stron.
- Widok ostrzeżeń o uprawnieniach: który pokazuje listę wszystkich ostrzeżeń o uprawnieniach Chrome, które będą wyświetlane po próbie zainstalowania rozszerzenia przez użytkownika.
- Niebezpieczna funkcja: pokazuje lokalizację niebezpiecznych funkcji, które mogą być potencjalnie wykorzystywane przez atakującego (np. funkcje takie jak innerHTML, chrome.tabs.executeScript).
- Punkty wejścia: pokazuje, gdzie rozszerzenie przyjmuje dane wejściowe od użytkownika/zewnętrzne. To jest przydatne do zrozumienia powierzchni rozszerzenia i szukania potencjalnych punktów do wysyłania złośliwie skonstruowanych danych do rozszerzenia.
- Zarówno skanery Niebezpiecznych Funkcji, jak i Punktów Wejścia mają następujące dla swoich wygenerowanych alertów:
- Odpowiedni fragment kodu i linia, która spowodowała alert.
- Opis problemu.
- Przycisk „Zobacz plik”, aby zobaczyć pełny plik źródłowy zawierający kod.
- Ścieżka alertowanego pliku.
- Pełny URI rozszerzenia Chrome alertowanego pliku.
- Typ pliku, taki jak skrypt strony w tle, skrypt treści, akcja przeglądarki itp.
- Jeśli podatna linia znajduje się w pliku JavaScript, ścieżki wszystkich stron, w których jest zawarta, a także typ tych stron oraz status web_accessible_resource.
- Analizator polityki bezpieczeństwa treści (CSP) i sprawdzacz obejść: To wskaże słabości w CSP twojego rozszerzenia i również oświetli wszelkie potencjalne sposoby obejścia twojego CSP z powodu białych list CDN itp.
- Znane podatne biblioteki: To wykorzystuje Retire.js do sprawdzenia użycia znanych podatnych bibliotek JavaScript.
- Pobierz rozszerzenie i sformatowane wersje.
- Pobierz oryginalne rozszerzenie.
- Pobierz wersję rozszerzenia w formacie beautified (automatycznie sformatowany HTML i JavaScript).
- Automatyczne buforowanie wyników skanowania, uruchomienie skanowania rozszerzenia zajmie sporo czasu przy pierwszym uruchomieniu. Jednak przy drugim uruchomieniu, zakładając, że rozszerzenie nie zostało zaktualizowane, będzie prawie natychmiastowe dzięki buforowanym wynikom.
- Linkowalne adresy URL raportów, łatwo linkuj kogoś do raportu rozszerzenia wygenerowanego przez tarnish.
Neto
Projekt Neto to pakiet Pythona 3 stworzony do analizy i odkrywania ukrytych funkcji wtyczek i rozszerzeń przeglądarek dla znanych przeglądarek, takich jak Firefox i Chrome. Automatyzuje proces rozpakowywania spakowanych plików, aby wydobyć te funkcje z odpowiednich zasobów w rozszerzeniu, takich jak manifest.json
, foldery lokalizacyjne lub pliki źródłowe JavaScript i HTML.
Odniesienia
- Dzięki @naivenom za pomoc w tej metodologii
- 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/24/attack-surface-of-extension-pages/
- https://palant.info/2022/08/31/when-extension-pages-are-web-accessible/
- 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/mv2/background-pages
- https://thehackerblog.com/kicking-the-rims-a-guide-for-securely-writing-and-auditing-chrome-extensions/
- https://gist.github.com/LongJohnCoder/9ddf5735df3a4f2e9559665fb864eac0
{% hint style="success" %}
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)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się sztuczkami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.