Metodologia de Pentesting para Extensões de Navegador

Reading time: 29 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Informações Básicas

Browser extensions são escritas em JavaScript e carregadas pelo browser em segundo plano. Elas têm seu DOM mas podem interagir com os DOMs de outros sites. Isso significa que podem comprometer a confidencialidade, integridade e disponibilidade (CIA) de outros sites.

Componentes Principais

Layouts de extensão ficam melhores quando visualizados e consistem em três componentes. Vamos analisar cada componente em profundidade.

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

Content Scripts

Cada content script tem acesso direto ao DOM de uma única página web e, portanto, fica exposto a entrada potencialmente maliciosa. No entanto, o content script não contém permissões além da habilidade de enviar mensagens ao extension core.

Extension Core

O extension core contém a maior parte dos privilégios/acessos da extensão, mas o extension core só pode interagir com o conteúdo web via XMLHttpRequest e content scripts. Além disso, o extension core não tem acesso direto à máquina host.

Native Binary

A extensão permite um native binary que pode acessar a máquina host com os privilégios completos do usuário. O native binary interage com o extension core através da Netscape Plugin Application Programming Interface padrão (NPAPI) usada pelo Flash e outros plug-ins de browser.

Boundaries

caution

To obtain the user's full privileges, an attacker must convince the extension to pass malicious input from the content script to the extension's core and from the extension's core to the native binary.

Cada componente da extensão é separado dos outros por fortes boundaries de proteção. Cada componente roda em um processo separado do sistema operacional. Content scripts e extension cores rodam em sandbox processes indisponíveis para a maioria dos serviços do sistema operacional.

Além disso, content scripts se separam das páginas web associadas por rodarem em um JavaScript heap separado. O content script e a página web têm acesso ao mesmo DOM subjacente, mas os dois nunca trocam JavaScript pointers, prevenindo o leak de funcionalidade JavaScript.

manifest.json

Uma Chrome extension é basicamente uma pasta ZIP com uma .crx file extension. O núcleo da extensão é o arquivo manifest.json na raiz da pasta, que especifica o layout, permissões e outras opções de configuração.

Exemplo:

json
{
"manifest_version": 2,
"name": "My extension",
"version": "1.0",
"permissions": ["storage"],
"content_scripts": [
{
"js": ["script.js"],
"matches": ["https://example.com/*", "https://www.example.com/*"],
"exclude_matches": ["*://*/*business*"]
}
],
"background": {
"scripts": ["background.js"]
},
"options_ui": {
"page": "options.html"
}
}

content_scripts

Content scripts são carregados sempre que o usuário navega para uma página correspondente, no nosso caso qualquer página que corresponda à expressão https://example.com/* e que não corresponda ao regex *://*/*/business*. Eles executam como os próprios scripts da página e têm acesso arbitrário ao Document Object Model (DOM).

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

Para incluir ou excluir mais URLs, também é possível usar include_globs e exclude_globs.

Este é um content script de exemplo que adicionará um botão "Explain" à página quando the storage API for usado para recuperar o valor message do armazenamento da extensão.

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

Uma mensagem é enviada para as páginas da extensão pelo content script quando este botão é clicado, por meio da utilização da runtime.sendMessage() API. Isso ocorre devido à limitação do content script em acessar APIs diretamente, com storage sendo uma das poucas exceções. Para funcionalidades além dessas exceções, mensagens são enviadas para páginas da extensão com as quais os content scripts podem se comunicar.

warning

Dependendo do navegador, as capacidades do content script podem variar ligeiramente. Para navegadores baseados em Chromium, a lista de capacidades está disponível na Chrome Developers documentation, e para o Firefox, o MDN serve como a fonte principal.
Também vale notar que os content scripts podem se comunicar com background scripts, permitindo que executem ações e retornem respostas.

Para visualizar e depurar content scripts no Chrome, o menu Chrome developer tools pode ser acessado em Options > More tools > Developer tools OU pressionando Ctrl + Shift + I.

Com o developer tools exibido, clique na Source tab, seguida pela Content Scripts tab. Isso permite observar os content scripts em execução de várias extensões e definir breakpoints para acompanhar o fluxo de execução.

Content scripts injetados

tip

Observe que Content Scripts não são obrigatórios, pois também é possível injetar scripts de forma dinâmica e programaticamente em páginas web via tabs.executeScript. Isso fornece controles mais granulares.

