Browser Extension Pentesting Methodology

Apoya a HackTricks

Información Básica

Las extensiones de navegador están escritas en JavaScript y son cargadas por el navegador en segundo plano. Tienen su DOM pero pueden interactuar con los DOM de otros sitios. Esto significa que puede comprometer la confidencialidad, integridad y disponibilidad (CIA) de otros sitios.

Componentes Principales

Los diseños de las extensiones se ven mejor cuando se visualizan y constan de tres componentes. Veamos cada componente en profundidad.

Scripts de Contenido

Cada script de contenido tiene acceso directo al DOM de una única página web y, por lo tanto, está expuesto a entradas potencialmente maliciosas. Sin embargo, el script de contenido no contiene permisos aparte de la capacidad de enviar mensajes al núcleo de la extensión.

Núcleo de la Extensión

El núcleo de la extensión contiene la mayoría de los privilegios/accesos de la extensión, pero el núcleo de la extensión solo puede interactuar con el contenido web a través de XMLHttpRequest y scripts de contenido. Además, el núcleo de la extensión no tiene acceso directo a la máquina host.

Binario Nativo

La extensión permite un binario nativo que puede acceder a la máquina host con todos los privilegios del usuario. El binario nativo interactúa con el núcleo de la extensión a través de la interfaz de programación de aplicaciones de plugins de Netscape estándar (NPAPI) utilizada por Flash y otros complementos de navegador.

Límites

Para obtener todos los privilegios del usuario, un atacante debe convencer a la extensión de pasar entradas maliciosas del script de contenido al núcleo de la extensión y del núcleo de la extensión al binario nativo.

Cada componente de la extensión está separado entre sí por fuertes límites protectores. Cada componente se ejecuta en un proceso de sistema operativo separado. Los scripts de contenido y los núcleos de extensión se ejecutan en procesos de sandbox no disponibles para la mayoría de los servicios del sistema operativo.

Además, los scripts de contenido están separados de sus páginas web asociadas al ejecutarse en un montón de JavaScript separado. El script de contenido y la página web tienen acceso al mismo DOM subyacente, pero los dos nunca intercambian punteros de JavaScript, lo que previene la filtración de funcionalidad de JavaScript.

manifest.json

Una extensión de Chrome es solo una carpeta ZIP con una .crx file extension. El núcleo de la extensión es el manifest.json archivo en la raíz de la carpeta, que especifica el diseño, permisos y otras opciones de configuración.

Ejemplo:

{
"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

Los scripts de contenido son cargados cada vez que el usuario navega a una página que coincide, en nuestro caso cualquier página que coincida con la expresión https://example.com/* y no coincida con la regex *://*/*/business*. Se ejecutan como los propios scripts de la página y tienen acceso arbitrario al Modelo de Objetos del Documento (DOM) de la página.

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

Para incluir o excluir más URLs, también es posible usar include_globs y exclude_globs.

Este es un ejemplo de script de contenido que añadirá un botón de explicación a la página cuando la API de almacenamiento para recuperar el valor message del almacenamiento de la extensión.

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

Un mensaje es enviado a las páginas de la extensión por el script de contenido cuando se hace clic en este botón, a través de la utilización de la runtime.sendMessage() API. Esto se debe a la limitación del script de contenido en el acceso directo a las APIs, siendo storage una de las pocas excepciones. Para funcionalidades más allá de estas excepciones, se envían mensajes a las páginas de la extensión con las que los scripts de contenido pueden comunicarse.

Dependiendo del navegador, las capacidades del script de contenido pueden variar ligeramente. Para los navegadores basados en Chromium, la lista de capacidades está disponible en la documentación de Chrome Developers, y para Firefox, el MDN sirve como la fuente principal. También es notable que los scripts de contenido tienen la capacidad de comunicarse con los scripts de fondo, lo que les permite realizar acciones y transmitir respuestas de vuelta.

Para ver y depurar scripts de contenido en Chrome, se puede acceder al menú de herramientas de desarrollador de Chrome desde Opciones > Más herramientas > Herramientas de desarrollador O presionando Ctrl + Shift + I.

Una vez que se muestran las herramientas de desarrollador, se debe hacer clic en la pestaña Fuente, seguida de la pestaña Scripts de Contenido. Esto permite observar los scripts de contenido en ejecución de varias extensiones y establecer puntos de interrupción para rastrear el flujo de ejecución.

Scripts de contenido inyectados

Tenga en cuenta que los Scripts de Contenido no son obligatorios ya que también es posible inyectar dinámicamente scripts y inyectarlos programáticamente en páginas web a través de tabs.executeScript. Esto en realidad proporciona un control más granular.

Para la inyección programática de un script de contenido, se requiere que la extensión tenga permisos de host para la página en la que se van a inyectar los scripts. Estos permisos pueden asegurarse ya sea solicitándolos dentro del manifiesto de la extensión o de manera temporal a través de activeTab.

Ejemplo de extensión basada en activeTab

manifest.json
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
  • Inyectar un archivo JS al hacer clic:

// 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"]
});
});
  • Inyectar una función al hacer clic:

