On many web applications, the option of uploading files is a standard feature.
Whether it’s adding a profile photo or sending a document, file upload simplifies user interaction. But this functionality is not without risks.
Poorly configured or insufficiently protected, this functionality can open the door to serious attacks, ranging from the execution of malicious code on the server to the complete compromise of the infrastructure. A simple, seemingly harmless file can become a Trojan horse, enabling an attacker to take control of the system or disrupt its operation.
In this article, we will review file upload vulnerabilities. We will detail the various techniques used by attackers to bypass security mechanisms. Finally, we will outline the best practices to implement in order to effectively secure an upload feature.
Comprehensive Guide to File Upload Vulnerabilities
File Upload Vulnerabilities: Exploitation Mechanisms and Risks
File uploads can represent a critical attack surface when poorly secured. Various methods can be used to exploit vulnerabilities, with the main aim of uploading a file whose type or content is not intended.
Some attacks can even use apparently legitimate files, authorised by the system, but used in a roundabout way to compromise security.
File upload exploitation modes
There are generally two main types of exploitation:
- On the client side: an attacker can inject malicious JavaScript code into a file, for example triggering a stored XSS vulnerability when it is displayed by a user.
- Server side: the upload can be used to execute arbitrary code, bypass restrictions or execute system commands, depending on the server configuration.
How server-side file upload works
When a web server receives a request for a file with the .php extension, for example, it does not simply serve the file as is. Instead, it transfers it to a PHP execution engine, such as mod_php
for Apache or PHP-FPM
for Nginx.
This engine will then interpret the contents of the file, execute the instructions it contains (including any system commands), and generate output, usually in HTML.
The web server then sends the result back to the client in the form of an HTTP response. The PHP code itself is never transmitted as is to the user’s browser. This mechanism makes it possible both to display dynamic content and to protect the source code on the server side.
This has major security implications:
- If a malicious file with a .php extension is uploaded to the server, it will be executed.
- If a .php file is placed in a directory not interpreted by PHP (or renamed with another extension), it can be downloaded as a simple text file, exposing the source code.
Incorrect configuration can therefore expose the code or allow commands to be executed remotely, particularly if a webshell is uploaded to a poorly protected folder.
In the following sections, we will detail these different vulnerabilities and illustrate their impact using concrete examples. Finally, we will look at the security best practices that can be put in place to prevent these attacks effectively.
But first, let’s take a look at the most common security mechanisms, and the techniques used by attackers to bypass them.
Understanding Techniques for Bypassing Protection and Exploiting File Upload Vulnerabilities
Bypassing file extension checks
Checking the file extension on the server side is an essential first line of defence. However, this alone does not guarantee the security of the upload mechanism.
Attackers have various techniques for bypassing these checks when they are poorly implemented.
Multiple extensions
A classic bypass method is to use double or disguised extensions, such as image.jpg.php
.
If the application simply checks that the file name contains .jpg
without checking the position or full validity, the file can be interpreted as a PHP script.
The limits of blacklists
Many applications rely on a blacklist of unauthorised extensions, blocking .php
for example. However, this approach is easily circumvented with variants such as .php5
, .phtml
, or other extensions that can be interpreted depending on the server configuration.
In fact, it is strongly recommended to use a whitelist of authorised extensions (such as .jpg
, .png
, .pdf
) so that only the file types expected by the application are allowed to pass through.
Fuzzing and black box techniques
In a black box context, an attacker will often fuzz extensions to identify which formats are accepted or poorly filtered by the application.
This test phase reveals unexpected behaviour and potentially dangerous extensions.
Injection of special characters
The use of special characters in file names is another technique for fooling validation:
%20
%0a
%00
%0d%0a
/
or.\
These characters can confuse filters and cause the file name or path to be misinterpreted on the server side.
Path Traversal risks
A major danger linked to badly filtered file names is the injection of relative paths (../
), or Path Traversal. This allows an attacker to move up the file system tree and:
- Overload or modify sensitive files (e.g.
config.php
,.htaccess
) - Place malicious files in strategic locations accessible via HTTP
Bypassing the MIME type
The MIME (Multipurpose Internet Mail Extensions) type, defined by RFC 6838, is used to indicate the format of a file.
It is made up of two parts: the main type and the sub-type, for example image/gif
. When a file is uploaded, this information is transmitted via the Content-Type
header.
On the server side, this validation can be carried out in two ways:
- Content-Type verification: based on the value sent in the HTTP request.
- Analysis of file signatures (also known as magic bytes): these are sequences of bytes located at the beginning of the file, typical of each format (e.g.
GIF89a
for GIFs).
Unfortunately, relying solely on these two checks is not enough to guarantee the legitimacy of the file. An attacker can create a hybrid file, combining a valid signing (for example, that of an image file), a credible Content-Type (image/gif
) and hidden malicious code, such as a PHP webshell.
File upload using the PUT method
The PUT method defined by RFC 7231 states:
‘The PUT method requests that the state of the target resource be created or replaced by the state defined by the representation included in the payload of the request message.’
In other words, when a server authorises the use of the HTTP PUT method, it accepts that a file will be created or replaced in the location specified by the URL, with the content defined in the body of the request. This opens the door to serious security breaches if this functionality is not properly restricted.
Fortunately, this method is not enabled by default on most web servers. It requires explicit configuration to become operational. However, during penetration tests, it is not uncommon to discover poorly configured servers where the PUT method is active without access control.
In this case, such a configuration would allow an attacker to:
- Upload a webshell, i.e. a file containing malicious code (usually PHP) that can be executed on the server side.
- Modify or replace critical files, potentially impacting the configuration or overall operation of the application.
- Trigger a denial of service (DoS) by overwriting files that are essential for the application to function correctly.
Potential consequences of a file upload vulnerability
Improper management of file uploads can have critical consequences for the security of a web application. When an attacker manages to exploit a vulnerability in this mechanism, several types of attack become possible:
- XSS (Cross-Site Scripting) vulnerabilities: SVG files, often considered safe because they are images, can contain malicious JavaScript. If these files are accessed via a browser, they can trigger a stored XSS attack, executing arbitrary code in the context of the target user’s session.
- Arbitrary file upload: Without rigorous control of the file name, an attacker can inject special characters such as
../
(path traversal) in order to leave the intended directory and override or overwrite sensitive files on the server (e.g..htaccess
, configuration files, etc.), depending on system permissions. - Denial of service (DoS) attack: If no size limit is imposed on uploads, it is possible to flood the server with large files, leading to saturation of resources and even outright crashing of the application.
- Remote Command Execution (RCE): The most dangerous scenario: uploading a webshell. This is a file containing server-side interpreted code (PHP, ASPX, etc.), enabling the attacker to take control of the remote system, execute commands and potentially pivot to other targets. To find out more about this type of vulnerability, please refer to our write-up: RCE vulnerability in a file name.
Exploiting File Upload Vulnerabilities
XSS attack using SVG file upload
XSS (Cross-Site Scripting) attacks linked to file uploads are a vulnerability frequently encountered during web penetration tests. One of the most common and effective vectors is the use of SVG files.
SVG files are an XML-based image format. It is therefore possible to include JavaScript tags in their structure.
These tags can be executed by the victim’s browser when they are opened, which can allow malicious code to be executed:
Let’s consider a web application that lets you upload files with the .svg
extension:
The SVG file is uploaded to the server. However, when a user accesses it via his browser, the malicious content embedded in the file is interpreted and executed. This triggers the execution of JavaScript code, enabling the targeted user’s session cookie, for example, to be retrieved:
In addition, an attacker can upload a booby-trapped SVG image and then share the link to this file with legitimate users. As soon as a user clicks on this link, the JavaScript code contained in the file is executed in the browser. This can allow the attacker to steal sensitive information, such as a session token, or cause any other effect typical of a stored XSS.
In some cases, the malicious file may even be automatically displayed on a public page (for example, the home page of a site or a community space). Users will then be exposed to the attack without needing to click on anything, simply by visiting the page.
Remote code execution via webshell upload
Let’s take the example of a webshell upload to a vulnerable application. This attack is based on uploading a file containing malicious code, with an extension interpreted by the PHP engine. If this file is executed on the server side, the attacker can then execute remote commands, opening the way to a partial or total takeover of the system.
Here is the vulnerable code:
In this example, uploading a file is authorised on condition that its signing corresponds to that of a JPEG or PNG file. For example, for a JPEG file, the first bytes must correspond to the FFD8
hexadecimal signing.
It is also easy to find the signatures (‘magic bytes’) specific to each type of file:
Thanks to this bypass, the file is correctly uploaded to the server. By accessing its location, it becomes possible to execute commands remotely:
How to Prevent File Upload Vulnerabilities?
It is strongly recommended to rely on proven frameworks to manage file upload and validation mechanisms. This significantly reduces the risk of errors and vulnerabilities.
Validate file content
However, if a custom implementation is required, here are some basic security rules to follow:
- Extension validation: restrict the file types accepted (for example, to image formats only) by setting up a whitelist of authorised extensions.
- Check MIME type and signing (magic bytes): Don’t just rely on the extension; also validate the Content-Type and the characteristic bytes (binary signing) of the file to confirm that it is the expected type.
- Strict control of file names: Avoid names containing special characters or dangerous sequences such as
../
, which could lead to path traversal vulnerabilities. Use a list of authorised characters (e.g. alphanumeric) for file names.
Additional security measures
In addition to technical validations, here are some defence-in-depth measures you can put in place to strengthen security:
- Hide the storage directory: Avoid making the path where uploaded files are stored public. Instead, use a dedicated server script to serve the files to the client, which allows precise control of access and avoids direct execution of a malicious file.
- Rename files with random names: When saving, assign a unique and unpredictable name (UUID, hash, etc.) to each file to prevent brute force attacks on URLs or the reuse of malicious files.
- Limit critical server language functions: Disable sensitive functions in your backend language configuration. In PHP, for example, you can disable high-risk functions via the php.ini file:
- Limit the size of uploaded files: Define a strict maximum size for files to be uploaded to prevent Denial of Service (DoS) attacks, which can consist of saturating server resources with files that are too large.
- Deploy a WAF (Web Application Firewall): A WAF enables malicious requests to be detected and blocked before they reach the application. It provides a useful additional layer of defence against common attacks. However, a WAF should never be considered as a sole protection. Experienced attackers can bypass it. It is therefore essential to integrate it into a more global security strategy.
Conclusion
File uploads represent a potentially critical vulnerability for web applications when poorly implemented.
Attacks such as XSS, remote command execution and arbitrary file uploads can have a major impact on a company’s security and reputation.
To significantly reduce these risks, it is essential to follow best security practices and rely on proven frameworks. Rigorous implementation of the upload mechanism is an effective first line of defence in protecting applications.
Author: Théo ARCHIMBAUD – Pentester @Vaadata