Para a injeção programática de um content script, a extensão precisa ter host permissions para a página na qual os scripts serão injetados. Essas permissões podem ser obtidas seja solicitando-as no manifest da extensão, seja temporariamente através de activeTab.

Exemplo de extensão baseada em activeTab

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Injetar um arquivo JS ao clicar:
javascript
// content-script.js
document.body.style.backgroundColor = "orange"

//service-worker.js - Inject the JS file
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"],
})
})
  • Inject a function ao clicar:
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,
})
})

Exemplo com permissões de script

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

Para incluir ou excluir mais URLs, também é possível usar include_globs e exclude_globs.

Scripts de Conteúdo run_at

O campo run_at controla quando os arquivos JavaScript são injetados na página web. O valor preferido e padrão é "document_idle".

Os valores possíveis são:

  • document_idle: Sempre que possível
  • document_start: Após quaisquer arquivos de css, mas antes de qualquer outro DOM ser construído ou qualquer outro script ser executado.
  • document_end: Imediatamente após o DOM estar completo, mas antes que subrecursos como imagens e frames tenham sido carregados.

Via manifest.json

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

Através de service-worker.js

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

background

Mensagens enviadas por content scripts são recebidas pela página em segundo plano, que desempenha um papel central na coordenação dos componentes da extensão. Notavelmente, a página em segundo plano persiste durante todo o ciclo de vida da extensão, operando discretamente sem interação direta do usuário. Ela possui seu próprio Document Object Model (DOM), permitindo interações complexas e gerenciamento de estado.

Pontos-chave:

  • Função da página em segundo plano: Atua como o centro nervoso da extensão, garantindo comunicação e coordenação entre suas várias partes.
  • Persistência: É uma entidade sempre presente, invisível ao usuário, mas integral à funcionalidade da extensão.
  • Geração automática: Se não definida explicitamente, o navegador criará automaticamente uma página em segundo plano. Essa página gerada automaticamente incluirá todos os scripts em segundo plano especificados no manifest da extensão, garantindo a operação contínua das tarefas em segundo plano da extensão.

tip

A conveniência oferecida pelo navegador ao gerar automaticamente uma página em segundo plano (quando não declarada explicitamente) garante que todos os scripts em segundo plano necessários estejam integrados e operacionais, simplificando o processo de configuração da extensão.

Exemplo de script em segundo plano:

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

Ele usa a runtime.onMessage API para escutar mensagens. Quando uma mensagem "explain" é recebida, ele usa a tabs API para abrir uma página em uma nova aba.

Para debugar o background script você pode ir aos extension details and inspect the service worker, isso abrirá as ferramentas de desenvolvedor com o background script:

Páginas de opções e outras

Browser extensions podem conter vários tipos de páginas:

  • Action pages são exibidas em um drop-down when the extension icon is clicked.
  • Páginas que a extensão irá load in a new tab.
  • Option Pages: Esta página é exibida sobre a extensão quando clicada. No manifest anterior, no meu caso consegui acessar essa página em chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca ou clicando:

Note que essas páginas não são persistentes como background pages, pois carregam conteúdo dinamicamente conforme necessário. Apesar disso, elas compartilham certas capacidades com a background page:

  • Communication with Content Scripts: Semelhante ao background page, estas páginas podem receber mensagens de content scripts, facilitando a interação dentro da extensão.
  • Access to Extension-Specific APIs: Essas páginas têm acesso abrangente às extension-specific APIs, sujeitas às permissões definidas para a extensão.

permissions & host_permissions

permissions e host_permissions são entradas do manifest.json que indicarão quais permissões a extensão do navegador possui (storage, location...) e em quais páginas web.

Como as browser extensions podem ser tão privileged, uma extensão maliciosa ou comprometida poderia permitir ao atacante different means to steal sensitive information and spy on the user.

Confira como essas configurações funcionam e como podem ser abusadas em:

BrowExt - permissions & host_permissions

content_security_policy

Uma content security policy também pode ser declarada dentro do manifest.json. Se houver uma definida, ela pode ser vulnerable.

A configuração padrão para as páginas da browser extension é bastante restritiva:

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

Para mais informações sobre CSP e possíveis bypasses, consulte:

Content Security Policy (CSP) Bypass

