FILE UPLOAD ATTACKS

Table of contents:

  • Absent validation (most basic, without any defense)

  • Bypassing Client-Side validation

  • Bypassing Server-Side validation

  • Preventing File Upload Vulnerability

ABSENT VALIDATION

The most basic type of file upload vulnerability occurs when the web application does not have any form of validation filters on the uploaded files, allowing the upload of any file type by default.

IDENTIFYING WEB FRAMEWORK

A Web Shell provides us with an easy method to interact with the back-end server by accepting shell commands and printing their output back to us within the web browser. A web shell has to be written in the same programming language that runs the web server, as it runs platform-specific functions and commands to execute system commands on the back-end server, making web shells non-cross-platform scripts. So, the first step would be to identify what language runs the web application.

We can do this using a few techniques-

  • visit the /index.ext page, where we would swap out ext with various common web extensions, like php, asp, aspx, among others, to see whether any of them exist.

  • Use extensions like Wappalyzerarrow-up-right

VULNERABILITY IDENTIFICATION

  • When selecting a file, it shows "all files" and does not limit to the type of files we can upload.

  • doesn't have any client-side protection, i.e let's any type of file be uploaded, and the rejection comes from the server (if it does).

  • Has only client-side protection, i.e if we bypass client-side protection, the server let's the the file be uploaded without verification

EXPLOITING BASIC FILE UPLOAD VULNERABILITY

If we do come across a file upload vulnerability, to exploit it, we can upload a malicious script in the same language as the web application, like a web shell or a reverse shell script.

WEB SHELLS

We can find many excellent web shells online that provide useful features, like directory traversal or file transfer. One good option for PHP this is phpbasharrow-up-right. while using phpbash looks something like this

WRITING CUSTOM WEB SHELL

Using a web shell from an online resource can be very easy and fast, but we should know how we can write a simple or advanced web shell.

  • with PHP web applications, we can use the system() A function that executes system commands and prints their output, and passes it to the cmd parameter with $_REQUEST['cmd']

like this

Or we can make a bit advanced web shell with better functionality

REVERSE SHELL

One reliable reverse shell for PHP It is the pentestmonkeyarrow-up-right PHP reverse shell. As usual, we'll have to start a listener on our machine and visit the file we have uploaded

. We can also generate a custom php reverse shell using msfvenom. Note that this is highly flagged by Defender or any AV.


BYPASSING CLIENT-SIDE VALIDATION

We can know that a web app has client-side validation if it rejects our uploaded file without sending it to the server.

So, what we can do to bypass it is:

  • Modify the upload request to the back-end server

  • manipulate the front-end code to disable these type validations.

BACK-END REQUEST MODIFICATION

First, we'll see back-end request modification. So, this is what a normal request looks like after it has passed the client-side validation and is sent to the server.r

So what we're gonna do is once the request has passed the client-side validation (which was the only security in this case), we're gonna intercept it and edit the file type and file content. To make it look like this (i.e., change the PNG image file type and content to a PHP webshell)

DISABLING FRONT-END VALIDATION

Another method to bypass client-side validations is through manipulating the front-end code. These functions are being completely processed within our web browser.

This is what a normal code validating the file type on the client side looks like:

Here we can see that the code below is checking for the file extensions

And even more interesting part is onchange="checkFile(this)", which appears to run a JavaScript code whenever we select a file, which appears to be doing the file type validation. When we go in details we see

The key thing we take from this function is where it checks whether the file extension is an image, and if it is not, it prints the error message we saw earlier (Only images are allowed!) and disables the Upload button. We can add PHP as one of the allowed extensions or modify the function to remove the extension check entirely.

Luckily here removing this entire validation wouldn't break anything so we'll do that, and the resultant client-side code will be this


BYPASSING SERVER-SIDE VALIDATION

BLACKLIST FILTERS

Blacklist filters simply means that the server checks the uploaded file against a set of specific extensions to see if the file's extension has been blacklisted, the code on server-side might look something like this-

The code is taking the file extension ($extension) from the uploaded file name ($fileName) and then comparing it against a list of blacklisted extensions ($blacklist). However, this validation method has a major flaw. Except that it is not comprehensive.

FUZZING EXTENSIONS

To identify which extensions are blacklisted we'll fuzz using either burpsuite or we can create a custom python script

we can set the our target web page in the intruder like this

There are many lists of extensions we can utilize in our fuzzing scan. PayloadsAllTheThings provides lists of extensions for PHParrow-up-right and .NETarrow-up-right web applications. We may also use SecLists list of common Web Extensionsarrow-up-right.

