☑️Bypassing Filters

Client-Side Validation

Many web applications only rely on front-end JavaScript code to validate the selected file format before it is uploaded and would not upload it if the file is not in the required format (e.g., not an image).

However, as the file format validation is happening on the client-side, we can easily bypass it by directly interacting with the server, skipping the front-end validations altogether. We may also modify the front-end code through our browser's dev tools to disable any validation in place.

The key note here is, find any line of code that is responsible for file validation and remove it from the HTML code. It could be just an HTML attribute in the code, it could be the HTML page calling a JS/PHP function that does the validation (in that case remove the function call), and so on.

Blacklist Filters

Lets talk about blacklisted extensions. In addition to front-end validation there can also be back-end validation of extensions. There are generally two common forms of validating a file extension on the back-end:

  1. Testing against a blacklist of types

  2. Testing against a whitelist of types

For example, the following piece of code checks if the uploaded file extension is PHP and drops the request if it is:

$fileName = basename($_FILES["uploadFile"]["name"]);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$blacklist = array('php', 'php7', 'phps');

if (in_array($extension, $blacklist)) {
    echo "File type not allowed";
    die();
}

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. It is not comprehensive, as many other extensions are not included in this list, which may still be used to execute PHP code on the back-end server if uploaded.

Tip: The comparison above is also case-sensitive, and is only considering lowercase extensions. In Windows Servers, file names are case insensitive, so we may try uploading a php with a mixed-case (e.g. pHp), which may bypass the blacklist as well, and should still execute as a PHP script.

So we can try to bypass that by fuzzing extensions. PayloadsAllTheThings provides lists of extensions for PHP and .NET web applications. We may also use SecLists list of common Web Extensions.

Note: we can use burp repeater to write the whole file too and the extension (content-type) etc. So when uploading a file we can write it on the go instead of writing it in a file and trying to upload that file.

Whitelist Filters

Sometimes we might find that we can upload a file with Double Extension. For example in this code below:

it only checks if .jpg is in the filename so that means we can still end our filename with .php.

Reverse Double Extension

In some cases, the file upload functionality itself may not be vulnerable, but the web server configuration may lead to a vulnerability. For example, an organization may use an open-source web application, which has a file upload functionality. Even if the file upload functionality uses a strict regex pattern that only matches the final extension in the file name, the organization may use the insecure configurations for the web server.

For example, the /etc/apache2/mods-enabled/php7.4.conf for the Apache2 web server may include the following configuration:

The above configuration is how the web server determines which files to allow PHP code execution. It specifies a whitelist with a regex pattern that matches .phar, .php, and .phtml. However, this regex pattern can have the same mistake we saw earlier if we forget to end it with ($). In such cases, any file that contains the above extensions will be allowed PHP code execution, even if it does not end with the PHP extension. For example, the file name (shell.php.jpg) should pass the earlier whitelist test as it ends with (.jpg), and it would be able to execute PHP code due to the above misconfiguration, as it contains (.php) in its name.

Character Injection

Finally, let's discuss another method of bypassing a whitelist validation test 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.

The following are some of the characters we may try injecting:

  • %20

  • %0a

  • %00

  • %0d0a

  • /

  • .\

  • .

  • :

Each character has a specific use case that may trick the web application to misinterpret the file extension. 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. The same may be used with web applications hosted on a Windows server by injecting a colon (:) before the allowed file extension (e.g. shell.aspx:.jpg), which should also write the file as (shell.aspx). Similarly, each of the other characters has a use case that may allow us to upload a PHP script while bypassing the type validation test.

We can write a small bash script that generates all permutations of the file name, where the above characters would be injected before and after both the PHP and JPG extensions, as follows:

With this custom wordlist, we can run a fuzzing scan with Burp Intruder.

Type Filters

Many modern web servers and web applications also test the content of the uploaded file to ensure it matches the specified type.

There are two common methods for validating the file content: Content-Type Header or File Content.

Content-Type

We may start by fuzzing the Content-Type header with SecLists' Content-Type Wordlist through Burp Intruder, to see which types are allowed. We can filter it further to select image type (if that's the case) adn get it down to 45 (instead of 700).

We can simply change the content type to the one that is allowed and upload our web shell.

Note: A file upload HTTP request has two Content-Type headers, one for the attached file (at the bottom), and one for the full request (at the top). We usually need to modify the file's Content-Type header, but in some cases the request will only contain the main Content-Type header (e.g. if the uploaded content was sent as POST data), in which case we will need to modify the main Content-Type header.

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 usually done by inspecting the first few bytes of the file's content, which contain the File Signature or Magic Bytes. For example, if a file starts with (GIF87a or GIF89a), this indicates that it is a GIF image, while a file starting with plaintext is usually considered a Text file. If we change the first bytes of any file to the GIF magic bytes, its MIME type would be changed to a GIF image, regardless of its remaining content or extension.

Tip: Many other image types have non-printable bytes for their file signatures, while a GIF image starts with ASCII printable bytes (as shown above), so it is the easiest to imitate. Furthermore, as the string GIF8 is common between both GIF signatures, it is usually enough to imitate a GIF image.

The file command on Unix systems finds the file type through the MIME type. If we create a basic file with text in it, it would be considered as a text file, as follows:

However, if we write GIF8 at the beginning:

PHP uses mime_content_type() to determine file content type.

Last updated