File Upload Vulnerabilities

Introduction

When handled badly, file uploads can also open up severe vulnerabilities in the server.

This can lead to anything from relatively minor, nuisance problems; all the way up to full Remote Code Execution (RCE) if an attacker manages to upload and execute a shell.

By uploading arbitrary files, an attacker could potentially also use the server to host and/or serve illegal content, or to leak sensitive information. Realistically speaking, an attacker with the ability to upload a file of their choice to your server -- with no restrictions -- is very dangerous indeed.


Overwriting Existing Files

When files are uploaded to the server, a range of checks should be carried out to ensure that the file will not overwrite anything which already exists on the server.

Common practice is to assign the file with a new name -- often either random, or with the date and time of upload added to the start or end of the original filename.

File permissions also come into play when protecting existing files from being overwritten.


Remote Code Execution

Remote Code Execution (as the name suggests) would allow us to execute code arbitrarily on the web server. Whilst this is likely to be as a low-privileged web user account (such as www-data on Linux servers), it's still an extremely serious vulnerability.

Remote code execution through a web application tends to be a result of uploading a program written in the same language as the back-end of the website (or another language which the server understands and will execute).

There are two basic ways to achieve RCE on a webserver

1. Webshells

  • a Webshell may be the only option available (for example, if a file length limit has been imposed on uploads).

  • For Example, A simple webshell works by taking a parameter and executing it as a system command. In PHP, the syntax for this would be

    • <?php
        	echo system($_GET["cmd"]);
        ?>
    • This code takes a GET parameter (in the url like ip/script.php?cmd=id;whoami;ls) and executes it as a system command. It then echoes the output out to the screen.

2. Reverse Shells

  • Realistically a fully featured reverse shell is the ideal goal for an attacker;

  • For example, we can upload a reverse shell script written in backend language and then we can open a listener on our machine and then executing the script on the server to gain reverse shell

As a general methodology, we would be looking to upload a shell of one kind or another, then activating it, either by navigating directly to the file if the server allows it, or by otherwise forcing the webapp to run the script for us.


Defense Mechanisms

Client Side Filtering

When we talk about a script being "Client-Side", in the context of web applications, we mean that it's running in the user's browser as opposed to on the web server itself. In the context of file-uploads, this means that the filtering occurs before the file is even uploaded to the server. client-side filtering by itself is a highly insecure method of verifying that an uploaded file is not malicious.

Server Side Filtering

A server-side script will be run on the server. Server-side filtering tends to be more difficult to bypass, as you don't have the code in front of you. As the code is executed on the server, in most cases it will also be impossible to bypass the filter completely instead we have to form a payload which conforms to the filters in place, but still allows us to execute our code.

Kinds of Filtering

  • Extension Validation

    • File extensions are used (in theory) to identify the contents of a file.

      • MS Windows still uses them to identify file types

      • Unix based systems use magic numbers for identifying files

    • Filters that check for extensions work in one of two ways.

      1. blacklist extensions : have a list of extensions which are not allowed

      2. whitelist extensions : have a list of extensions which are allowed and reject everything else

  • File Type Filtering

    • Similar to Extension validation, but more intensive, file type filtering looks, once again, to verify that the contents of a file are acceptable to upload. We'll be looking at two types of file type validation:

      1. MIME validation

        • MIME (Multipurpose Internet Mail Extension) types are used as an identifier for files

          • originally when transfered as attachments over email, but now also when files are being transferred over HTTP(S). The MIME type for a file upload is attached in the header of the request

        • MIME types follow the format <type>/<subtype>.

        • As MIME is based on the extension of the file, this is extremely easy to bypass.

      2. Magic Number validation

        • Magic numbers are the more accurate way of determining the contents of a file

        • They are by no means impossible to fake.

        • The "magic number" of a file is a string of bytes at the very beginning of the file content which identify the content. For example, a PNG file would have these bytes at the very top of the file: 89 50 4E 47 0D 0A 1A 0A.

        • When dealing with file uploads, it is possible to check the magic number of the uploaded file to ensure that it is safe to accept.

  • File Length Filtering

    File length filters are used to prevent huge files from being uploaded to the server via an upload form.

  • File Name Filtering

    Files uploaded to a server should be unique. Additionally, file names should be sanitised on upload to ensure that they don't contain any "bad characters", e.g. null bytes or forward slashes on Linux, as well as control characters such as ; and potentially unicode characters So be aware that you may have to go hunting for your shell in the event that you manage to bypass the content filtering.

  • File Content Filtering

    More complicated filtering systems may scan the full contents of an uploaded file to ensure that it's not spoofing its extension, MIME type and Magic Number.


Attack Mechanisms

Client Side Filtering