We can filter the extensions which are blacklisted or allowed by the lenghts in the fuzzing result.

WHITELIST FILTERS

A whitelist is generally more secure than a blacklist. The web server would only allow the specified extensions, and the list would not need to be comprehensive in covering uncommon extensions.

the code on the server-side for whitelist validation ight look something like this

We see that the script uses a Regular Expression (regex) to test whether the filename contains any whitelisted image extensions. The issue here lies within the regex, as it only checks whether the file name contains the extension and not if it actually ends with it. Many developers make such mistakes due to a weak understanding of regex patterns.

DOUBLE EXTENSIONS

The code only tests whether the file name contains an image extension; a straightforward method of passing the regex test is through Double Extensions. For example, if the .jpg extension was allowed, we can add it in our uploaded file name and still end our filename with .php (e.g. shell.jpg.php), in which case we should be able to pass the whitelist test, while still uploading a PHP script that can execute PHP code.

We will fuzz the extension similarly but instead of simple extensions we can use Wordlistarrow-up-right.

CHARACTER INJECTION

Another method of bypassing a whitelist validation test is through Character Injection. We can inject several characters before or after the final extension to cause the web application to misinterpret the filename and execute the uploaded file as a PHP script.

like:

  • %20

  • %0a

  • %00

  • %0d0a

  • /

  • .\

  • .

  • :

For example, (shell.php%00.jpg) works with PHP servers with version 5.X or earlier, as it causes the PHP web server to end the file name after the (%00), and store it as (shell.php), while still passing the whitelist.

TYPE FILTERS

Type filters are used to validate the file content instead of the file name itself.

There are two common methods for validating the file content: Content-Type Header or File Content. Let's see how we can identify each filter and how to bypass both of them.

CONTENT-TYPE

There might be a content-type filter in the server-side like this

The code sets the ($type) variable from the uploaded file's Content-Type header. Our browsers automatically set the Content-Type header when selecting a file through the file selector dialog, usually derived from the file extension. However, since our browsers set this, this operation is a client-side operation, and we can manipulate it to change the perceived file type and potentially bypass the type filter.

We may start by fuzzing the Content-Type header with SecLists' Content-Type Wordlistarrow-up-right through Burp Intruder

MIME-TYPE

The second and more common type of file content validation is testing the uploaded file's MIME-Type. Multipurpose Internet Mail Extensions (MIME) is an internet standard that determines the type of a file through its general format and bytes structure.

This is done by inspecting the first few bytes of the file's content, which contain the File Signaturearrow-up-right or Magic Bytesarrow-up-right. For eg- a file starting with GIF87a or GIF89a indicates that the file is a GIF image, even if the file has .txt extension it would still be treated as GIF.

while a file starting with plaintext will be treated as a txt file even if it's extension is .jpg, like below

Web servers can also utilize this standard to determine file types, which is usually more accurate than testing the file extension. Web servers can use code like this to check for MIME type of an uploaded file

so to this we have to first fuzz the file to verify allowed files, then we will put the MIME of that signature file (extension would still be of php), and after the first few bytes we will put in our web shell, like this


PREVENTING FILE UPLOAD VULNERABILITIES

EXTENSION VALIDATION

While whitelisting extensions is always more secure, as we have seen previously, it is recommended to use both by whitelisting the allowed extensions and blacklisting dangerous extensions. This way, the blacklist list will prevent uploading malicious scripts if the whitelist is ever bypassed (e.g. shell.php.jpg).

The following example shows how this can be done with a PHP web application, but the same concept can be applied to other frameworks:

CONTENT VALIDATION

We can use framework like below to validate the file extension through whitelisting, and validate both the File Signature and the HTTP Content-Type header, while ensuring both of them match our expected file type:

UPLOAD DISCLOSURE

Another thing we should avoid doing is disclosing the uploads directory or providing direct access to the uploaded file.

users should not have direct access to the uploads directory. Any direct requests to this directory should return a 403 Forbidden response. Instead, files should be served through the controlled script using security-focused HTTP headers such as:

  • Content-Disposition: Used to specify how the content should be displayed in the browser. Setting it to attachment instructs the browser to download the file rather than render it inline.

  • Content-Type: Specifies the MIME type of the file, ensuring that the browser knows how to handle the file content appropriately.

  • X-Content-Type-Options: nosniff: Prevents the browser from MIME-type sniffing, which helps mitigate security risks by ensuring that the browser adheres strictly to the specified Content-Type.

Last updated