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.
General application architecture review
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.realPath , etc …) This means that the architecture of Electron native application (desktop app) is poorly designed 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.
Planning an attack vector
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
Based on this, we could plan an attack vector to achieve client-side RCE in two possible ways:
- Attack the
preload.jsscript by abusing the already existing code of the Desktop application
JSON.parse cannot be modified by scripts running in the renderer process.
nodeIntegration option. By interfering with them from the function overridden in the web page, RCE can be achieved even if
nodeIntegration is set to
nodeIntegration: false is used, to truly enforce strong isolation and prevent the use of Node primitives
contextIsolation must also be used.
Back to the attack
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.