Методологія pentesting браузерних розширень
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Базова інформація
Розширення браузера написані на JavaScript і завантажуються браузером у фоні. Вони мають свій DOM, але можуть взаємодіяти з DOM інших сайтів. Це означає, що вони можуть скомпрометувати конфіденційність, цілісність та доступність (CIA) інших сайтів.
Основні компоненти
Структуру розширення краще уявляти візуально — вона складається з трьох компонентів. Розглянемо кожен компонент детально.
 (1) (1).png)
Content Scripts
Кожен content script має прямий доступ до DOM однієї веб-сторінки і таким чином піддається потенційно шкідливому вводу. Проте content script не має дозволів, окрім можливості відправляти повідомлення до extension core.
Extension Core
Extension core містить більшість привілеїв/доступу розширення, але extension core може взаємодіяти з веб-контентом лише через XMLHttpRequest і content scripts. Також extension core не має прямого доступу до хост-машини.
Native Binary
Розширення може включати native binary, який може отримувати доступ до хост-машини з повними привілеями користувача. Native binary взаємодіє з extension core через стандартний Netscape Plugin Application Programming Interface (NPAPI), який використовувався Flash та іншими browser plug-ins.
Boundaries
Caution
Щоб отримати повні привілеї користувача, нападнику потрібно переконати розширення передати шкідливий ввід від content script до extension core і від extension core до native binary.
Кожен компонент розширення відокремлений один від одного сильними захисними межами. Кожен компонент запускається в окремому процесі операційної системи. Content scripts та extension cores працюють у sandbox processes, недоступних для більшості служб операційної системи.
Крім того, content scripts відокремлені від пов’язаних веб-сторінок тим, що запускаються в окремому JavaScript heap. Content script і веб-сторінка мають доступ до одного й того самого DOM, але вони ніколи не обмінюються JavaScript pointers, що запобігає leaking of JavaScript functionality.
manifest.json
Chrome extension — це просто ZIP-папка з .crx file extension. Ядром розширення є файл manifest.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
Контент-скрипти завантажуються щоразу, коли користувач переходить на відповідну сторінку, у нашому випадку будь-яка сторінка, що відповідає виразу https://example.com/* і не відповідає регулярному виразу *://*/*/business*. Вони виконуються як скрипти самої сторінки і мають довільний доступ до сторінки Document Object Model (DOM).
"content_scripts": [
{
"js": [
"script.js"
],
"matches": [
"https://example.com/*",
"https://www.example.com/*"
],
"exclude_matches": ["*://*/*business*"],
}
],
Щоб включити або виключити більше URL-адрес, також можна використовувати include_globs та exclude_globs.
Це приклад content script, який додасть на сторінку кнопку ‘Explain’, коли the storage API використовується для отримання значення message зі сховища розширення.
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)
})
.png)
Коли ця кнопка натискається, контент-скрипт надсилає повідомлення на сторінки розширення за допомогою runtime.sendMessage() API. Це зумовлено обмеженнями контент-скрипта у прямому доступі до API, причому storage — одна з небагатьох винятків. Для функціональностей, що виходять за межі цих винятків, повідомлення надсилаються на сторінки розширення, з якими контент-скрипти можуть взаємодіяти.
Warning
Залежно від браузера, можливості контент-скрипта можуть незначно відрізнятися. Для браузерів на базі Chromium список можливостей доступний у Chrome Developers documentation, а для Firefox основним джерелом є MDN.
Також варто зазначити, що контент-скрипти можуть спілкуватися з фонoвими скриптами, дозволяючи виконувати дії та передавати відповіді назад.
Щоб переглядати та відлагоджувати контент-скрипти в Chrome, меню Chrome developer tools можна відкрити через Options > More tools > Developer tools OR натиснувши Ctrl + Shift + I.
Після відкриття developer tools потрібно клікнути на вкладку Source, а потім на вкладку Content Scripts. Це дозволяє спостерігати виконувані контент-скрипти різних розширень та встановлювати breakpoints для відстеження потоку виконання.
Інжектовані контент-скрипти
Tip
Зверніть увагу, що Content Scripts aren’t mandatory — також можливо динамічно inject скрипти та programatically inject them на веб-сторінки через
tabs.executeScript. Це надає більш гранулярний контроль.
Для програмного інжектування контент-скрипта розширенню потрібні host permissions для сторінки, у яку будуть інжектуватися скрипти. Ці дозволи можна отримати або шляхом requesting them у manifest розширення, або тимчасово через activeTab.
Example activeTab-based extension
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
- Inject a JS file при натисканні:
// 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"],
})
})
- Інжектувати функцію при натисканні:
//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,
})
})
Приклад з scripting permissions
// 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" })
Щоб включити або виключити більше URL-адрес, також можна використовувати include_globs та exclude_globs.
Контентні скрипти run_at
Поле run_at керує тим, коли файли JavaScript вбудовуються у веб-сторінку. Бажане і значення за замовчуванням — "document_idle".
Можливі значення:
document_idle: Коли це можливоdocument_start: Після будь-яких файлів зcss, але перед тим, як буде побудований DOM або виконано будь-який інший скрипт.document_end: Негайно після завершення побудови DOM, але до того, як підресурси, наприклад зображення та фрейми, завантажаться.
Через manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
Через service-worker.js
chrome.scripting.registerContentScripts([
{
id: "test",
matches: ["https://*.example.com/*"],
runAt: "document_idle",
js: ["contentScript.js"],
},
])
background
Повідомлення, надіслані content scripts, отримує фонова сторінка, яка відіграє центральну роль у координуванні компонентів розширення. Варто зауважити, що фонова сторінка зберігається протягом усього життя розширення, працюючи непомітно без прямої взаємодії з користувачем. Вона має власну об’єктну модель документа (Document Object Model, DOM), що дозволяє реалізовувати складні взаємодії та управління станом.
Ключові моменти:
- Роль фонової сторінки: Виконує функцію центру зв’язку для розширення, забезпечуючи комунікацію та координацію між різними частинами розширення.
- Постійність: Це завжди присутній компонент, невидимий для користувача, але невід’ємний для функціонування розширення.
- Автоматичне створення: Якщо вона явно не визначена, браузер автоматично створить фонова сторінку. Ця авто-створена сторінка включатиме всі фонoві скрипти, зазначені в manifest розширення, забезпечуючи безперебійну роботу фонових завдань розширення.
Tip
Зручність, яку надає браузер шляхом автоматичного створення фонової сторінки (коли вона явно не оголошена), гарантує, що всі необхідні фонoві скрипти інтегровані та працюють, спрощуючи процес налаштування розширення.
Приклад фонового скрипта:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "explain") {
chrome.tabs.create({ url: "https://example.net/explanation" })
}
})
Він використовує runtime.onMessage API для прослуховування повідомлень. Коли отримується повідомлення "explain", він використовує tabs API щоб відкрити сторінку в новій вкладці.
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:
Сторінки налаштувань та інше
Browser extensions можуть містити різні типи сторінок:
- Action pages відображаються у drop-down when the extension icon при натисканні на іконку.
- Сторінки, які розширення буде load in a new tab.
- Option Pages: Ця сторінка відкривається поверх розширення при натисканні. У попередньому manifest у моєму випадку я зміг отримати доступ до цієї сторінки за адресою
chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjcaабо натисканням:
.png)
Зауважте, що ці сторінки не є постійними, як background pages, оскільки вони завантажують вміст динамічно за необхідності. Незважаючи на це, вони мають певні можливості, спільні з background page:
- Communication with Content Scripts: Подібно до background page, ці сторінки можуть отримувати повідомлення від content scripts, полегшуючи взаємодію в межах розширення.
- Access to Extension-Specific APIs: Ці сторінки мають повний доступ до extension-specific APIs, залежно від 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.
Оскільки browser extensions можуть бути настільки привілейованими, шкідливе розширення або таке, що було скомпрометоване, може дозволити зловмиснику різні способи вкрасти конфіденційну інформацію та шпигувати за користувачем.
Перевірте, як ці налаштування працюють і як ними можуть зловживати у:
BrowExt - permissions & host_permissions
content_security_policy
політика безпеки вмісту може бути оголошена також всередині manifest.json. Якщо вона визначена, вона може бути вразливою.
The default setting for browser extension pages is rather restrictive:
script-src 'self'; object-src 'self';
Для додаткової інформації про CSP та потенційні обхідні шляхи перегляньте:
Content Security Policy (CSP) Bypass
web_accessible_resources
Щоб веб-сторінка могла отримати доступ до сторінки розширення браузера, наприклад до .html-сторінки, ця сторінка має бути вказана у полі web_accessible_resources файлу manifest.json.
Наприклад:
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Ці сторінки доступні за URL, наприклад:
chrome-extension://<extension-id>/message.html
In public extensions the extension-id is accesible:
.png)
Although, if the manifest.json parameter use_dynamic_url is used, this id can be dynamic.
Tip
Зверніть увагу, що навіть якщо сторінка тут згадується, вона може бути захищена від ClickJacking завдяки Content Security Policy. Тому також потрібно перевірити її (секція frame-ancestors) перед підтвердженням, що атака ClickJacking можлива.
Being allowed to access these pages make these pages potentially vulnerable ClickJacking:
Tip
Дозвіл на завантаження цих сторінок лише розширенням, а не довільними URL, може запобігти атакам ClickJacking.
Caution
Зауважте, що сторінки з
web_accessible_resourcesта інші сторінки розширення також можуть контактувати з background scripts. Тому якщо одна з цих сторінок вразлива до XSS, це може призвести до більшої вразливості.Крім того, зверніть увагу, що сторінки, зазначені в
web_accessible_resources, можна відкривати всередині iframe, тоді як у новій вкладці можна отримати доступ до будь-якої сторінки розширення, знаючи extension ID. Тому, якщо знайдено XSS, що зловживає тими ж параметрами, його можна буде використати навіть якщо сторінка не налаштована в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_connectablekey 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:
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
- Якщо вказано порожнім:
"externally_connectable": {}, жодна програма або веб-сайт не зможуть підключитися.
Чим менше розширень та URL тут вказано, тим менша буде поверхня атаки.
Caution
Якщо у
externally_connectableвказано веб-сторінку, яка є vulnerable to XSS or takeover, нападник зможе send messages directly to the background script, повністю обходячи Content Script та його CSP.Отже, це є very powerful bypass.
Більше того, якщо клієнт встановить rouge розширення, навіть якщо воно не має дозволу на спілкування з вразливим розширенням, воно може інжектити XSS data in an allowed web page або зловживати API
WebRequestчиDeclarativeNetRequest, щоб маніпулювати запитами на цільовому домені, змінюючи запит сторінки на JavaScript file. (Зверніть увагу, що CSP на цільовій сторінці може запобігти цим атакам). Ця ідея походить from this writeup.
Підсумок комунікації
Extension <–> WebApp
Для зв’язку між content script і веб-сторінкою зазвичай використовуються post messages. Тому у веб-додатку ви зазвичай знайдете виклики функції window.postMessage і в content script — слухачі на кшталт window.addEventListener. Зауважте, однак, що розширення також може communicate with the web application sending a Post Message (і тому веб має це очікувати) або просто змусити веб завантажити новий скрипт.
Всередині розширення
Зазвичай функція chrome.runtime.sendMessage використовується для відправки повідомлення всередині розширення (зазвичай оброблюваного background script), а щоб отримати й обробити його, оголошується слухач, який викликає chrome.runtime.onMessage.addListener.
Також можливо використати chrome.runtime.connect() для встановлення постійного з’єднання замість відправки окремих повідомлень; за його допомогою можна send та receive messages, як у наведеному нижче прикладі:
chrome.runtime.connect() приклад
```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>
Також можливо надсилати повідомлення з фонового скрипта до контент-скрипта, розташованого в певній вкладці, викликавши **`chrome.tabs.sendMessage`**, де потрібно вказати **ідентифікатор вкладки** для надсилання повідомлення.
### Від дозволених `externally_connectable` до розширення
**Дозволені web-додатки та зовнішні розширення браузера** у конфігурації `externally_connectable` можуть надсилати запити, використовуючи :
```javascript
chrome.runtime.sendMessage(extensionId, ...
Де потрібно вказувати extension ID.
Native Messaging
Фонові скрипти можуть спілкуватися з бінарними файлами в системі, що може призвести до критичних вразливостей, таких як RCEs, якщо ця комунікація не буде належним чином захищена. More on this later.
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
Взаємодія Web ↔︎ Content Script
Середовища, в яких працюють content scripts, та де існують host pages, є відокремленими одне від одного, що забезпечує ізоляцію. Незважаючи на цю ізоляцію, обидва мають змогу взаємодіяти з Document Object Model (DOM) сторінки — спільним ресурсом. Щоб host page могла встановити зв’язок з content script, або опосередковано з розширенням через content script, потрібно використовувати DOM, доступний обом сторонам як канал комунікації.
Post Messages
// This is like "chrome.runtime.sendMessage" but to maintain the connection
var port = chrome.runtime.connect()
window.addEventListener(
"message",
(event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return
}
if (event.data.type && event.data.type === "FROM_PAGE") {
console.log("Content script received: " + event.data.text)
// Forward the message to the background script
port.postMessage(event.data.text)
}
},
false
)
document.getElementById("theButton").addEventListener(
"click",
() => {
window.postMessage(
{ type: "FROM_PAGE", text: "Hello from the webpage!" },
"*"
)
},
false
)
Безпечна Post Message комунікація повинна перевіряти автентичність отриманого повідомлення, це можна зробити перевіряючи:
event.isTrusted: Це True тільки якщо подія була викликана дією користувача- Контент-скрипт може очікувати повідомлення лише якщо користувач виконує певну дію
- origin domain: може очікувати повідомлення лише від allowlist доменів.
- Якщо використовується regex, будьте дуже обережні
- Source:
received_message.source !== windowможна використати, щоб перевірити, чи повідомлення було з того ж вікна, де слухає Content Script.
Попередні перевірки, навіть якщо виконані, можуть бути уразливими, тому перегляньте на наступній сторінці potential Post Message bypasses:
Iframe
Ще один можливий спосіб комунікації — через Iframe URLs, приклад можна знайти в:
DOM
Це не зовсім спосіб комунікації, але web й content script матимуть доступ до web DOM. Тому, якщо content script читає якусь інформацію з нього, довіряючи web DOM, web може змінити ці дані (бо web не слід довіряти, або бо web уразливий до XSS) і компрометувати Content Script.
Приклад DOM based XSS to compromise a browser extension також можна знайти в:
Content Script ↔︎ Background Script Communication
Content Script може використовувати функції runtime.sendMessage() or tabs.sendMessage() щоб відправити one-time JSON-serializable повідомлення.
Щоб обробити response, використовуйте повернутий Promise. Проте, для зворотної сумісності, ви все ще можете передати callback як останній аргумент.
Відправка запиту з content script виглядає так:
;(async () => {
const response = await chrome.runtime.sendMessage({ greeting: "hello" })
// do something with response here, not outside the function
console.log(response)
})()
Надсилання запиту з розширення (зазвичай із фонового скрипту). Приклад того, як надіслати повідомлення до content script у вибраній вкладці:
// 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)
})()
На приймаючому боці, вам потрібно налаштувати runtime.onMessage обробник подій для обробки повідомлення. Це виглядає однаково як у content script, так і у extension page.
// 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" })
})
У наведеному прикладі sendResponse() виконувалася синхронно. Щоб змінити обробник події onMessage для асинхронного виконання sendResponse(), необхідно додати return true;.
Важливо пам’ятати, що якщо декілька сторінок налаштовані на отримання подій onMessage, перша сторінка, яка виконає sendResponse() для конкретної події, буде єдиною, що зможе ефективно доставити відповідь. Будь-які наступні відповіді на ту саму подію враховані не будуть.
При створенні нових розширень слід віддавати перевагу promises замість callbacks. Що стосується використання callbacks, функція sendResponse() вважається дійсною лише якщо вона виконується безпосередньо в синхронному контексті, або якщо обробник події вказує на асинхронну операцію, повертаючи true. Якщо ж жоден із обробників не поверне true або функція sendResponse() буде видалена з пам’яті (garbage-collected), callback, пов’язаний з sendMessage(), буде викликаний за замовчуванням.
Native Messaging
Розширення браузера також дозволяють спілкуватися з бінарними файлами в системі через stdin. Додаток має встановити json, що це вказує, у 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/"]
}
Де name — це рядок, який передається в runtime.connectNative() або runtime.sendNativeMessage() для спілкування з додатком з фонових скриптів розширення браузера. path — це шлях до бінарного файлу, існує лише 1 дійсний type, який — stdio (use stdin and stdout), а allowed_origins вказують розширення, які можуть отримати доступ (і не можуть мати wildcard).
Chrome/Chromium шукатиме цей json у реєстрі Windows та в деяких шляхах на macOS і Linux (більше інформації в docs).
Tip
Розширенню браузера також потрібно задекларувати дозвіл
nativeMessaing, щоб мати можливість використовувати цю комунікацію.
Ось як виглядає код фонового скрипта, який надсилає повідомлення нативному додатку:
chrome.runtime.sendNativeMessage(
"com.my_company.my_application",
{ text: "Hello" },
function (response) {
console.log("Received " + response)
}
)
In this blog post, a vulnerable pattern abusing native messages is proposed:
- Browser extension has a wildcard pattern for content script.
- Content script passes
postMessagemessages to the background script usingsendMessage. - Background script passes the message to native application using
sendNativeMessage. - Native application handles the message dangerously, leading to code execution.
And inside of it an example of going from any page to RCE abusing a browser extension is explained.
Sensitive Information in Memory/Code/Clipboard
If a Browser Extension stores sensitive information inside it’s memory, this could be dumped (specially in Windows machines) and searched for this information.
Therefore, the memory of the Browser Extension shouldn’t be considered secure and sensitive information such as credentials or mnemonic phrases shouldn’t be stored.
Of course, do not put sensitive information in the code, as it will be public.
To dump memory from the browser you could dump the process memory or to go to the settings of the browser extension click on Inspect pop-up -> In the Memory section -> Take a snaphost and CTRL+F to search inside the snapshot for sensitive info.
Moreover, highly sensitive information like mnemonic keys or passwords shouldn’t be allowed to be copied in the clipboard (or at least remove it from the clipboard in a few seconds) because then processes monitoring the clipboard will be able to get them.
Loading an Extension in the Browser
- Download the Browser Extension & unzipped
- Go to
chrome://extensions/and enable theDeveloper Mode - Click the
Load unpackedbutton
In Firefox you go to about:debugging#/runtime/this-firefox and click Load Temporary Add-on button.
Getting the source code from the store
The source code of a Chrome extension can be obtained through various methods. Below are detailed explanations and instructions for each option.
Download Extension as ZIP via Command Line
The source code of a Chrome extension can be downloaded as a ZIP file using the command line. This involves using curl to fetch the ZIP file from a specific URL and then extracting the contents of the ZIP file to a directory. Here are the steps:
- Replace
"extension_id"with the actual ID of the extension. - Execute the following commands:
extension_id=your_extension_id # Replace with the actual extension ID
curl -L -o "$extension_id.zip" "https://clients2.google.com/service/update2/crx?response=redirect&os=mac&arch=x86-64&nacl_arch=x86-64&prod=chromecrx&prodchannel=stable&prodversion=44.0.2403.130&x=id%3D$extension_id%26uc"
unzip -d "$extension_id-source" "$extension_id.zip"
Використовуйте вебсайт CRX Viewer
Використовуйте розширення CRX Viewer
Ще один зручний спосіб — використати Chrome Extension Source Viewer, який є проектом з відкритим вихідним кодом. Його можна встановити з Chrome Web Store. Вихідний код viewer доступний у його GitHub repository.
Переглянути вихідний код локально встановленого розширення
Локально встановлені розширення Chrome також можна інспектувати. Ось як це зробити:
- Отримайте доступ до локального каталогу профілю Chrome, перейшовши на
chrome://version/і знайшовши поле “Profile Path”. - Перейдіть до підпапки
Extensions/у каталозі профілю. - Ця папка містить усі встановлені розширення, зазвичай із їхнім вихідним кодом у читабельному форматі.
Щоб ідентифікувати розширення, можна зіставити їхні ID з іменами:
- Увімкніть Developer Mode на сторінці
about:extensions, щоб бачити ID кожного розширення. - У папці кожного розширення файл
manifest.jsonмістить читабельне полеname, яке допомагає ідентифікувати розширення.
Використовуйте архіватор або розпакувальник файлів
Зайдіть у Chrome Web Store і скачайте розширення. Файл матиме розширення .crx. Змініть розширення файлу з .crx на .zip. Використайте будь-який файл-архіватор (наприклад WinRAR, 7-Zip тощо), щоб витягти вміст ZIP-файлу.
Використовуйте Developer Mode у Chrome
Відкрийте Chrome і перейдіть до chrome://extensions/. Увімкніть “Developer mode” у верхньому правому куті. Натисніть “Load unpacked extension…”. Перейдіть до каталогу вашого розширення. Це не завантажує вихідний код, але корисно для перегляду та модифікації коду вже завантаженого або розробленого розширення.
Chrome extension manifest dataset
Щоб спробувати виявити вразливі розширення браузера, ви можете використати thehttps://github.com/palant/chrome-extension-manifests-dataset і перевірити їхні manifest файли на потенційні ознаки вразливостей. Наприклад, щоб перевірити розширення з більш ніж 25000 користувачів, content_scripts та дозвіл nativeMessaing:
# 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 technique to backdoor Chromium by directly editing per-user Preferences and forging valid HMACs, causing the browser to accept and activate an arbitrary unpacked extension without prompts or flags.
Forced Extension Load Preferences Mac Forgery Windows
Виявлення шкідливих оновлень розширень (Статичне порівняння версій)
Supply-chain compromises often arrive as malicious updates to previously benign extensions. Практичний, малошумний підхід — порівняти новий пакет розширення з останньою відомо-правильною версією за допомогою статичного аналізу (наприклад, Assemblyline). Мета — сигналізувати про значущі відмінності, а не про будь-яку зміну.
Workflow
- Submit both versions (old + new) to the same static-analysis profile.
- Flag new or updated background/service worker scripts (персистентність + привілейована логіка).
- Flag new or updated content scripts (доступ до DOM і збір даних).
- Flag new permissions/host_permissions added in
manifest.json. - Flag new domains extracted from code (потенційні C2/exfil endpoints).
- Flag new static-analysis detections (наприклад, base64 decode, cookie harvesting, network-request builders, обфускаційні патерни).
- Flag statistical anomalies such as sharp entropy jumps or outlier z-scores in changed scripts.
Detecting script changes accurately
- New script added → detect via
manifest.jsondiff. - Existing script modified (manifest unchanged) → compare per-file hashes from the extracted file tree (e.g., Assemblyline
Extractoutput). Це ловить приховані оновлення існуючих workers або content scripts.
Pre-disclosure detections
Щоб уникнути «easy mode» виявлень, що базуються на вже відомих IOC, відключіть сервіси, що живляться threat-intel, і покладайтеся на внутрішні сигнали (домені, евристичні сигнатури, дельти скриптів, аномалії ентропії). Це підвищує шанси виявити шкідливі оновлення до публічного розкриття.
Example high-confidence alert logic
- Low-noise combo: new domains + new static-analysis detections + updated background/service worker + updated or added content scripts.
- Broader catch: new domain + new or updated background/service worker (вища recall, вищий шум).
Ключові сервіси Assemblyline для цього workflow:
- Extract: розпаковує розширення і видає per-file hashes.
- Characterize: обчислює характеристики файлів (наприклад, entropy).
- JsJAWS / FrankenStrings / URLCreator: витягують JS-евристики, рядки та домени для дифу між версіями.
Security Audit Checklist
Хоча Browser Extensions мають обмежену поверхню атаки, деякі з них можуть містити vulnerabilities або вимагати посилення безпеки. Нижче наведено найпоширеніші пункти:
- 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_resourcesis 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
- Thanks to @naivenom for the help with this methodology
- 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
- https://redcanary.com/blog/threat-detection/assemblyline-browser-extensions/
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.


