Unrestricted Upload of File with Dangerous Type
Description
Unrestricted Upload of File with Dangerous Type occurs when software allows users to upload files without properly validating, restricting, or handling the file types. Attackers can upload files with dangerous content such as web shells, scripts, or executables that can be executed on the server. Common attack vectors include uploading PHP, JSP, or ASP files to web servers, uploading executables to be run by other users, or uploading HTML/SVG files containing malicious scripts. This vulnerability provides attackers with direct code execution capabilities on the target system.
Risk
Unrestricted file upload is one of the most severe web application vulnerabilities because it can directly lead to remote code execution. The 2019 Magento breach compromised thousands of e-commerce stores through web shell uploads. The Equifax breach involved file upload exploitation during attack phases. WordPress plugins regularly suffer from this vulnerability, affecting millions of websites. Once attackers upload a web shell, they gain persistent access to execute commands, steal data, pivot to internal networks, and install malware. The impact is often complete system compromise.
Solution
Implement a whitelist of allowed file extensions and reject all others. Validate file content using magic bytes/file signatures, not just extensions. Rename uploaded files to remove original extensions and prevent execution. Store uploads outside the web root or in a location that cannot execute scripts. Use a separate domain for serving uploaded content. Set proper Content-Type and Content-Disposition headers. Implement file size limits. Scan uploaded files for malware. Never rely solely on client-side validation.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Remote Code Execution Uploaded scripts or executables can be executed, giving attackers full server control. |
| Integrity | Scope: System Compromise Web shells provide persistent backdoor access for data manipulation and system modification. |
| Confidentiality | Scope: Data Breach Server access enables attackers to steal databases, credentials, and sensitive files. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: No file type validation
<?php
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["file"]["name"]);
// Attacker uploads "shell.php" - gets executed!
move_uploaded_file($_FILES["file"]["tmp_name"], $target_file);
echo "File uploaded successfully";
?>
// VULNERABLE: Extension-only check (easily bypassed)
<?php
$filename = $_FILES["file"]["name"];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
// Attacker uses "shell.php.jpg" or "shell.pHp"
if ($ext == "jpg" || $ext == "png") {
move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/" . $filename);
}
?>
# VULNERABLE: Trusting user-provided filename
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
# Attacker controls filename - could be "../../../var/www/html/shell.php"
file.save(os.path.join(UPLOAD_DIR, file.filename))
return 'Uploaded'
Fixed Code
<?php
// SAFE: Comprehensive file upload validation
function secure_upload($file) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
$max_size = 5 * 1024 * 1024; // 5 MB
// Check file size
if ($file['size'] > $max_size) {
throw new Exception('File too large');
}
// Get and validate extension
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_extensions)) {
throw new Exception('Invalid file extension');
}
// Validate MIME type from content (not user-provided)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowed_types)) {
throw new Exception('Invalid file type');
}
// Verify magic bytes
$magic_bytes = file_get_contents($file['tmp_name'], false, null, 0, 4);
if (!is_valid_image_magic($magic_bytes)) {
throw new Exception('Invalid file content');
}
// Generate random filename (remove original)
$new_filename = bin2hex(random_bytes(16)) . '.' . $ext;
// Store outside web root
$upload_path = '/var/uploads/' . $new_filename; // Not in web root!
move_uploaded_file($file['tmp_name'], $upload_path);
return $new_filename;
}
function is_valid_image_magic($bytes) {
$signatures = [
"\xFF\xD8\xFF" => 'jpeg', // JPEG
"\x89PNG" => 'png', // PNG
"GIF87a" => 'gif', // GIF87a
"GIF89a" => 'gif', // GIF89a
];
foreach ($signatures as $sig => $type) {
if (strpos($bytes, $sig) === 0) {
return true;
}
}
return false;
}
?>
import os
import uuid
from werkzeug.utils import secure_filename
import magic # python-magic library
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
ALLOWED_MIMES = {'image/png', 'image/jpeg', 'image/gif'}
UPLOAD_FOLDER = '/var/uploads' # Outside web root
MAX_SIZE = 5 * 1024 * 1024 # 5 MB
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['POST'])
def upload_safe():
file = request.files['file']
# Check filename has allowed extension
if not allowed_file(file.filename):
return 'Invalid file type', 400
# Check file size
file.seek(0, 2) # Seek to end
size = file.tell()
file.seek(0) # Reset to beginning
if size > MAX_SIZE:
return 'File too large', 400
# Validate MIME type from content
mime = magic.from_buffer(file.read(2048), mime=True)
file.seek(0)
if mime not in ALLOWED_MIMES:
return 'Invalid file type', 400
# Generate safe, random filename
ext = file.filename.rsplit('.', 1)[1].lower()
new_filename = f"{uuid.uuid4().hex}.{ext}"
# Save to secure location
filepath = os.path.join(UPLOAD_FOLDER, new_filename)
file.save(filepath)
return {'filename': new_filename}, 200
Exploited in the Wild
Magento E-Commerce Breach (Magento, 2019)
Critical file upload vulnerability in Magento allowed attackers to upload web shells, compromising thousands of online stores. Customer payment data and personal information was stolen.
WordPress Plugin Vulnerabilities (WordPress, Multiple)
Multiple WordPress plugins including WP Statistics have suffered from CWE-434 vulnerabilities, allowing attackers to upload backdoors to millions of websites.
WSO2 Enterprise Integrator (WSO2, 2025)
CVE-2025-1862 in WSO2 Enterprise Integrator 6.6.0 allows administrators to upload arbitrary files through the BPEL uploader due to improper filename validation.
Tools to test/exploit
-
Burp Suite — manipulate file upload requests.
-
Weevely — web shell generation and management.
-
Upload Scanner — automated file upload vulnerability finder.
CVE Examples
-
CVE-2025-1862 — WSO2 Enterprise Integrator arbitrary file upload.
-
CVE-2024-27198 — JetBrains TeamCity authentication bypass enabling file upload.
-
CVE-2023-50164 — Apache Struts path traversal file upload RCE.
References
-
MITRE. "CWE-434: Unrestricted Upload of File with Dangerous Type." https://cwe.mitre.org/data/definitions/434.html
-
OWASP. "Unrestricted File Upload." https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload