Skip to content
NovaDen
Go back

File Upload Vulnerabilities

Introduction

File upload vulnerabilities arise when a system allows users to upload files in an unsafe manner. These vulnerabilities can be exploited regardless of whether the attacker can subsequently invoke the uploaded file. For example, a system that fails to validate the size of uploaded files may be vulnerable to a denial-of-service attack if an attacker uploads a file large enough to exhaust the available disk space.

How File Uploads Work

The way a server handles uploaded files depends on its configuration:

HTTP Request Structure

File uploads are transmitted using multipart/form-data requests. The Content-Type header specifies both the type and a boundary string that separates the form fields:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqQFv421UZxV128wE

Each field in the request body is delimited by this boundary. For example:

------WebKitFormBoundaryqQFv421UZxV128wE
Content-Disposition: form-data; name="avatar"; filename="temp_shell.php"
Content-Type: application/x-php

<?php echo file_get_contents('/home/carlos/secret'); ?>

------WebKitFormBoundaryqQFv421UZxV128wE
Content-Disposition: form-data; name="user"

wiener
------WebKitFormBoundaryqQFv421UZxV128wE
Content-Disposition: form-data; name="csrf"

kys6Cdo1AVseVTWNyx9yt2Nn3Kfjcp4N
------WebKitFormBoundaryqQFv421UZxV128wE--

The boundary is randomly generated by the browser and separates the individual form fields. Everything within a boundary section belongs to one field.

Note: The Content-Type response header may reveal what the server believes it has served. If not explicitly set, the response type will reflect the extension-to-MIME mapping.

Bypassing File Upload Defenses

Applications commonly implement restrictions on file uploads, but each restriction must be tested thoroughly.

Content-Type Validation

An application that trusts the Content-Type header supplied by the client can be trivially bypassed, as this header is easily spoofed. Testing should be performed with a valid-seeming MIME type (e.g., image/jpeg) while sending a malicious payload.

Path Traversal in Filenames

A server may be configured to prevent execution of files within the upload directory (e.g., /user-profile-pics/). However, if it does not validate the filename and is vulnerable to path traversal, an attacker can escape the protected directory by uploading a file named:

../../payload.php

This writes the file outside the sandboxed directory, where execution may be permitted.

Extension Blacklist Bypasses

Blacklist-based extension filtering is fragile. Many bypass techniques exist:

Overlooked extensions: A blacklist may block .php but not .php5, .phtml, .pht, or other PHP-related extensions. Similarly, .html may be blocked while .shtml is not.

Server configuration manipulation: An attacker can alter how the server interprets extensions by uploading a configuration file first. For example, uploading an .htaccess file containing:

AddType application/x-httpd-php .l33t

Then uploading a payload with the .l33t extension, which is not blacklisted. The same technique applies to web.config on IIS or httpd.conf/apache2.conf if writable.

Case variations: If the blacklist is case-sensitive and blocks .php, variations such as .PhP or .pHp can be attempted.

Null byte and delimiter injection: If the filename is processed by low-level C/C++ functions, a null byte (%00) or semicolons can be used to terminate the filename early:

exploit.asp;.jpg
exploit.asp%00.jpg

The server sees the allowed extension, but the actual stored file may have a different (or truncated) extension.

Recursive stripping bypass: If the defense strips blacklisted extensions from the filename, nesting can defeat it:

exploit.p.phphp

After stripping .php, the remaining characters collapse into exploit.php.

Apache extension mapping: When Apache encounters an unrecognized extension, it walks left through the filename until it finds one it knows how to handle. A file named exploit.php.jpg may be interpreted as PHP if .jpg is not mapped to a handler; Apache falls back to .php.

Polyglot Shells

A polyglot file is simultaneously valid in two formats (e.g., JPEG and PHP). This can bypass content-type checks that inspect the file’s magic bytes. They can be created using ExifTool:

exiftool -Comment="<?php echo 'START ' . file_get_contents('/home/carlos/secret') . ' END'; ?>" input.jpg -o polyglot.php

The resulting file is both a valid image and a valid PHP script.

Race Conditions

Some systems upload the file temporarily to disk before running validation checks. The file exists for a short window during which an attacker can execute it. This is a race condition. For example:

<?php
$target_dir = "avatars/";
$target_file = $target_dir . $_FILES["avatar"]["name"];

// temporary move
move_uploaded_file($_FILES["avatar"]["tmp_name"], $target_file);

if (checkViruses($target_file) && checkFileType($target_file)) {
    echo "The file " . htmlspecialchars($target_file) . " has been uploaded.";
} else {
    unlink($target_file);
    echo "Sorry, there was an error uploading your file.";
    http_response_code(403);
}
?>

If the server uses randomly generated filenames, an attacker can extend the processing time (e.g., by uploading a larger file) to increase the window for brute-forcing the directory and filename.

PUT Requests

Some servers support PUT requests for file uploads. This can serve as an alternative attack vector when the application’s upload functionality is well-defended.

Alternative Attack Vectors

Even when server-side code execution is not possible, file uploads can enable other attacks:

Remediation


Share this post on:

Previous Post
SSH Cheat Sheet
Next Post
Information Disclosure Vulnerabilities