Another Security Blog

A place to show my mad skills

TidBits Walkthroughs Resources Projects View on GitHub
30 March 2026

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

  1. Turn Burp Intercept on and attempt to upload shell.php normally
  2. Burp catches the POST request which contains:
    Content-Disposition: form-data; name="avatar"; filename="shell.php"
    Content-Type: application/x-php
    
  3. Change the Content-Type to:
    Content-Type: image/jpeg
    
  4. 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.

  5. 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