Skip to content

CVE-2018-15685 - Electron WebPreferences Remote Code Execution Finding

    

Contrast Labs has discovered a remote code execution (RCE) vulnerability affecting apps with the ability to open nested child windows on Electron versions (3.0.0-beta.6, 2.0.7, 1.8.7, and 1.7.15). This vulnerability has been assigned the CVE identifier CVE-2018-15685.

POC Available at: https://github.com/matt-/CVE-2018-15685

What's Electron?

Electron is a framework that powers many of the applications you use every day. Slack, Atom, Visual Studio Code, WordPress Desktop, Github Desktop, Skype, and Google Chat are just a few applications built on the Electron framework. It allows a developer to quickly port a traditional web application to a native cross platform desktop application.

Vulnerability Details

A recent post was made by @SecurityMB about a security issue in Google Chat where he could create a link that, when clicked, would redirect away from a Google site to content controlled by the attacker, but it would stay inside the Electron app. Upon Google’s review, they found that the reported issue could lead to remote code execution and paid a sizeable bounty.

Although Google quickly fixed the issue related to the redirect, I could not help but wonder what exactly the path to code execution could be. Google Chat is built on the Electron framework. Electron has a detailed security section (available here)and Google followed most of these practices. More specifically, they set “nodeIntegration” to false. Normally the web content of an Electron window has access to the Node Javascript engine underneath it. Node has many core libraries that give you access to the file system and let you execute code. This can be handy when building an app, but something you don’t want user-controlled code to have access to. With “nodeIntegration” set to false, even with the ability to load remote content we “should” be safe.

However, after a bit of testing I came across a relatively simple payload that gave me access to the Node bindings:

open('about:blank').open('data:text/html,<script>document.write(process.cwd())</script>')

At first, I thought that this must have just been an oversight by the Google Chat team, but after digging some more, I discovered that was not the case. Every window that is created should have these properties set:

After a bit more digging and a couple of conversations with Luca Carettoni https://twitter.com/lucacarettoni  (who has done some really great research / work with Electron in the past), it became apparent that this was a bug in the core Electron framework. We came across what seemed to be related to a commit in an unmerged PR: https://github.com/electron/electron/pull/13222/commits/444b6f987bd793989999f64f40062f68217cf0ba

Properties of the window were not being inherited properly through nested windows and iframes. This meant vulnerable windows are able to be spawned by any application where:

  • user code runs inside an iframe, or
  • can create an iframe, or
  • if you open any of your windows with the “nativeWindowOpen: true” or “sandbox: true” options

(It’s a bit ironic that the two conditions leading to the issue are security controls)

See a video here.

We then followed appropriate channels to notify the various teams.

The Electron team responded quickly to the issue, and had a patch ready to go in just a couple of days. They were truly great to work with!

At the end of the day, the “root cause” of the issue is insecure default settings. In this case, with the default settings, a window has access to Node bindings and is not isolated. So, with this bug, when a window did not inherit properties as expected, it fell back to the insecure defaults. It would be great if the fallback was safe defaults. While changing an API can be a difficult process I hope this is something the Electron Team will work towards.

Recommendations for Electron

  1. Follow the security guidelines at https://github.com/electron/electron/blob/master/docs/tutorial/security.md
    These are not the default so you need to make sure these are in place.
  2. Most importantly, set contextIsolation to true and nodeIntegration to false.
  3. Don’t allow user controlled HTML / Javascript (or an XSS) in an electron window if you can avoid it.
  4. Handle how new windows are created (or block them if they are not needed)
    mainWindow.webContents.on('new-window', e => e.preventDefault())

Protecting your Enterprise from similar CVEs  

As you leverage third party frameworks, be sure to validate the default security settings. Default values are often not those optimized for security.

We'd also like to thank the Electron team for being extremely responsive and for quickly working to provide a patch.

Matt Austin, Director of Security Research

Matt Austin, Director of Security Research

Matt is an accomplished application security expert with over 11 years of experience focused on security research, development, and engineering.