Metodologia de Pentesting de Extensões de Navegador

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

Extensões de navegador são escritas em JavaScript e carregadas pelo navegador 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

O layout das extensões fica melhor quando visualizado e consiste em três componentes. Vamos analisar cada componente em profundidade.

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

Content Scripts

Each content script has direct access to the DOM of a single web page and is thereby exposed to potentially malicious input. However, the content script contains no permissions other than the ability to send messages to the extension core.

Extension Core

The extension core contains most of the extension privileges/access, but the extension core can only interact with web content via XMLHttpRequest and content scripts. Also, the extension core does not have direct access to the host machine.

Native Binary

The extension allows a native binary that can access the host machine with the user’s full privileges. The native binary interacts with the extension core through the standard Netscape Plugin Application Programming Interface (NPAPI) used by Flash and other browser plug-ins.

Limites

Caution

Para obter os privilégios completos do usuário, um atacante deve convencer a extensão a passar entrada maliciosa do content script para o extension core e do extension core para o native binary.

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

Além disso, content scripts são separados das páginas web associadas por rodarem em um heap JavaScript separado. O content script e a página web têm acesso ao mesmo DOM subjacente, mas os dois nunca trocam ponteiros JavaScript, prevenindo o leaking da funcionalidade JavaScript.

manifest.json

A Chrome extension is just a ZIP folder with a .crx file extension. The extension’s core is the manifest.json file at the root of the folder, which specifies layout, permissions, and other configuration options.

Example:

{
"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 à regex *://*/*/business*. Eles são executados como os próprios scripts da página e têm acesso arbitrário ao Document Object Model (DOM) da página.

"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 exemplo de content script que adicionará um explain button à página ao usar the storage API para recuperar o valor message do armazenamento da extensão.

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 às 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 no acesso direto às APIs, sendo storage uma das poucas exceções. Para funcionalidades além dessas exceções, mensagens são enviadas às páginas da extensão com as quais os content scripts podem se comunicar.

Warning

Dependendo do browser, 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 têm a habilidade de comunicar-se com background scripts, permitindo que executem ações e encaminhem respostas de volta.

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

Com as developer tools exibidas, clique na aba Source, seguida da aba Content Scripts. Isso permite a observação dos content scripts em execução de várias extensões e a definição de breakpoints para rastrear o fluxo de execução.

Scripts de conteúdo injetados

Tip

Note que Content Scripts aren’t mandatory já que também é possível dinamicamente injectar scripts e injectá-los programaticamente em páginas web via tabs.executeScript. Isso na verdade 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 solicitando-as dentro do manifest da extensão ou de forma temporária através de activeTab.

Exemplo de extensão baseada em activeTab

{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Injetar um arquivo JS ao clicar:
// 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"],
})
})
  • Injetar uma função ao clicar:
//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 scripting

// 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.

Content Scripts run_at

O campo run_at controla quando 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: Depois de quaisquer arquivos de css, mas antes da construção do DOM e da execução de outros scripts.
  • document_end: Imediatamente após o DOM estar completo, mas antes que subrecursos como imagens e frames tenham carregado.

Via manifest.json

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

Através de service-worker.js

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 ao longo da vida útil 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 as várias partes da extensão.
  • Persistência: É uma entidade sempre presente, invisível ao usuário mas integral para a funcionalidade da extensão.
  • Geração automática: Se não for explicitamente definida, o navegador criará automaticamente uma página em segundo plano. Essa página gerada automaticamente incluirá todos os background scripts especificados no manifest da extensão, garantindo a operação contínua das tarefas em segundo plano da extensão.

Tip

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

Example background script:

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

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

Para depurar o background script você pode ir aos detalhes da extensão e inspecionar o service worker, isso abrirá as ferramentas de desenvolvedor com o background script:

Páginas de opções e outras

As extensões do navegador podem conter vários tipos de páginas:

  • Action pages são exibidas em um menu suspenso quando o ícone da extensão é clicado.
  • Páginas que a extensão irá carregar em uma nova aba.
  • Option Pages: Esta página é exibida sobre a extensão quando clicada. No manifest anterior, no meu caso consegui acessar esta 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: Assim como a background page, essas 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 a extension-specific APIs, sujeito às permissões definidas para a extensão.

permissions & host_permissions

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

Como as extensões do navegador podem ser tão privilegiadas, uma maliciosa ou comprometida poderia permitir ao atacante diversos meios para roubar informações sensíveis e espionar o usuário.