There are four easy ways to bypass your average client-side file upload filter:

  1. Turn off Javascript in your browser

    • this will work provided the site doesn't require Javascript in order to provide basic functionality.

  2. Intercept and modify the incoming page

    • Using Burpsuite, we can intercept the incoming web page and strip out the Javascript filter before it has a chance to run.

    • Be sure to clear that site cache to clear any stored js file else it would not be intercepted

  3. Intercept and modify the file upload

    • Where the previous method works before the webpage is loaded, this method allows the web page to load as normal, but intercepts the file upload after it's already passed (and been accepted by the filter).

  4. Send the file directly to the upload point

    • Why use the webpage with the filter, when you can send the file directly using a tool like curl, the syntax for such a command would look something like this:

      • curl -X POST -F "submit:<value>" -F "<file-parameter>:@<path-to-file>" <site>

      • To use this method you would first aim to intercept a successful upload (using Burpsuite or the browser console) to see the parameters being used in the upload, which can then be slotted into the above command.

Server Side Filtering : File Extensions

  • In server-side filter, we have to perform a lot of testing to build up an idea of what is or is not allowed through the filter, then gradually put together a payload which conforms to the restrictions.

  • We'll take a look at a website that's using a blacklist for file extensions as a server side filter.

  • For example, our web app blocks php uploads with extensions (.php and .phtml), however there are total 5 php extensions which are there, we can use those extensions to run our php script

  • Other example may include trying out nested extensions like shell.jpg.php to see that whether the app reads - the first extension after period.

  • There are a million different ways to implement the same feature when it comes to programming -- your exploitation must be tailored to the filter at hand.

    • The key to bypassing any kind of server side filter is to enumerate and see what is allowed, as well as what is blocked; then try to craft a payload which can pass the criteria the filter is looking for.

Server Side Filtering : Magic Numbers

  • The magic number of a file is a string of hex digits, and is always the very first thing in a file. Knowing this, it's possible to use magic numbers to validate file uploads, simply by reading those first few bytes and comparing them against either a whitelist or a blacklist.

  • This technique can be very effective against a PHP based webserver

    • however, it can sometimes fail against other types of webserver.

  • For example, to add a .jpg magic number in a file,

    • we see that jpg magic hex no is FF D8 FF DB

    • we open the file and add AAAA on top of file, random 4 characters

    • then we open the file using hexeditor and change the first four hex numbers 41 41 41 41 with jpg magic numbers


Methodology to Approach Finding File Upload Vulneralbilities

We'll look at this as a step-by-step process. Let's say that we've been given a website to perform a security audit on.

  1. The first thing we would do is take a look at the website as a whole. Using browser extensions such as the aforementioned Wappalyzer (or by hand) we would look for indicators of what languages and frameworks the web application might have been built with. Be aware that Wappalyzer is not always 100% accurate. A good start to enumerating this manually would be by making a request to the website and intercepting the response with Burpsuite. Headers such as server or x-powered-by can be used to gain information about the server. We would also be looking for vectors of attack, like, for example, an upload page.

  2. Having found an upload page, we would then aim to inspect it further. Looking at the source code for client-side scripts to determine if there are any client-side filters to bypass would be a good thing to start with, as this is completely in our control.

  3. We would then attempt a completely innocent file upload. From here we would look to see how our file is accessed. In other words, can we access it directly in an uploads folder? Is it embedded in a page somewhere? What's the naming scheme of the website? This is where tools such as Gobuster might come in if the location is not immediately obvious. This step is extremely important as it not only improves our knowledge of the virtual landscape we're attacking, it also gives us a baseline "accepted" file which we can base further testing on.

    • An important Gobuster switch here is the -x switch, which can be used to look for files with specific extensions. For example, if you added -x php,txt,html to your Gobuster command, the tool would append .php, .txt, and .html to each word in the selected wordlist, one at a time. This can be very useful if you've managed to upload a payload and the server is changing the name of uploaded files.

  4. Having ascertained how and where our uploaded files can be accessed, we would then attempt a malicious file upload, bypassing any client-side filters we found in step two. We would expect our upload to be stopped by a server side filter, but the error message that it gives us can be extremely useful in determining our next steps.

Assuming that our malicious file upload has been stopped by the server, here are some ways to ascertain what kind of server-side filter may be in place:

  • If you can successfully upload a file with a totally invalid file extension (e.g. testingimage.invalidfileextension) then the chances are that the server is using an extension blacklist to filter out executable files. If this upload fails then any extension filter will be operating on a whitelist.

  • Try re-uploading your originally accepted innocent file, but this time change the magic number of the file to be something that you would expect to be filtered. If the upload fails then you know that the server is using a magic number based filter.

  • As with the previous point, try to upload your innocent file, but intercept the request with Burpsuite and change the MIME type of the upload to something that you would expect to be filtered. If the upload fails then you know that the server is filtering based on MIME types.

  • Enumerating file length filters is a case of uploading a small file, then uploading progressively bigger files until you hit the filter. At that point you'll know what the acceptable limit is. If you're very lucky then the error message of original upload may outright tell you what the size limit is. Be aware that a small file length limit may prevent you from uploading the reverse shell we've been using so far.

Last updated