Recently I took a look at Atom, a text editor by GitHub. With a little bit of work, I was able to chain multiple vulnerabilities in Atom into an actual Remote Code Execution.
The vulnerabilities have been fixed in the 1.21.1 release on October 12th, 2017 after I reported it via their HackerOne program. In case you want to reproduce those issues yourself, you can still find the old version as a GitHub release.
Bringing web security issues to desktop apps
Of course, that’s an oversimplification. There are several ways to mitigate the impact of an XSS vulnerability in Electron. In fact, some are discussed in the issue tracker itself. However, as with any mitigation, if applied incorrectly they can potentially be bypassed.
Mitigating XSS with CSP
Before we’re looking at the vulnerability itself, let’s take a look at how GitHub decided to mitigate XSS issues within Atom: using Content-Security-Policy. If you look at
index.html of Atom you’ll see the following policy applied:
How Atom parses Markdown files
When dealing with software that contains parsers or preview generators of any kind, spending extra time on those components often pays back. In a lot of cases, the parsing libraries are some third-party components and may have been implemented with different security concerns in mind. Security lies in the eye of the beholder and the original author may have had totally different requirements. For example, they may have assumed that the library is only called with trusted input.
So my first step was taking a look at how Atom parses Markdown files. The relevant code for this default component can be found at atom/markdown-preview on GitHub. Quickly, I noticed, that the Markdown parser also seems to parse arbitrary HTML documents:
And as it turns out, there is! As can be seen below the
script statement does not appear in the DOM.
So a quick research on GitHub turned up that the rendering of arbitrary HTML documents is in fact intended. For this reason, the sanitization mode of the used Markdown library got reverted “atom/markdown-preview#73”, and a custom sanitization function has been introduced:
While the sanitization function is already very weak, bypassing it using one of the countless on-listeners would merely have triggered a Content-Security-Policy violation. Thus the malicious payload wouldn’t be executed.
However, it also told us that we could insert any other kind of HTML payload. So let’s take a closer look at one of the previous screenshot:
Apparently, Atom is executed under the protocol
So I quickly created a file named
hacked.html in my home folder with the following content:
Simply embedding that using an
Chaining with a local DOM XSS
- The user has to actively open a malicious Markdown document
- The user has to open the preview pane for the Markdown document
So in a real world, this seemed a little bit far-fetched for exploitation. However, what if there would be a local file that contained a DOM XSS vulnerability? That would mean a successful exploitation would already be way more likely.
So I decided to take a look at the bundled HTML files. Luckily, on OS X, applications are just a bundle of files. So the Atom bundle can be accessed under
A quick search for HTML files in the bundle found some files:
Now you can either use some kind of statical analysis, or check those HTML files yourself. Since they were so few, I went the manual way and
/Applications/Atom.app/Contents/Resources/app/apm/node_modules/clone/test-apart-ctx.html looked interesting:
There is an
eval call on
document.location.search which is basically everything after the
? in an URL. Also the Content-Security-Police of Atom allowed
eval statements so opening something like the following should open an alert box:
Executing arbitrary local code
window.top object and use the NodeJS
require function to access the
URL-encoded would the previous exploit now look like the following:
And in fact, just by opening said Markdown document the Calculator.app would open:
Doing the whole thing remotely
While above steps make the issue already way more exploitable, it still requires the victim to open a malicious Markdown document. However, that’s not the only place where Atom renders Markdown documents.
After performing a short grep search over the Atom source code, there was another module which rendered Markdown files: The atom settings, atom/settings-view. And in fact, the sanitization method also seemed rather lacking:
And in fact, the Markdown parser was also here affected. But the impact was way worse.
Atom supports so-called “Packages”, which are community-supplied, and available from atom.io/packages. And those can define a README in Markdown format which will be rendered in the Atom settings view.
So a malicious attacker would just have to register a bunch of malicious packages for every letter or offer a few packages with similar names to existing ones. As soon as someone clicked on the name to see the full entry (not installing it!), the malicious code would already be executed.
How GitHub fixed this issue
After some discussion with GitHub, this issue has been resolved by:
- Removing the unnecessary HTML files from the bundle
- Sanitizing the Markdown using DOMPurify
While not a perfect solution, this should already act as a good first mitigation. Also while they could have switched to a stricter Markdown parser, this would probably have broken a lot of existing users’ workflows.