Portswigger File Upload Vulnerability
by C. Casquatch
Some notes on file upload vulnerabilities: appsec
A file upload vulnerability is when an end user is able to upload any kind of file without proper checks or validation. Someone could upload a malicious executable like a PHP web shell, and if the server executes it, that opens the door to any number of attacks. The server is supposed to safely store uploaded files — but if it receives something malicious and executes it, the attacker gains code execution on the server.
Lab 1 — No Validation
This lab had no file upload validation at all. The goal was to upload a PHP web shell that would read and return the contents of /home/carlos/secret.
The web shell was a single line of PHP saved as shell.php:
<?php echo file_get_contents('/home/carlos/secret'); ?>
This works because file_get_contents() reads a file from the server’s filesystem and echo prints it to the response — so when the server executes the PHP, it returns carlos’s secret directly.
Initial Problem — Wrong File Extension When creating the file in a text editor the file saved as shell.php.txt instead of shell.php. When the server serves a .txt file it just displays the raw PHP code rather than executing it. The fix was to create the file via terminal to guarantee the correct extension:
echo "<?php echo file_get_contents('/home/carlos/secret'); ?>" > shell.php
You can verify the filename is correct by checking the upload POST request in Burp HTTP History — the request body should show:
filename="shell.php"
and not filename="shell.php.txt".
Executing the Shell After uploading, the file path was found by checking the Network tab in DevTools or the upload response in Burp HTTP History. Navigating directly to:
/files/avatars/shell.php
caused the server to execute the PHP and return carlos’s secret in the response.
Lab 2 — Content-Type Validation Bypass
This lab attempted to validate uploads by checking the Content-Type header sent by the browser. Since this is a user-controllable value it can simply be changed to bypass the check.
Steps
- Turn Burp Intercept on and attempt to upload
shell.phpnormally - Burp catches the POST request which contains:
Content-Disposition: form-data; name="avatar"; filename="shell.php" Content-Type: application/x-php - Change the Content-Type to:
Content-Type: image/jpeg -
Forward the request — the server checks the Content-Type, sees image/jpeg, thinks it’s a legitimate image and accepts it — but the file is still stored as shell.php on the filesystem.
- Navigate to /files/avatars/shell.php — the server executes the PHP and returns carlos’s secret.
Content-Type is just a header the client sends — the server should never trust it as proof of what a file actually is. Similarly, file extensions alone are not reliable validation. Proper file upload validation should inspect the actual file contents (magic bytes) rather than trusting user-supplied metadata. Uploaded files should also never be stored in a location where the server will execute them.
tags: portswigger - burpsuite - file upload - appsec