Confira como essas configurações funcionam e como elas 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 vulnerável.

A configuração padrão para páginas de extensões do navegador é bastante restritiva:

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

Para mais informações sobre CSP e potenciais bypasses confira:

Content Security Policy (CSP) Bypass

web_accessible_resources

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

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

Estas páginas estão acessíveis em URLs como:

chrome-extension://<extension-id>/message.html

Em extensões públicas o extension-id é acessível:

No entanto, se o parâmetro use_dynamic_url no manifest.json for usado, este id pode ser dinâmico.

Tip

Note 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 verificá-la (seção frame-ancestors) antes de confirmar que um ataque ClickJacking é possível.

Ter permissão para acessar 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 em web_accessible_resources e outras páginas da extensão também são capazes de contatar background scripts. Assim, se uma dessas páginas for vulnerável a XSS, isso pode abrir uma vulnerabilidade maior.

Além disso, observe 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 um XSS for encontrado abusando dos mesmos parâmetros, ele pode ser explorado mesmo se a página não estiver configurada em web_accessible_resources.

externally_connectable

De acordo com a docs, a propriedade de manifest "externally_connectable" declara quais extensões e páginas web podem conectar-se à sua extensão via runtime.connect e runtime.sendMessage.

  • Se a chave externally_connectable não estiver declarada no manifest da sua extensão ou estiver declarada como "ids": ["*"], todas as extensões podem se conectar, mas nenhuma página web pode se conectar.
  • Se IDs específicos forem especificados, como em "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], apenas essas aplicações podem se conectar.
  • Se matches forem especificados, esses web apps poderão se conectar:
"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • If it’s specified as empty: "externally_connectable": {}, no app or web will be able to connect.

Quanto menos extensões e URLs 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 para o 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 tenha permissão para comunicar-se 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. (Note que o CSP da página alvo poderia prevenir esses ataques). Esta ideia vem deste writeup.

Communication summary

Extension <–> WebApp

Para comunicar entre o content script e a página web, normalmente são usados post messages. Portanto, na aplicação web você geralmente 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 comunicar-se com a aplicação web enviando um Post Message (e, portanto, a web deve esperar por isso) ou simplesmente fazer a web carregar um novo script.

Inside the extension

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 manipulá-la, declara-se um listener 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 })

</details>

Também é possível enviar mensagens de um background script para um content script 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