//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,
});
});

Ejemplo con permisos 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 o excluir más URLs, también es posible usar include_globs y exclude_globs.

Content Scripts run_at

El campo run_at controla cuándo se inyectan los archivos JavaScript en la página web. El valor preferido y predeterminado es "document_idle".

Los valores posibles son:

  • document_idle: Siempre que sea posible

  • document_start: Después de cualquier archivo de css, pero antes de que se construya cualquier otro DOM o se ejecute cualquier otro script.

  • document_end: Inmediatamente después de que el DOM esté completo, pero antes de que se hayan cargado subrecursos como imágenes y marcos.

Via manifest.json

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

A través de service-worker.js

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

background

Los mensajes enviados por los scripts de contenido son recibidos por la página de fondo, que desempeña un papel central en la coordinación de los componentes de la extensión. Notablemente, la página de fondo persiste a lo largo de la vida de la extensión, operando discretamente sin interacción directa del usuario. Posee su propio Modelo de Objetos del Documento (DOM), lo que permite interacciones complejas y gestión del estado.

Puntos Clave:

  • Rol de la Página de Fondo: Actúa como el centro neurálgico de la extensión, asegurando la comunicación y coordinación entre las diversas partes de la extensión.

  • Persistencia: Es una entidad siempre presente, invisible para el usuario pero integral para la funcionalidad de la extensión.

  • Generación Automática: Si no se define explícitamente, el navegador creará automáticamente una página de fondo. Esta página generada automáticamente incluirá todos los scripts de fondo especificados en el manifiesto de la extensión, asegurando el funcionamiento sin problemas de las tareas de fondo de la extensión.

La conveniencia proporcionada por el navegador al generar automáticamente una página de fondo (cuando no se declara explícitamente) asegura que todos los scripts de fondo necesarios estén integrados y operativos, simplificando el proceso de configuración de la extensión.

Ejemplo de script de fondo:

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

Utiliza la API runtime.onMessage para escuchar mensajes. Cuando se recibe un mensaje de "explain", utiliza la API tabs para abrir una página en una nueva pestaña.

Para depurar el script de fondo, puedes ir a los detalles de la extensión e inspeccionar el service worker, esto abrirá las herramientas de desarrollo con el script de fondo:

Páginas de opciones y otras

Las extensiones de navegador pueden contener varios tipos de páginas:

  • Las páginas de acción se muestran en un menú desplegable cuando se hace clic en el ícono de la extensión.

  • Páginas que la extensión cargará en una nueva pestaña.

  • Páginas de opciones: Esta página se muestra en la parte superior de la extensión cuando se hace clic. En el manifiesto anterior, en mi caso, pude acceder a esta página en chrome://extensions/?options=fadlhnelkbeojnebcbkacjilhnbjfjca o haciendo clic en:

Ten en cuenta que estas páginas no son persistentes como las páginas de fondo, ya que cargan contenido dinámicamente según sea necesario. A pesar de esto, comparten ciertas capacidades con la página de fondo:

  • Comunicación con scripts de contenido: Similar a la página de fondo, estas páginas pueden recibir mensajes de scripts de contenido, facilitando la interacción dentro de la extensión.

  • Acceso a APIs específicas de la extensión: Estas páginas disfrutan de acceso completo a APIs específicas de la extensión, sujeto a los permisos definidos para la extensión.

permissions & host_permissions

permissions y host_permissions son entradas del manifest.json que indicarán qué permisos tiene la extensión del navegador (almacenamiento, ubicación...) y en qué páginas web.

Dado que las extensiones de navegador pueden ser tan privilegiadas, una maliciosa o una que haya sido comprometida podría permitir al atacante diferentes medios para robar información sensible y espiar al usuario.

Consulta cómo funcionan estas configuraciones y cómo podrían ser abusadas en:

BrowExt - permissions & host_permissions