web_accessible_resources

Para que uma página web consiga acessar uma página de uma extensão do navegador, por exemplo um arquivo .html, essa página precisa ser mencionada no campo web_accessible_resources do manifest.json.
Por exemplo:

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

Essas páginas são acessíveis em URLs como:

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

Observe que, mesmo que uma página seja mencionada aqui, ela pode estar protegida contra ClickJacking graças à Content Security Policy. Portanto, você também precisa checar isso (seção frame-ancestors) antes de confirmar que um ataque ClickJacking é possível.

Permitir o acesso a essas páginas as torna potencialmente vulneráveis a ClickJacking:

BrowExt - ClickJacking

tip

Permitir que essas páginas sejam carregadas apenas pela extensão e não por URLs aleatórias pode prevenir ataques de ClickJacking.

caution

Observe que as páginas listadas em web_accessible_resources e outras páginas da extensão também são capazes de contatar background scripts. Portanto, se uma dessas páginas for vulnerável a XSS isso pode abrir uma vulnerabilidade maior.

Além disso, lembre-se de que você só pode abrir páginas indicadas em web_accessible_resources dentro de iframes, mas a partir de uma nova aba é possível acessar qualquer página da extensão conhecendo o extension ID. Portanto, se for encontrada uma XSS que abuse dos mesmos parâmetros, ela poderia ser explorada mesmo que a página não esteja configurada em 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/*",
  • Se for especificado como vazio: "externally_connectable": {}, nenhum app ou site poderá se conectar.

Quanto menos extensões e URLs forem indicadas aqui, menor será a superfície de ataque.

caution

Se uma página web vulnerável a XSS ou takeover for indicada em externally_connectable, um atacante poderá enviar mensagens diretamente ao background script, contornando completamente o Content Script e seu CSP.

Portanto, este é um bypass muito poderoso.

Além disso, se o cliente instalar uma extensão maliciosa, mesmo que ela não esteja autorizada a se comunicar com a extensão vulnerável, ela poderia injetar dados XSS em uma página web permitida ou abusar das APIs WebRequest ou DeclarativeNetRequest para manipular requisições em um domínio alvo, alterando a requisição de uma página por um arquivo JavaScript. (Observe que o CSP na página alvo pode prevenir esses ataques). Esta ideia vem from this writeup.

Resumo de comunicação

Extensão <--> WebApp

Para comunicar entre o Content Script e a página web, normalmente são usados post messages. Portanto, na aplicação web normalmente você encontrará chamadas para a função window.postMessage e, no Content Script, listeners como window.addEventListener. Note, porém, que a extensão também poderia se comunicar com a aplicação web enviando um Post Message (e, portanto, a aplicação web deve esperar por isso) ou simplesmente fazer com que a web carregue um novo script.

Dentro da extensão

Normalmente a função chrome.runtime.sendMessage é usada para enviar uma mensagem dentro da extensão (geralmente tratada pelo background script) e, para receber e tratá-la, um listener é declarado chamando chrome.runtime.onMessage.addListener.

Também é possível usar chrome.runtime.connect() para ter uma conexão persistente em vez de enviar mensagens únicas; é possível usá-la para enviar e receber mensagens como no exemplo a seguir:

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

Também é possível enviar mensagens de um script em segundo plano para um script de conteúdo localizado em uma aba específica chamando chrome.tabs.sendMessage, onde você precisará indicar o ID da aba para enviar a mensagem.

De externally_connectable permitidos para a extensão

Aplicações web e extensões de navegador externas permitidas na configuração externally_connectable podem enviar requisições usando :

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

Quando for necessário mencionar o extension ID.

Native Messaging

É possível que os background scripts se comuniquem com binaries no sistema, o que pode ser suscetível a vulnerabilidades críticas, como RCEs, se essa comunicação não for adequadamente protegida. More on this later.

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

Comunicação Web ↔︎ Content Script

Os ambientes onde os content scripts operam e onde as páginas host existem são separados entre si, garantindo isolamento. Apesar desse isolamento, ambos têm a capacidade de interagir com o Document Object Model (DOM) da página, um recurso compartilhado. Para que a página host se comunique com o content script, ou indiretamente com a extensão através do content script, é necessário utilizar o DOM acessível por ambas as partes como canal de comunicação.

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
)

Uma comunicação Post Message segura deve verificar a autenticidade da mensagem recebida; isso pode ser feito verificando:

  • event.isTrusted: Isso é True somente se o evento foi disparado por uma ação do usuário
  • O Content Script pode esperar uma mensagem apenas se o usuário executar alguma ação
  • origin domain: pode esperar uma mensagem apenas de uma allowlist de domínios.
  • Se uma regex for usada, tenha muito cuidado
  • Source: received_message.source !== window pode ser usado para verificar se a mensagem foi da mesma janela onde o Content Script está escutando.

As verificações anteriores, mesmo se realizadas, podem ser vulneráveis; portanto, confira na página a seguir potential Post Message bypasses:

PostMessage Vulnerabilities

Iframe

Outra forma possível de comunicação pode ser através de Iframe URLs, você pode encontrar um exemplo em:

BrowExt - XSS Example

DOM

Isso não é "exatamente" uma forma de comunicação, mas a web e o Content Script terão acesso ao web DOM. Então, se o content script estiver lendo alguma informação dele confiando no web DOM, a página poderia modificar esses dados (porque a web não deve ser confiável, ou porque a página é vulnerável a XSS) e comprometer o Content Script.

Você também pode encontrar um exemplo de um DOM based XSS to compromise a browser extension em:

BrowExt - XSS Example

Content Script ↔︎ Background Script Communication

Um Content Script pode usar as funções runtime.sendMessage() or tabs.sendMessage() para enviar uma mensagem one-time JSON-serializable.

Para lidar com a response, use a Promise retornada. Embora, por compatibilidade retroativa, você ainda possa passar um callback como último argumento.

Enviar uma request a partir de um content script fica assim:

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

Enviando uma requisição a partir da extension (normalmente um background script). Exemplo de como enviar uma mensagem para o content script na tab selecionada:

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

No lado receptor, você precisa configurar um runtime.onMessage ouvinte de eventos para tratar a mensagem. Isso fica igual em um content script ou 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" })
})

No exemplo destacado, sendResponse() foi executado de forma síncrona. Para modificar o manipulador de eventos onMessage para execução assíncrona de sendResponse(), é imprescindível incorporar return true;.

Uma consideração importante é que, em cenários onde múltiplas páginas estão configuradas para receber eventos onMessage, a primeira página a executar sendResponse() para um evento específico será a única capaz de entregar a resposta de forma eficaz. Quaisquer respostas subsequentes ao mesmo evento não serão levadas em conta.

Ao criar novas extensões, a preferência deve ser por promises em vez de callbacks. Em relação ao uso de callbacks, a função sendResponse() é considerada válida somente se for executada diretamente dentro do contexto síncrono, ou se o manipulador de eventos indicar uma operação assíncrona retornando true. Caso nenhum dos manipuladores retorne true ou se a função sendResponse() for removida da memória (garbage-collected), o callback associado à função sendMessage() será acionado por padrão.

Native Messaging

Extensões de navegador também permitem se comunicar com binários no sistema via stdin. O aplicativo deve instalar um json indicando isso em um json como:

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

Onde o name é a string passada para runtime.connectNative() ou runtime.sendNativeMessage() para comunicar com o aplicativo a partir dos scripts em segundo plano da extensão do navegador. O path é o caminho para o binário, existe apenas 1 type válido que é stdio (use stdin and stdout) e os allowed_origins indicam as extensões que podem acessá-lo (não podem usar coringa).

Chrome/Chromium irá procurar por esse json em alguns registros do Windows e em alguns caminhos no macOS e Linux (mais informações nos docs).

tip

A extensão do navegador também precisa que a permissão nativeMessaing esteja declarada para poder usar essa comunicação.

This is how it looks like some background script code sending messages to a native application:

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

In this blog post, um padrão vulnerável que abusa de native messages é proposto:

  1. Browser extension has a wildcard pattern for content script.
  2. Content script passes postMessage messages to the background script using sendMessage.
  3. Background script passes the message to native application using sendNativeMessage.
  4. Native application handles the message dangerously, leading to code execution.

E dentro dele é apresentado um exemplo de going from any page to RCE abusing a browser extension is explained.

Sensitive Information in Memory/Code/Clipboard

Se uma Browser Extension armazena informação sensível na sua memória, isso pode ser dumped (especialmente em máquinas Windows) e procurado por essa informação.

Portanto, a memória da Browser Extension não deve ser considerada segura e informação sensível como credenciais ou frases mnemônicas não deve ser armazenada.

Claro, não coloque informação sensível no código, pois ela será pública.

Para dump da memória do browser você pode dump the process memory ou ir às settings da browser extension clicar em Inspect pop-up -> Na secção Memory -> Take a snaphost e usar CTRL+F para procurar dentro do snapshot por informação sensível.

Além disso, informação altamente sensível como chaves mnemônicas ou passwords não deve ser permitida ser copiada para a clipboard (ou pelo menos removê-la da clipboard em alguns segundos) porque processos que monitorizam a clipboard poderão obtê-las.

Loading an Extension in the Browser

  1. Download the Browser Extension & unzipped
  2. Vá para chrome://extensions/ e ative o Developer Mode
  3. Clique no botão Load unpacked

No Firefox vá para about:debugging#/runtime/this-firefox e clique no botão Load Temporary Add-on.

Getting the source code from the store

O código-fonte de uma Chrome extension pode ser obtido através de vários métodos. Abaixo estão explicações detalhadas e instruções para cada opção.

Download Extension as ZIP via Command Line

O código-fonte de uma Chrome extension pode ser descarregado como um ficheiro ZIP usando a linha de comando. Isto envolve usar curl para obter o ficheiro ZIP a partir de uma URL específica e depois extrair o conteúdo do ZIP para um diretório. Aqui estão os passos:

  1. Substitua "extension_id" pelo ID real da extensão.
  2. Execute os seguintes comandos:
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"

Use the CRX Viewer website

https://robwu.nl/crxviewer/

Use the CRX Viewer extension

Outro método conveniente é usar o Chrome Extension Source Viewer, que é um projeto open-source. Pode ser instalado a partir da Chrome Web Store. O código-fonte do viewer está disponível em seu GitHub repository.

View source of locally installed extension

Extensões do Chrome instaladas localmente também podem ser inspecionadas. Veja como:

  1. Acesse o diretório de perfil local do Chrome visitando chrome://version/ e localizando o campo "Profile Path".
  2. Navegue até a subpasta Extensions/ dentro do diretório de perfil.
  3. Esta pasta contém todas as extensões instaladas, tipicamente com seu código-fonte em um formato legível.

Para identificar extensões, você pode mapear seus IDs para nomes:

  • Ative o Developer Mode na página about:extensions para ver os IDs de cada extensão.
  • Dentro da pasta de cada extensão, o arquivo manifest.json contém um campo name legível, ajudando a identificar a extensão.

Use a File Archiver or Unpacker

Vá ao Chrome Web Store e faça o download da extensão. O arquivo terá a extensão .crx. Altere a extensão do arquivo de .crx para .zip. Use qualquer arquivador de arquivos (como WinRAR, 7-Zip, etc.) para extrair o conteúdo do arquivo ZIP.

Use Developer Mode in Chrome

Abra o Chrome e vá para chrome://extensions/. Habilite "Developer mode" no canto superior direito. Clique em "Load unpacked extension...". Navegue até o diretório da sua extensão. Isso não baixa o código-fonte, mas é útil para visualizar e modificar o código de uma extensão já baixada ou desenvolvida.

Chrome extension manifest dataset

Para tentar identificar extensões de navegador vulneráveis você pode usar ohttps://github.com/palant/chrome-extension-manifests-dataset e verificar seus arquivos manifest em busca de sinais potencialmente vulneráveis. Por exemplo, para checar por extensões com mais de 25000 usuários, content_scripts e a permissão 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)

Técnica furtiva para backdoorar o Chromium editando diretamente as Preferences por usuário e forjando HMACs válidos, fazendo com que o navegador aceite e ative uma arbitrary unpacked extension sem prompts ou flags.

Forced Extension Load Preferences Mac Forgery Windows

Checklist de Auditoria de Segurança

Embora as Extensões do Navegador tenham uma superfície de ataque limitada, algumas podem conter vulnerabilidades ou potenciais melhorias de hardening. As seguintes são as mais comuns:

  • Limitar ao máximo os permissions solicitados
  • Limitar ao máximo os host_permissions
  • Usar uma strong content_security_policy
  • Limitar ao máximo o externally_connectable; se nenhum for necessário e possível, não deixe por default, especifique {}
  • Se uma URL vulnerável a XSS ou a takeover for mencionada aqui, um atacante poderá enviar mensagens diretamente aos background scripts. Bypass muito poderoso.
  • Limitar ao máximo os web_accessible_resources, mesmo vazio se possível.
  • Se web_accessible_resources não for none, verifique por ClickJacking
  • Se qualquer comunicação ocorrer da extensão para a página web, verifique por XSS vulnerabilities causadas na comunicação.
  • Se Post Messages estiverem sendo usados, verifique por Post Message vulnerabilities.
  • Se o Content Script acessa detalhes do DOM, verifique se eles não estão introduzindo um XSS caso sejam modificados pela web
  • Faça ênfase especial se essa comunicação também estiver envolvida na Content Script -> Background script communication
  • Se o background script estiver se comunicando via native messaging, verifique se a comunicação é segura e sanitizada
  • Informações sensíveis não devem ser armazenadas dentro do código da Browser Extension
  • Informações sensíveis não devem ser armazenadas na memória da Browser Extension
  • Informações sensíveis não devem ser armazenadas no file system sem proteção

Riscos de Browser Extension

  • O app https://crxaminer.tech/ analisa alguns dados como os permissions que a browser extension solicita para fornecer um nível de risco do uso da browser extension.

Ferramentas

Tarnish

  • Baixa qualquer Chrome extension a partir de um link do Chrome webstore fornecido.
  • manifest.json viewer: simplesmente exibe uma versão JSON-prettified do manifest da extension.
  • Fingerprint Analysis: Detecção de web_accessible_resources e geração automática de JavaScript para fingerprinting de Chrome extension.
  • Potential Clickjacking Analysis: Detecção de páginas HTML da extension com a diretiva web_accessible_resources definida. Essas podem ser potencialmente vulneráveis a clickjacking dependendo da finalidade das páginas.
  • Permission Warning(s) viewer: mostra uma lista de todos os avisos de permissão do Chrome que serão exibidos quando um usuário tentar instalar a extensão.
  • Dangerous Function(s): mostra a localização de funções perigosas que poderiam ser potencialmente exploradas por um atacante (ex.: funções como innerHTML, chrome.tabs.executeScript).
  • Entry Point(s): mostra onde a extensão recebe input do usuário/externo. Útil para entender a superfície da extensão e procurar pontos potenciais para enviar dados maliciosamente crafted para a extensão.
  • Ambos os scanners Dangerous Function(s) e Entry Point(s) possuem, para seus alertas gerados:
    • Trecho de código relevante e linha que causou o alerta.
    • Descrição do problema.
    • Um botão “View File” para ver o arquivo fonte completo contendo o código.
    • O caminho do arquivo alertado.
    • O URI completo da extensão Chrome do arquivo alertado.
    • O tipo de arquivo, como Background Page script, Content Script, Browser Action, etc.
    • Se a linha vulnerável estiver em um arquivo JavaScript, os caminhos de todas as páginas onde ele é incluído, bem como o tipo dessas páginas e o status de web_accessible_resource.
  • Content Security Policy (CSP) analyzer and bypass checker: Aponta fraquezas no CSP da sua extensão e também ilumina possíveis formas de burlar o CSP devido a CDNs listadas como whitelist, etc.
  • Known Vulnerable Libraries: Utiliza Retire.js para verificar o uso de bibliotecas JavaScript conhecidas como vulneráveis.
  • Download da extensão e versões formatadas.
  • Download da extensão original.
  • Download de uma versão beautified da extensão (HTML e JavaScript auto-prettified).
  • Cache automático dos resultados de scan; rodar um scan de extensão levará um bom tempo na primeira vez. Na segunda vez, assumindo que a extensão não foi atualizada, será quase instantâneo devido ao cache dos resultados.
  • URLs de relatório linkáveis, facilitando enviar a alguém um relatório de extensão gerado pelo tarnish.

Neto

O Projeto Neto é um pacote Python 3 concebido para analisar e desvendar funcionalidades ocultas de browser plugins e extensions para navegadores conhecidos como Firefox e Chrome. Automatiza o processo de descompactar os arquivos empacotados para extrair essas funcionalidades de recursos relevantes em uma extensão como manifest.json, pastas de localização ou arquivos fonte Javascript e HTML.

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks