File upload is one of the most common functionalities in web applications. Typically, it involves uploading images or documents to the server. It is also a place that pentesters look for due to the numerous security errors in implementations. In this article, we will present the most common vulnerabilities and show how they can be used. We will also discuss defense methods.
Two sides
We will look at all the implementations discussed in this article from two sides:
- Server-side: How mistakes in an indicated implementation from the server side may be exploited. How to lead to execution of any code (RCE – remote code execution) on the server side.
- Client-side: In the case of attacks on the client side, it will be either to execute XSS or to steal data (such as CSRF tokens) from another domain.
Allowing All File Types
It is the most basic implementation that still appears in web applications. We assume that the application saves on the server a file with the same extension as the original file had, and the content of the file is not checked.
Server-side perspective
Consider several possible solutions for saving the file on the server side:
The file is accessible directly via the URL (file located in webroot)
The simplest possible variant: the user uploads a file named hack.php, which is then available at http://www.example.com/uploads/hack.php. Direct reference to this URL leads straight to the RCE in the application.
File in webroot, but without the ability to perform
It is possible to protect yourself against the above-mentioned attack, making it impossible to make .php files in a single directory. This can be done by the php_flag engine off directive in the .htaccess file or one of the Apache configuration files. Thanks to this, after referring directly to the file http://www.example.com/uploads/hack.php, its source will be displayed. However, the code will not be executed. Moreover, if an attacker can send files with arbitrary names, they can send a .htaccess file with the following content:
1 |
AddHandler application/x-httpd-php .jpeg |
This will cause files with the extension .jpeg to be interpreted by the PHP parser, thereby circumventing the security. Allowing files of any extensions may also be useful in the event of LFI (Local File Inclusion) error if a code similar to the following appears in another application location:
1 2 3 |
<?php include 'includes/'.$_GET['module'].'.php'; ?> |
Files outside of webroot
A solution where uploaded files are outside the main directory tree and available directly through URLs. For example, webroot applications can be located in /var/www, and uploads in /var/uploads. In this case, the trick with .htaccess will not work anymore. However, the file can still be useful for LFI.
Files with changed names
Sometimes developers decide to save a file with its original extension, but with a changed file name. So, for example, instead of hack.php, the file 4093b2546a7437d1a2ca3f9b7ac662e3.php could be saved on the server. If the name of the new file is later directly visible somewhere, this variant does not differ from those discussed earlier. However, even if the name is not directly known, in some cases attacks can be possible:
- The file name is created in a predictable way; e.g., as md5(time()). Based on the Date header from the HTTP response, you can guess the correct file name.
- Another place in the application allows you to list the contents of directories (for example, in Java applications, this option gives you an XXE attack).
- The new part of the file name is attached to the original one; i.e., instead of hack.php, the hack_d02eae87c92ec51173c3659216d87518.php file is created. In this case, it is always worth trying attacks with a zero byte in the file name. Even in new versions of PHP, this type of attack can work; one of the null-byte errors (in the move_uploaded_file function) was corrected in March 2015.
Client-side perspective
If the uploaded file is directly accessible via the URL, there are a number of extensions that will result in the XSS being executed. The easiest way is to simply send a .html file with the following content:
1 |
<script>alert(1)</script> |
XSSs are still possible through many file extensions that the browser interprets as XML, e.g. .xml, .xsl, .rss, .svg, .xslt and others. Payload, which should work for any file based on XML, is:
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <script>alert(1)</script> </html> <?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <script>alert(1)</script> </html> |
The next way to execute XSS files are Flash files (.swf). Flash, through the ExternalInterface class, can refer to the JS engine in the browser. On GitHub, you can find an example of the execution of XSS by .swf.
Black list of extensions
The standard answer to the above problems is to introduce a black list of extensions, in other words: explicit extensions which are not allowed. If a file with an extension from this list is sent to the server, it responds with a message informing about the incorrect type. What problems may be associated with such a solution?
Server-side perspective
Too short blacklist
The authors of the application are not always aware of which specific extensions result in the execution of the code. If there is only a .php extension on the blacklist, perhaps the security will be circumvented using .php5. For Microsoft IIS server—a blacklist consisting of the .aspx extension—omit by .asp, etc.
File with many extensions
The problem described in this section is specific to the Apache server. Basically, there are two ways to define the handling of .php files by the language parser: SetHandler and AddHandler. An example entry in the configuration with the use of the latter:
1 |
AddHandler application/x-httpd-php .php |
AddHandler has its own specific behavior, which is explained in the documentation:
Filenames may have multiple extensions and the extension argument will be compared against each of them.
This means that if you send the file test.php.jpg to the server, when no handlers for .jpg extensions are defined on the server (which is very likely), such a file will be executed by the PHP parser.
Interestingly, the AddHandler directive was used in the default configuration in Red Hat Enterprise Linux 6/CentOS 6 distributions as well as in earlier versions. It was only in the seventh version, released in the middle of last year, that it was changed to SetHandler. Just in case, check your configurations.
Case insensitivity
The problem concerns, in particular, applications running on Windows, where the case sensitivity in file names is not considered. We can therefore come across an implementation in which only the .aspx extension will be blacklisted, and the file with the .ASPX extension will be passed which, in effect, will lead to RCE.
Client-side perspective
Usually, when using the black list of extensions, application developers do not consider the problem of client-side attacks. With a high probability, you will be able to upload a file with one of the extensions mentioned earlier; i.e., .html, .htm, .xml, .swf etc.
White list of extensions
Approach where the application explicitly provided a white list of extensions, and thus a list of extensions, where uploading is allowed. For pictures, the white list could consist, for example, of extensions: [“jpg”, “png”, “jpeg”, “gif”, „tiff”].
Server-side perspective
LFI (Local File Inclusion)
An LFI error may occur elsewhere in the application.
1 2 |
<?php include 'includes/'.$_GET['template']; |
As the application does not check the contents of the files, but only their extension, the uploaded “image” file will be able to be used to execute the PHP code.
Verification of the file content
As the application expects an image, you can add a verification code to it. Using one of the libraries, the file sent will be checked to see if it is an image. Fundamentally, we can distinguish two ways of verification:
- Checking the file without modifying its contents. You can then place the PHP code in EXIF tags or (in the case of some formats) add it at the end of the file.
- Processing the image and saving the file with new content. However, there are known attacks that allow you to place PHP code after processing the PNG file. It is worth noting that there may be errors in individual implementations (e.g., there is an error in the imagefromgif and imagefromjpeg functions in PHP, allowing some of the malicious PHP code to be kept).
Client-side perspective
Incorrect white list
The white list of allowed image extensions can contain .svg, which is actually a regular XML file parsed by the browser. Then you can use the code mentioned earlier in this article and make XSS.
Uploading Flash file
The .swf files allow a certain interesting attack when the application verifies the extensions, but does not check the contents of the file. In Flash, the rules for the same source policy are a little different than in the case of JavaScript itself. For example, if we have an .swf file hosted on www.google.com, then regardless of which domain it is called from, it can download any resources (POST and GET methods) from the domain www.google.com. The previously mentioned xss.swf script can be used to confirm that such an attack will actually succeed. What’s more—even if the file has the .jpg extension, you can still persuade the browser to interpret it as a Flash file. Assume that domain victim.com has managed to host a xss.png file, which is actually xss.swf. Then add the code to the domain controlled by the attacker:
1 |
<embed allowscriptaccess="always" src="http://www.victim.com/uploads/xss.png?a=get&c=http://www.victim.com/inny_zasob" type="application/x-shockwave-flash"> |
If everything works out, we should answer “corss domain request ok” (the typo is intended; the same is in the script code).
Protection methods
So far we have only focused on various methods of uploading in web applications, but how should we defend? All attacks (whether it was server-side or client-side) were related to the fact that the files downloaded to the server are on the same host and the same domain as the web application. Whenever possible, distinguish a separate domain (e.g., if the application is running on www.example.com, the files can be hosted at www.example-files.com), as well as a separate server instance for uploads only. Thanks to this, the risk related to the execution of the code will be eliminated, and all XSS attacks will take place on a separate domain.
However, we do not always have the resources and the possibilities to implement such a solution. Then it is important to remember a few basic rules:
- Files uploaded by users should be saved outside the webroot,
- Files should not be saved under the same file name as the user sent. It’s best to use a random hash. The original file name can be stored in the database. Alternatively, the entire file can be stored in the database.
- For HTTP responses with downloads previously uploaded, add headers:
- Content-Disposition: attachment – thanks to which the browser will prompt to download the file.
- X-Content-Type-Options: nosniff – thanks to which some attacks related to guessing the type of document by browsers will be prevented (this applies in particular to Internet Explorer).