**Web apps e extensões de navegador externas permitidas** na configuração `externally_connectable` podem enviar requisições usando :
```javascript
chrome.runtime.sendMessage(extensionId, ...

Onde for necessário mencionar o extension ID.

Native Messaging

É possível que os scripts em segundo plano se comuniquem com binários no sistema, o que pode ser propenso a vulnerabilidades críticas, como RCEs se essa comunicação não for devidamente protegida. Mais sobre isso mais adiante.

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

Web ↔︎ Comunicação com Content Script

Os ambientes onde content scripts operam e onde as host pages existem estã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 host page estabeleça comunicação com o content script, ou indiretamente com a extension através do content script, é necessário utilizar o DOM acessível por ambas as partes como canal de comunicação.

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
)

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

  • event.isTrusted: Isso é True apenas se o evento foi acionado por uma ação do usuário
  • O content script pode esperar uma mensagem somente se o usuário realizar 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 que realizadas, podem ser vulneráveis, então verifique na página a seguir potenciais Post Message bypasses:

PostMessage Vulnerabilities

Iframe

Outra possível forma 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 DOM da web. Então, se o content script estiver lendo algumas informações dele, confiando no DOM da web, a web poderia modificar esses dados (porque a web não deve ser confiável, ou porque a web é 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 Comunicação

Um Content Script pode usar as funções runtime.sendMessage() ou tabs.sendMessage() para enviar uma mensagem JSON-serializável de única vez.

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

Enviar uma requisição a partir de um content script fica assim:

;(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 aba selecionada:

// 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 lidar com a mensagem. Isso é igual em um content script ou página da extensão.

// 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 executada de forma síncrona. Para modificar o onMessage event handler 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 efetivamente. Qualquer resposta subsequente ao mesmo evento não será levada em conta.

Ao criar novas extensões, a preferência deve ser por promises em vez de callbacks. No que diz respeito ao uso de callbacks, a função sendResponse() é considerada válida apenas se for executada diretamente no contexto síncrono, ou se o event handler indicar uma operação assíncrona retornando true. Caso nenhum dos handlers 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

As extensões do navegador também permitem comunicar-se com binaries in the system via stdin. A aplicação deve instalar um json indicando isso em um json como:

{
"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 a aplicação a partir dos scripts de background da extensão do navegador. O path é o caminho para o binário, existe apenas 1 type válido que é stdio (usar stdin e stdout) e os allowed_origins indicam as extensões que podem acessá-lo (e não podem ter wildcard).

Chrome/Chromium vai procurar este json em algumas chaves do registro do Windows e em alguns caminhos no macOS e Linux (mais info na docs).

Tip

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

É assim que fica o código de um background script enviando mensagens para uma aplicação nativa:

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

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

  1. A extensão do navegador tem um padrão curinga para content script.
  2. O content script passa mensagens postMessage para o background script usando sendMessage.
  3. O background script passa a mensagem para a aplicação nativa usando sendNativeMessage.
  4. A aplicação nativa processa a mensagem de forma perigosa, levando à execução de código.

E dentro dele é explicado um exemplo de ir de qualquer página para RCE abusando de uma extensão do navegador.

Sensitive Information in Memory/Code/Clipboard

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

Portanto, a memória da Extensão do navegador não deve ser considerada segura e informações sensíveis como credenciais ou frases mnemônicas não devem ser armazenadas.

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

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

  1. Download the Extensão do navegador & unzipped
  2. Vá para chrome://extensions/ e enable the Developer Mode
  3. Clique no botão Load unpacked

In Firefox you go to about:debugging#/runtime/this-firefox and click Load Temporary Add-on button.

Getting the source code from the store

O código-fonte de uma extensão Chrome pode ser obtido por 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 extensão Chrome pode ser baixado como um arquivo ZIP usando a linha de comando. Isso envolve usar curl para buscar o arquivo ZIP a partir de uma URL específica e então extrair o conteúdo do arquivo 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:
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 do Chrome Web Store. O código-fonte do viewer está disponível no seu GitHub repository.

View source of locally installed extension

Chrome extensions instaladas localmente também podem ser inspecionadas. Veja como:

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

Para identificar extensions, você pode mapear seus IDs para nomes:

  • Habilite o Modo de desenvolvedor na página about:extensions para ver os IDs de cada extension.
  • Dentro da pasta de cada extension, o arquivo manifest.json contém um campo name legível, ajudando a identificar a extension.

Use a File Archiver or Unpacker

Vá ao Chrome Web Store e baixe a extension. O arquivo terá a extensão .crx. Mude a extensão do arquivo de .crx para .zip. Use qualquer file archiver (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 o “Developer mode” no canto superior direito. Clique em “Load unpacked extension…”. Navegue até o diretório da sua extension. Isso não baixa o código-fonte, mas é útil para visualizar e modificar o código de uma extension já baixada ou desenvolvida.

Chrome extension manifest dataset

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

Técnica furtiva para backdoor no Chromium editando diretamente os Preferences por usuário e forjando HMACs válidos, fazendo com que o navegador aceite e ative uma extensão unpacked arbitrária sem prompts ou flags.

Forced Extension Load Preferences Mac Forgery Windows

Detectando Atualizações Maliciosas de Extensões (Static Version Diffing)

Comprometimentos na cadeia de suprimentos frequentemente chegam como malicious updates em extensões anteriormente benignas. Uma abordagem prática e de baixo ruído é comparar um novo pacote de extensão contra a última versão conhecida boa usando análise estática (por exemplo, Assemblyline). O objetivo é alertar sobre deltas de alto sinal em vez de qualquer alteração.

Workflow

  • Submit both versions (old + new) to the same static-analysis profile.
  • Flag new or updated background/service worker scripts (persistência + lógica privilegiada).
  • Flag new or updated content scripts (acesso ao DOM e coleta de dados).
  • Flag new permissions/host_permissions added in manifest.json.
  • Flag new domains extracted from code (potenciais endpoints de C2/exfil).
  • Flag new static-analysis detections (ex.: base64 decode, cookie harvesting, network-request builders, padrões de obfuscação).
  • Flag statistical anomalies como saltos bruscos de entropia ou z-scores outliers em scripts alterados.

Detectando alterações de script com precisão

  • New script added → detectar via diff de manifest.json.
  • Existing script modified (manifest unchanged) → comparar per-file hashes da árvore de arquivos extraída (ex.: saída Extract do Assemblyline). Isso pega atualizações furtivas em workers ou content scripts existentes.

Pre-disclosure detections

Para evitar detecções “modo fácil” baseadas em IOCs já conhecidos, desative serviços alimentados por threat intel e confie em sinais intrínsecos (domínios, assinaturas heurísticas, deltas de script, anomalias de entropia). Isso aumenta as chances de identificar malicious updates antes do reporte público.

Exemplo de lógica de alerta de alta confiança

  • 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 (maior recall, mais ruido).

Serviços chave do Assemblyline para esse workflow:

  • Extract: desempacota a extensão e gera per-file hashes.
  • Characterize: calcula características de arquivo (ex.: entropia).
  • JsJAWS / FrankenStrings / URLCreator: expõem heurísticas JS, strings e domains para diff entre versões.

Security Audit Checklist

Apesar de Browser Extensions terem uma superfície de ataque limitada, algumas podem conter vulnerabilidades ou possíveis melhorias de hardening. As seguintes são as mais comuns:

  • Limit as much as possible requested permissions
  • Limit as much as possible host_permissions
  • Use a strong content_security_policy
  • Limit as much as possible the externally_connectable, if none is needed and possible, do not leave it by default, specify {}
  • If URL vulnerable to XSS or to takeover is mentioned here, an attacker will be able to send messages to the background scripts directly. Very powerful bypass.
  • Limit as much as possible the web_accessible_resources, even empty if possible.
  • If web_accessible_resources is not none, check for ClickJacking
  • If any communication occurs from the extension to the web page, check for XSS vulnerabilities caused in the communication.
  • If Post Messages are used, check for Post Message vulnerabilities.
  • If the Content Script access DOM details, check that they aren’t introducing a XSS if they get modified by the web
  • Make a special emphasis if this communication is also involved in the Content Script -> Background script communication
  • If the background script is communicating via native messaging check the communication is secure and sanitized
  • Sensitive information shouldn’t be stored inside the Browser Extension code
  • Sensitive information shouldn’t be stored inside the Browser Extension memory
  • Sensitive information shouldn’t be stored inside the file system unprotected

Browser Extension Risks

  • O app https://crxaminer.tech/ analisa alguns dados como as permissions que a browser extension solicita para fornecer um nível de risco ao usar a extensão.

Tools

Tarnish

  • Faz pull de qualquer Chrome extension a partir de um link do Chrome webstore.
  • manifest.json viewer: exibe uma versão JSON-prettified do manifest da extensão.
  • Fingerprint Analysis: detecção de web_accessible_resources e geração automática de JavaScript para fingerprinting de Chrome extensions.
  • Potential Clickjacking Analysis: detecção de páginas HTML de extensão com a diretiva web_accessible_resources configurada. Essas podem ser potencialmente vulneráveis a clickjacking dependendo do propósito das páginas.
  • Permission Warning(s) viewer: mostra a lista de avisos de permissão do Chrome que serão exibidos quando o usuário tentar instalar a extensão.
  • Dangerous Function(s): mostra a localização de funções perigosas que poderiam ser exploradas por um atacante (ex.: innerHTML, chrome.tabs.executeScript).
  • Entry Point(s): mostra onde a extensão recebe input usuário/externo. Útil para entender a superfície da extensão e encontrar pontos para enviar dados maliciosamente construídos.
  • Os scanners de Dangerous Function(s) e Entry Point(s) geram para cada alerta:
    • Trecho de código relevante e linha que causou o alerta.
    • Descrição do problema.
    • Botão “View File” para ver o arquivo fonte completo.
    • O path do arquivo alertado.
    • A URI completa da Chrome extension do arquivo alertado.
    • O tipo de arquivo (Background Page script, Content Script, Browser Action, etc.).
    • Se a linha vulnerável está em um arquivo JavaScript, os paths de todas as páginas onde ele é incluído, 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 extensão e potenciais formas de contorná-lo por CDNs permitidos, etc.
  • Known Vulnerable Libraries: usa Retire.js para checar uso de bibliotecas JavaScript conhecidas 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; a primeira execução pode demorar, a segunda (se a extensão não mudou) será quase instantânea.
  • URLs de relatório linkáveis, facilitando compartilhar o relatório 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. Automatio o processo de descompactar os arquivos empacotados para extrair essas features de recursos relevantes em uma extensão como manifest.json, pastas de localização ou arquivos fonte Javascript e HTML.

References

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