SecureBlockLog inStart a pentest
Vulnerability Repository
CriticalInjection

Unrestricted File Upload

Accepting file uploads without validating type, content, or execution context allows attackers to upload and execute server-side code or malicious payloads.

CVSS 9.8CWE CWE-434OWASP A04:2021 — Insecure Design

Description

File upload functionality is a frequent attack surface because it requires the server to process attacker-controlled data and, in many implementations, store it in a location accessible via HTTP. When the application fails to validate the uploaded file's type, content, name, and execution context, attackers can upload server-side script files (PHP, JSP, ASP), files containing polyglot payloads, malicious archives that exploit path traversal in extraction, or content designed to be served to other users.

CWE-434 (Unrestricted Upload of File with Dangerous Type) captures the core issue: the application accepts file types it should not. A04:2021 Insecure Design reflects that secure file upload requires architectural decisions—upload storage location, web server execution configuration, content validation—not just input filtering.

The most severe impact is remote code execution via webshell upload: an attacker uploads a PHP file to a web-accessible directory and then requests it via HTTP to execute arbitrary commands. But unrestricted file upload also enables stored XSS (uploading HTML/SVG files served with permissive content types), path traversal attacks (filenames like ../../etc/passwd), denial of service (uploading extremely large files or zip bombs), and client-side exploitation through malicious Office documents or PDFs.

How It Works

A typical webshell upload attack begins by testing whether the server enforces file type restrictions:

POST /api/upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg

<?php system($_GET['cmd']); ?>
------WebKitFormBoundary--

If the server validates the Content-Type header but not the actual file content, the bypass is trivial—the header is attacker-controlled. Penetration testers also test MIME-type bypass via magic bytes: the server checks the file's magic bytes (FF D8 FF for JPEG) but not the rest of the content:

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg

FF D8 FF                                          <- JPEG magic bytes
<?php system($_GET['cmd']); ?>                    <- Appended PHP payload
------WebKitFormBoundary--

Extension bypass techniques include double extensions (shell.php.jpg), null byte injection (shell.php%00.jpg), and case variation (shell.PHP, shell.pHp). In environments where .htaccess uploads are permitted:

# Upload an .htaccess file that makes .jpg files executable as PHP
AddType application/x-httpd-php .jpg

After a successful upload, the shell is accessible at the upload path:

curl "https://example.com/uploads/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Impact

  • Remote code execution — Uploaded webshells give attackers interactive command execution on the server with the web application's process privileges.
  • Server compromise — From a webshell, attackers pivot to reading configuration files, stealing database credentials, and escalating privileges.
  • Stored XSS — HTML or SVG files served by the application execute JavaScript in the context of users who view them.
  • Path traversal — Malicious filenames like ../../../../var/www/html/index.php overwrite application files when the upload destination is not sanitized.
  • Denial of service — Zip bomb archives (e.g., 42.zip) or excessively large files exhaust disk space or memory during extraction/processing.

Detection

  1. Upload files with disallowed extensions (.php, .jsp, .asp, .aspx, .py) and test whether they are stored in a web-accessible location and executable via HTTP request.
  2. Test MIME type bypass by uploading a PHP file with Content-Type: image/jpeg in the multipart request—if accepted, test whether the file executes.
  3. Try double extension bypasses: shell.jpg.php, shell.php.jpg, shell.php%00.jpg, shell.PHP.
  4. Attempt to upload an .htaccess file that configures the uploaded directory to execute uploaded file types as scripts.
  5. Test filename sanitization with path traversal filenames: ../shell.php, ../../shell.php, ....//shell.php.
  6. Upload SVG files containing embedded <script> tags and verify whether they are served with a content type that triggers script execution in the browser.

Remediation

Validate file content server-side. Use a server-side library to verify file magic bytes against the expected type. Do not trust the Content-Type header or file extension alone.

Rename all uploaded files. Generate a new random filename (UUID) server-side, discarding the original. This prevents extension-based execution and path traversal:

import uuid, os
safe_filename = str(uuid.uuid4()) + '.jpg'  # Always use the validated extension

Store uploads outside the web root. Files stored in a non-web-accessible directory cannot be requested via HTTP. Serve them through an application endpoint that validates access permissions and sets an explicit Content-Disposition: attachment header.

Configure the upload directory as non-executable. In Nginx or Apache, explicitly disable script execution in the upload directory:

location /uploads/ {
    add_header Content-Type application/octet-stream;
    location ~ \.(php|pl|py|jsp|asp|sh|cgi)$ { deny all; }
}

Enforce file size limits. Set a maximum upload size at both the application and web server level to prevent denial of service via large file uploads.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping