Protecting Node.js Applications from Zip Slip
Zip Slip is a recently coined phrase for a variant of the classical Path Traversal attack. In order to exploit this attack, one creates a compressed archive containing files which include
.. in their directory name. Then, when unpatched libraries attempt to extract these files, the extracted files can be written to unintended
As an example, if an attacker uploads multiple
*.zip files to a service, each containing different permutations of
../../etc/passwd, etc., and assuming the process has write privileges to
/etc/passwd, then the process which extracts the file would overwrite this important OS file. This can then leave the OS in a dangerous state (e.g., unable to boot or with login credentials known by a third party).
Some more examples of what an attacker could do includes uploading a file which overwrites the actual
server.js application, replace an important system binary with a malicious one, add a configuration file which allows for remote access to a service, etc.
Here’s an example of an application vulnerable to Zip Slip:
const fs = require('fs');
const unzipper = require('unzipper'); // unzipper <= 0.8.2
(Note that this depends on an unpatched version of the
unzipper library. This library was recently updated in
v0.8.3 to fix exactly this vulnerability.) In this example we have a zip file located at
/tmp/user-upload.zip, and are attempting to extract the contents to
/tmp/target. With the vulnerable library installed, an attacker can upload a specially crafted
*.zip file and this will result in creating files outside of the intended
/tmp/target destination. So if the
user-upload.zip file contains an entry for
../../etc/passwd, it will write to a file at
/tmp/target/../../etc/passwd, which resolves to
Assuming these policies were set, and that a malicious zip file containing path traversal filenames were uploaded, the extraction operation would be denied and the following message (along with a full stack trace) would be logged in the server output:
Unhandled rejection FsPolicyViolation: POLICY_VIOLATION Can't create directory at /etc
Intrinsic can then be used to actively prevent such attacks, without any knowledge of which packages contain vulnerabilities.
Intrinsic has many more policies than just filesystem reads and writes. It also supports policies based on HTTP requests which take into account the method and path, it supports generic networking policies based on hosts and ports, it supports child process spawning, etc.
This article was written by me, Thomas Hunter II. I work at a company called Intrinsic where we specialize in writing software for securing Node.js applications. We currently have a product called Intrinsic for Lambda which follows the Least Privilege model for securing applications. Our product proactively protects Node.js applications from attackers, and is surprisingly easy to implement. If you are looking for a way to secure your Node.js applications, give us a shout at firstname.lastname@example.org.