Atom.io Misconfiguration Allowed Code Execution on Untrusted Networks
Developers have increasingly become a more valuable target to compromise in recent years. The DevOps movement means they have more access to production, not to mention the plethora of source code and keys that you are likely to find.
This post is going to show you how a production configuration mistake on the atom.io domain that put users of the Atom editor at risk.
Note this issue is now fixed thanks to the GitHub Security team.
Atom’s package manager apm uses npm to fulfill it’s dependencies and for native modules this means using node-gyp when code needs to be compiled.
As part of the compilation step for native modules, node-gyp downloads a tarball for headers from a specified dist-url and a list of SHASUMS for integrity verification.
These urls are configured with a flag, something like this
node-gyp rebuild --dist-url=https://atom.io/download/atom-shell
This is where the subtle bug gets introduced. When auditing the source someone might look at this, confirm that HTTPS is being used and move on. They might even go a step further and curl the url and verify that it is in fact using HTTPS and you would be correct that it is.
The dist-url and SHASUM url’s that are derived by node-gyp are forwarded to an S3 bucket over HTTP by the atom.io web service.
When https://atom.io/download/atom-shell/v0.30.6/node-v0.30.6.tar.gz is requested a 302 is returned with a redirect location of http://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist/v0.30.6/node-v0.30.6.tar.gz
The same goes for the SHASUM url
That means if we can get between the developer and atom.io we can control the content returned for the dist tarball and the SHASUM file.
But before getting that far I had to figure out how to backdoor the dist tarball so that I could have code execution. Knowing nothing about how node-gyp works, and finding it to be a rats nest of gibberish this took a while. I’m sure there are other ways as well, such as causing a backdoor to be compiled into the final library. I went with what I felt was a simpler route, as attackers tend to do.
What I found was that node-gyp loads a file named
common.gypi. This file provides ways to specify defaults and configuration for gyp. The proof of concept below includes a default target with an action to execute the say command, however this could be any command the attacker wants to run.
‘action’: [‘/usr/bin/say’, ‘ha ha ha ha ha ha powned’],
Once I had reliable code execution during a node-gyp installation, I had to perform a MITM (man in the middle) attack and swap out the tarball and the SHASUMS file.
For this task I used Bettercap, an extensible MITM framework with many already built plugins. Since what I had to do was a bit custom, I wrote a custom plugin to replace more than one file on the fly.
First of all, not all modules trigger this condition because there are plenty of modules that are not native modules.
Secondly, node-gyp caches the dist content so if it’s already on disk it won’t be downloaded again.
This vulnerability was found because of the research we do to protect our clients and the users of the Node Security Platform. If you enjoyed this post and build node.js applications, consider signing up for continuous security monitoring.
Timeline Fun Facts
10.28.2016 — Vulnerability found
10.29.2016 — Confirmed exploitability
10.30.2016 — Notified atom.io via email
10.31.2016 — Notified AtomEditor via twitter
11.03.2016 — Submitted to GitHub bounty via hackerone.com
11.03.2016 — Received bug confirmation
11.05.2016 — Verified fix was in place
11.07.2016 — GithHub Security resolved the issue & issued a bounty
Big thanks to the GitHub Security team for their quick response and good communication with the reported issue.