content_security_policy

Una política de seguridad de contenido también puede declararse dentro del manifest.json. Si hay una definida, podría ser vulnerable.

La configuración predeterminada para las páginas de extensiones de navegador es bastante restrictiva:

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

Para más información sobre CSP y posibles bypasses, consulta:

Content Security Policy (CSP) Bypass

web_accessible_resources

Para que una página web acceda a una página de una extensión de navegador, una página .html por ejemplo, esta página debe mencionarse en el campo web_accessible_resources del manifest.json. Por ejemplo:

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

Estas páginas son accesibles en URL como:

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

En extensiones públicas, el extension-id es accesible:

Sin embargo, si se utiliza el parámetro use_dynamic_url en manifest.json, este id puede ser dinámico.

Ten en cuenta que incluso si una página se menciona aquí, podría estar protegida contra ClickJacking gracias a la Content Security Policy. Así que también necesitas verificarlo (sección frame-ancestors) antes de confirmar que un ataque de ClickJacking es posible.

El acceso a estas páginas hace que estas páginas sean potencialmente vulnerables a ClickJacking:

BrowExt - ClickJacking

Permitir que estas páginas se carguen solo por la extensión y no por URLs aleatorias podría prevenir ataques de ClickJacking.

Ten en cuenta que las páginas de web_accessible_resources y otras páginas de la extensión también son capaces de contactar scripts de fondo. Así que si una de estas páginas es vulnerable a XSS, podría abrir una vulnerabilidad mayor.

Además, ten en cuenta que solo puedes abrir páginas indicadas en web_accessible_resources dentro de iframes, pero desde una nueva pestaña es posible acceder a cualquier página en la extensión conociendo el ID de la extensión. Por lo tanto, si se encuentra un XSS abusando de los mismos parámetros, podría ser explotado incluso si la página no está configurada en web_accessible_resources.

externally_connectable

Según la documentación, la propiedad de manifiesto "externally_connectable" declara qué extensiones y páginas web pueden conectarse a tu extensión a través de runtime.connect y runtime.sendMessage.

  • Si la clave externally_connectable no está declarada en el manifiesto de tu extensión o está declarada como "ids": ["*"], todas las extensiones pueden conectarse, pero ninguna página web puede conectarse.

  • Si se especifican IDs específicos, como en "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], solo esas aplicaciones pueden conectarse.

  • Si se especifican coincidencias, esas aplicaciones web podrán conectarse:

