One of the most promising avenues of attack in a web application is the file upload. With results ranging from XSS to full-blown code execution, file uploads are an attractive target for hackers. There are usually restrictions in place that can make it challenging to execute an attack, but there are various techniques a hacker could use to beat file upload restrictions to get a shell.
The first method we'll explore is how to bypass blacklisting. Blacklisting is a type of protection where certain strings of data, in this case, specific extensions, are explicitly prohibited from being sent to the server. At first glance, it might seem like an optimal solution to prevent bad extensions, often executables, from being uploaded, but it is trivial to bypass.
In addition to the regular extensions, there are alternative extensions that can be used to get around blacklist filters. Here are some extensions for PHP files:
.pht, .phtml, .php3, .php4, .php5, .php6, .inc
.jspx, .jspf, .jsw, .jsv
In some situations, simply changing the case of the extension can trick filters into accepting the file, like so:
.pHp, .Php, .phP
Another type of prevention commonly encountered on the web is whitelisting. Whitelisting is precisely the opposite of blacklisting, where the server accepts only specific extensions. For example, an application that allows you to upload a profile picture might only take JPG, JPEG, or PNG files. While this type of prevention is better than blacklisting, it can still be easily bypassed.
Some web servers, such as Apache, allow files with double extensions. That means we can trick the server into accepting a PHP file that also has a JPG extension tacked on the end:
We can also use a null byte injection to bypass whitelist filters. Anything after the null character will be ignored when the file is saved, so injecting between a forbidden extension and an allowed extension can lead to a bypass:
This can also be accomplished with Burp and modifying the hex request. Name the file shell.phpD.jpg — we'll replace the D character with a null character during the request. When uploading the file, intercept the request, go to the hex tab, and find the hex representation of the D character:
Simply replace the 44 with 00 and send the request through:
This technique can be used in tricky situations where the standard null byte injection won't work.
Another way to beat whitelisting is to fool the server with file type headers. Usually, if an upload function accepts images, it will accept GIF files as well. We can add GIF89a; to the beginning of the shell to trick the upload:
GIF89a; <?php system($_GET['cmd']); ?>
The next method to bypass file upload restrictions utilizes Exif data in an image. We can insert a comment that contains valid PHP code that will be executed by the server when the image is processed.
We can use exiftool to do this — if it is not installed already, install it with the package manager:
~# apt install exiftool
Then we can insert a simple command shell as a comment in our pic:
~# exiftool -Comment="<?php system($_GET['cmd']); ?>" pic.jpg
Now if we use the file command on our pic, we can see the code was successfully inserted:
~# file pic.jpg pic.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "<?php system(['cmd']); ?>", baseline, precision 8, 1920x840, components 3
All we have to do now is add a PHP extension so it can be executed:
~# mv pic.jpg pic.php.jpg
This technique can be combined with any of the approaches to bypass blacklists or whitelists.
The content type of a file can also be used as a way to validate uploaded content. For example, an image upload will usually check that the content type of the file is an image, not a script or other malicious file type. This type of prevention can easily be bypassed by intercepting the request and changing the content type.
In some situations, the length of the content can also be used to validate uploaded files. It will depend on the specific circumstances, but often all that's needed is a shorter payload. For example, a typical PHP command shell can be shortened to this:
Several precautions developers can take to prevent unrestricted file uploads and reduce the likelihood of an attacker bypassing restrictions. First and foremost, the directory where the uploads are stored should have no execute permissions. Consider storing files in a database rather than on the filesystem at all.
File types to be uploaded should be limited to the bare minimum for necessary business functionality. Also, any control characters or special characters should be stripped from the uploaded file name and extension, and only one dot should be permitted. Also, the size should be limited as multiple large files can lead to denial of service.
After a file is uploaded, the name should be changed, ideally, to a hash that cannot be guessed. This will prevent attackers from being able to find their file after it is uploaded. It is also wise to prevent files from being overwritten, so hashes cannot be deduced by an attacker.
In this tutorial, we covered various methods to get around common file upload restrictions. We learned how to bypass blacklist filters, whitelist filters, content validation, and more. File uploads, if they can be beaten, are a surefire path to shell.
Want to start making money as a white hat hacker? Jump-start your hacking career with our 2020 Premium Ethical Hacking Certification Training Bundle from the new Null Byte Shop and get over 60 hours of training from cybersecurity professionals.
Other worthwhile deals to check out: