If the Electron application is not configured with appropriate features any XSS vulnerability can be disruptive, and result in 0-click client-side RCE attacks. This kind of attack exploits the Electron model and bypasses user’s sandbox mechanism.
For the presented example lets assume that we’ve discovered a stored XSS vulnerability in the mail box functionality (ie XSS can be achieved by sending an email to a user) in email wrapper application and that the current application is built specifically as a Desktop application.
If reviewing the source code grep for and find Electron native functions such as: (Desktop.shell.openExternal
, Desktop.shell.realPath
, etc ...) This means that the architecture of Electron native application (desktop app) is poorly designed and contextIsolation
and nodeIntegration
settings are potentially disabled. In case where the Electron app had been configured securely, the Electron native code loaded from remote origin would never execute. Those settings can also be checked by decompiling the Desktop application’s .asar
file, and reviewing the webPreferences
settings for all renderers in the desktop app.
While reviewing the code of our example decompiled desktop application (.asar
file), it is not a surprise to find that all the application window and tab renderers are set to contextIsolation:false
. Any architectural solution that allows Electron native functions to be loaded from remote origins with application’s client-side JavaScript code introduces significant security and maintainability issues.
Based on this, we could plan an attack vector to achieve client-side RCE in two possible ways:
preload.js
script by abusing the already existing code of the Desktop application__proto__
objectThe contextIsolation
is an Electron feature that allows developers to run code in preload scripts and in Electron APIs in a dedicated JavaScript context. In practice, this means that global objects such as Array.prototype.push
or JSON.parse
cannot be modified by scripts running in the renderer process.
It introduces separated contexts between Electron's native JavaScript code and the web application’s JavaScript code, so that execution of the application's JavaScript code does not affect the native code.
If contextIsolation
is disabled, the web application’s JavaScript code may affect execution of Electron's native JavaScript code on the renderer and preload scripts. This behavior is dangerous because Electron allows the web application’s JavaScript code to use the Node.js features regardless of the nodeIntegration
option. By interfering with them from the function overridden in the web page, RCE can be achieved even if nodeIntegration
is set to false
.
Even if nodeIntegration: false
is used, to truly enforce strong isolation and prevent the use of Node primitives contextIsolation
must also be used.
By reviewing the preload.js
scripts it’s fairly common to identify functionality that could be abused in the attack, for example require
function can be reassigned and redeclared with a new namespace to avoid confusions with the require
functions from the main renderer.