"matches": [
"https://*.google.com/*",
"*://*.chromium.org/*",
  • Si se especifica como vacío: "externally_connectable": {}, ninguna aplicación o web podrá conectarse.

Cuantas menos extensiones y URLs se indiquen aquí, menor será la superficie de ataque.

Si una página web vulnerable a XSS o takeover se indica en externally_connectable, un atacante podrá enviar mensajes directamente al script de fondo, eludiendo completamente el Content Script y su CSP.

Por lo tanto, este es un bypass muy poderoso.

Además, si el cliente instala una extensión maliciosa, incluso si no se le permite comunicarse con la extensión vulnerable, podría inyectar datos XSS en una página web permitida o abusar de las APIs WebRequest o DeclarativeNetRequest para manipular solicitudes en un dominio objetivo alterando la solicitud de una archivo JavaScript. (Tenga en cuenta que el CSP en la página objetivo podría prevenir estos ataques). Esta idea proviene de este informe.

Resumen de comunicación

Extensión <--> WebApp

Para comunicarse entre el script de contenido y la página web, generalmente se utilizan mensajes post. Por lo tanto, en la aplicación web generalmente encontrará llamadas a la función window.postMessage y en el script de contenido oyentes como window.addEventListener. Sin embargo, tenga en cuenta que la extensión también podría comunicarse con la aplicación web enviando un Post Message (y por lo tanto la web debería esperarlo) o simplemente hacer que la web cargue un nuevo script.

Dentro de la extensión

Generalmente se utiliza la función chrome.runtime.sendMessage para enviar un mensaje dentro de la extensión (generalmente manejado por el script background) y para recibirlo y manejarlo se declara un oyente llamando a chrome.runtime.onMessage.addListener.

También es posible usar chrome.runtime.connect() para tener una conexión persistente en lugar de enviar mensajes individuales, es posible usarlo para enviar y recibir mensajes como en el siguiente ejemplo:

chrome.runtime.connect() ejemplo

```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>

También es posible enviar mensajes desde un script de fondo a un script de contenido ubicado en una pestaña específica llamando a **`chrome.tabs.sendMessage`** donde necesitarás indicar el **ID de la pestaña** a la que enviar el mensaje.

### Desde `externally_connectable` permitido a la extensión

**Las aplicaciones web y las extensiones de navegador externas permitidas** en la configuración de `externally_connectable` pueden enviar solicitudes usando :
```javascript
chrome.runtime.sendMessage(extensionId, ...

Donde es necesario mencionar el extension ID.

Mensajería Nativa

Es posible que los scripts de fondo se comuniquen con binarios dentro del sistema, lo que podría ser propenso a vulnerabilidades críticas como RCEs si esta comunicación no está debidamente asegurada. Más sobre esto más tarde.

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

Web ↔︎ Comunicación entre Scripts de Contenido

Los entornos donde operan los scripts de contenido y donde existen las páginas anfitrionas están separados entre sí, asegurando aislamiento. A pesar de este aislamiento, ambos tienen la capacidad de interactuar con el Modelo de Objetos del Documento (DOM) de la página, un recurso compartido. Para que la página anfitriona participe en la comunicación con el script de contenido, o indirectamente con la extensión a través del script de contenido, se requiere utilizar el DOM que es accesible por ambas partes como canal de comunicación.

Mensajes Post

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

Una comunicación segura de Post Message debe verificar la autenticidad del mensaje recibido, esto se puede hacer verificando:

  • event.isTrusted: Esto es Verdadero solo si el evento fue desencadenado por una acción del usuario.

  • El script de contenido podría estar esperando un mensaje solo si el usuario realiza alguna acción.

  • dominio de origen: podría estar esperando un mensaje solo de una lista permitida de dominios.

  • Si se utiliza una expresión regular, ten mucho cuidado.

  • Fuente: received_message.source !== window se puede usar para verificar si el mensaje fue desde la misma ventana donde el Script de Contenido está escuchando.

Las verificaciones anteriores, incluso si se realizan, podrían ser vulnerables, así que verifica en la siguiente página posibles bypasses de Post Message:

Iframe

Otra posible forma de comunicación podría ser a través de URLs de Iframe, puedes encontrar un ejemplo en:

DOM

Esto no es "exactamente" una forma de comunicación, pero el web y el script de contenido tendrán acceso al DOM web. Así que, si el script de contenido está leyendo alguna información de él, confiando en el DOM web, el web podría modificar estos datos (porque el web no debería ser confiable, o porque el web es vulnerable a XSS) y comprometer el Script de Contenido.

También puedes encontrar un ejemplo de un XSS basado en DOM para comprometer una extensión de navegador en:

Comunicación entre Script de Contenido ↔︎ Script de Fondo

Un Script de Contenido puede usar las funciones runtime.sendMessage() o tabs.sendMessage() para enviar un mensaje serializable en JSON de una sola vez.

Para manejar la respuesta, usa la Promesa devuelta. Aunque, por compatibilidad hacia atrás, aún puedes pasar un callback como el último argumento.

Enviar una solicitud desde un script de contenido se ve así:

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

Enviando una solicitud desde la extensión (generalmente un script de fondo). Ejemplo de cómo enviar un mensaje al script de contenido en la pestaña seleccionada:

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

En el lado receptor, necesitas configurar un runtime.onMessage escuchador de eventos para manejar el mensaje. Esto se ve igual desde un script de contenido o una página de extensión.

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

En el ejemplo destacado, sendResponse() se ejecutó de manera sincrónica. Para modificar el controlador de eventos onMessage para la ejecución asincrónica de sendResponse(), es imperativo incorporar return true;.

Una consideración importante es que en escenarios donde múltiples páginas están configuradas para recibir eventos onMessage, la primera página en ejecutar sendResponse() para un evento específico será la única capaz de entregar la respuesta de manera efectiva. Cualquier respuesta posterior al mismo evento no será tenida en cuenta.

Al crear nuevas extensiones, la preferencia debe ser hacia promesas en lugar de callbacks. Con respecto al uso de callbacks, la función sendResponse() se considera válida solo si se ejecuta directamente dentro del contexto sincrónico, o si el controlador de eventos indica una operación asincrónica al devolver true. Si ninguno de los controladores devuelve true o si la función sendResponse() se elimina de la memoria (recolectada por el garbage collector), el callback asociado con la función sendMessage() se activará por defecto.

Native Messaging

Las extensiones del navegador también permiten comunicarse con binarios en el sistema a través de stdin. La aplicación debe instalar un json que indique esto en un 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/"]
}

Donde el name es la cadena pasada a runtime.connectNative() o runtime.sendNativeMessage() para comunicarse con la aplicación desde los scripts de fondo de la extensión del navegador. El path es la ruta al binario, solo hay 1 type válido que es stdio (usar stdin y stdout) y los allowed_origins indican las extensiones que pueden acceder a él (y no pueden tener comodín).

Chrome/Chromium buscará este json en algunos registros de Windows y algunas rutas en macOS y Linux (más información en los docs).

La extensión del navegador también necesita el permiso nativeMessaing declarado para poder usar esta comunicación.

Así es como se ve un código de script de fondo enviando mensajes a una aplicación nativa:

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

En esta publicación del blog, se propone un patrón vulnerable que abusa de los mensajes nativos:

  1. La extensión del navegador tiene un patrón comodín para el script de contenido.

  2. El script de contenido pasa mensajes postMessage al script de fondo usando sendMessage.

  3. El script de fondo pasa el mensaje a la aplicación nativa usando sendNativeMessage.

  4. La aplicación nativa maneja el mensaje de manera peligrosa, lo que lleva a la ejecución de código.

Y dentro de esto se explica un ejemplo de cómo ir de cualquier página a RCE abusando de una extensión del navegador.

Información Sensible en Memoria/Código/Portapapeles

Si una Extensión del Navegador almacena información sensible dentro de su memoria, esto podría ser volcado (especialmente en máquinas Windows) y buscado para esta información.

Por lo tanto, la memoria de la Extensión del Navegador no debe considerarse segura y la información sensible como credenciales o frases mnemotécnicas no debe ser almacenada.

Por supuesto, no ponga información sensible en el código, ya que será pública.

Para volcar la memoria del navegador, podría volcar la memoria del proceso o ir a la configuración de la extensión del navegador y hacer clic en Inspeccionar pop-up -> En la sección Memoria -> Tomar un instantáneo y CTRL+F para buscar dentro del instantáneo información sensible.

Además, información altamente sensible como claves mnemotécnicas o contraseñas no debería permitirse que se copien en el portapapeles (o al menos eliminarla del portapapeles en unos segundos) porque entonces los procesos que monitorean el portapapeles podrán obtenerlas.

Cargando una Extensión en el Navegador

  1. Descargue la Extensión del Navegador y descomprímala.

  2. Vaya a chrome://extensions/ y active el Modo de Desarrollador.

  3. Haga clic en el botón Cargar descomprimido.

En Firefox, vaya a about:debugging#/runtime/this-firefox y haga clic en el botón Cargar complemento temporal.

Obteniendo el código fuente de la tienda

El código fuente de una extensión de Chrome se puede obtener a través de varios métodos. A continuación se detallan explicaciones e instrucciones para cada opción.

Descargar Extensión como ZIP a través de la Línea de Comandos

El código fuente de una extensión de Chrome se puede descargar como un archivo ZIP utilizando la línea de comandos. Esto implica usar curl para obtener el archivo ZIP de una URL específica y luego extraer el contenido del archivo ZIP a un directorio. Aquí están los pasos:

  1. Reemplace "extension_id" con el ID real de la extensión.

  2. Ejecute los siguientes 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"

Usa el sitio web CRX Viewer

https://robwu.nl/crxviewer/

Usa la extensión CRX Viewer

Otro método conveniente es usar el Chrome Extension Source Viewer, que es un proyecto de código abierto. Se puede instalar desde la Chrome Web Store. El código fuente del visor está disponible en su repositorio de GitHub.

Ver el código fuente de la extensión instalada localmente

Las extensiones de Chrome instaladas localmente también se pueden inspeccionar. Aquí te explicamos cómo:

  1. Accede a tu directorio de perfil local de Chrome visitando chrome://version/ y localizando el campo "Profile Path".

  2. Navega a la subcarpeta Extensions/ dentro del directorio del perfil.

  3. Esta carpeta contiene todas las extensiones instaladas, típicamente con su código fuente en un formato legible.

Para identificar extensiones, puedes mapear sus IDs a nombres:

  • Habilita el Modo Desarrollador en la página about:extensions para ver los IDs de cada extensión.

  • Dentro de la carpeta de cada extensión, el archivo manifest.json contiene un campo name legible, ayudándote a identificar la extensión.

Usa un archivador de archivos o descompresor

Ve a la Chrome Web Store y descarga la extensión. El archivo tendrá una extensión .crx. Cambia la extensión del archivo de .crx a .zip. Usa cualquier archivador de archivos (como WinRAR, 7-Zip, etc.) para extraer el contenido del archivo ZIP.

Usa el Modo Desarrollador en Chrome

Abre Chrome y ve a chrome://extensions/. Habilita "Modo desarrollador" en la parte superior derecha. Haz clic en "Cargar extensión descomprimida...". Navega al directorio de tu extensión. Esto no descarga el código fuente, pero es útil para ver y modificar el código de una extensión ya descargada o desarrollada.

Conjunto de datos del manifiesto de la extensión de Chrome

Para intentar detectar extensiones de navegador vulnerables, podrías usar el https://github.com/palant/chrome-extension-manifests-dataset y revisar sus archivos de manifiesto en busca de señales potencialmente vulnerables. Por ejemplo, para verificar extensiones con más de 25000 usuarios, content_scripts y el permiso 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')"

Lista de Verificación de Auditoría de Seguridad

Aunque las extensiones de navegador tienen una superficie de ataque limitada, algunas de ellas pueden contener vulnerabilidades o mejoras de endurecimiento potenciales. Las siguientes son las más comunes:

Herramientas

  • Extrae cualquier extensión de Chrome de un enlace proporcionado de la tienda web de Chrome.

  • Visor de manifest.json: simplemente muestra una versión JSON formateada del manifiesto de la extensión.

  • Análisis de huellas digitales: Detección de web_accessible_resources y generación automática de JavaScript de huellas digitales de extensiones de Chrome.

  • Análisis de Clickjacking potencial: Detección de páginas HTML de extensiones con la directiva web_accessible_resources establecida. Estas son potencialmente vulnerables a clickjacking dependiendo del propósito de las páginas.

  • Visor de advertencias de permisos: que muestra una lista de todas las advertencias de permisos de Chrome que se mostrarán cuando un usuario intente instalar la extensión.

  • Función(es) peligrosa(s): muestra la ubicación de funciones peligrosas que podrían ser potencialmente explotadas por un atacante (por ejemplo, funciones como innerHTML, chrome.tabs.executeScript).

  • Punto(s) de entrada: muestra dónde la extensión recibe entrada de usuario/externa. Esto es útil para entender la superficie de una extensión y buscar puntos potenciales para enviar datos maliciosamente elaborados a la extensión.

  • Tanto los escáneres de Función(es) Peligrosa(s) como de Punto(s) de Entrada tienen lo siguiente para sus alertas generadas:

  • Fragmento de código relevante y línea que causó la alerta.

  • Descripción del problema.

  • Un botón de “Ver Archivo” para ver el archivo fuente completo que contiene el código.

  • La ruta del archivo alertado.

  • La URI completa de la extensión de Chrome del archivo alertado.

  • El tipo de archivo que es, como un script de Página de Fondo, Script de Contenido, Acción del Navegador, etc.

  • Si la línea vulnerable está en un archivo JavaScript, las rutas de todas las páginas donde está incluida, así como el tipo de estas páginas y el estado de web_accessible_resource.

  • Analizador de Política de Seguridad de Contenido (CSP) y verificador de bypass: Esto señalará debilidades en la CSP de su extensión y también iluminará cualquier forma potencial de eludir su CSP debido a CDNs en la lista blanca, etc.

  • Bibliotecas Vulnerables Conocidas: Esto utiliza Retire.js para verificar cualquier uso de bibliotecas JavaScript conocidas como vulnerables.

  • Descargar extensión y versiones formateadas.

  • Descargar la extensión original.

  • Descargar una versión embellecida de la extensión (HTML y JavaScript auto formateados).

  • Caché automática de resultados de escaneo, ejecutar un escaneo de extensión tomará un buen tiempo la primera vez que lo ejecute. Sin embargo, la segunda vez, suponiendo que la extensión no ha sido actualizada, será casi instantáneo debido a que los resultados están en caché.

  • URLs de informes enlazables, fácilmente enlazar a alguien más a un informe de extensión generado por tarnish.

El proyecto Neto es un paquete de Python 3 concebido para analizar y desentrañar características ocultas de plugins y extensiones de navegador para navegadores bien conocidos como Firefox y Chrome. Automatiza el proceso de descompresión de los archivos empaquetados para extraer estas características de recursos relevantes en una extensión como manifest.json, carpetas de localización o archivos fuente de Javascript y HTML.

Referencias

Last updated