Shell upload vulnerabilities allow an attacker to upload a malicious PHP file and execute it by accessing it via a web browser. The "shell" is a PHP script that allows the attacker to control the server - essentially a backdoor program, similar in functionality to a trojan for personal computers. If the attacker can upload this page/shell to a web site, they can control the application server. Shell upload vulnerabilities are very easy to find and exploit in PHP. File uploads are usually handled by the move_uploaded_file() function, so searching for calls to that function can reveal code that might be potentially vulnerable. Multiple methods are available to prevent shell upload vulnerabilities, which I’ll discuss in just a bit.
An example of a web-shell is a web page that includes a text field and a button and when you enter some commands into the text field and press the button, the web page will execute these commands on the server and then display itself again; it will also show the output from the executed commands. This is an example of a very simple shell. Actual shells are just more advanced variations of this principle and often include features like file browsers and database managers. If you want to look at actual web shells, do a Google image search for "c100 shell" and feel the 1337ness of the UI design.
To determine whether it is possible to upload the shell, an attacker can search application code for calls to move_uploaded_file() and see if the file upload functionality can be abused. It is also possible to use the copy() function to handle uploaded files in earlier versions of PHP, but that’s a much less common way of handling file uploads. Many new programmers, especially those that are not aware of this vulnerability type, make the mistake of simply uploading files to some folder on the web server, which is the canonical example of the shell upload vulnerability. All an attacker has to do in that case is upload his shell to take over the application.
To prevent shell upload vulnerabilities, search your application code for calls to move_uploaded_files() and strengthen each piece of code that uses that function. I recommend creating a spreadsheet that enumerates all code that can be used to upload files in the application to keep track of the application hardening process. The following defenses can be used to defend against shell upload vulnerabilities:
- require authentication to upload files
- store uploaded files in a location not accessible from the web
- don't eval or include uploaded data
- scramble uploaded file names and extensions,
- define valid types of files that the users should be allowed to upload.
A maximum possible combination of these defenses should be used according to the defense in depth principle.
Authentication should be required to upload files. Examine each piece of code that can be used to upload files to make sure that the move_uploaded_files() function will not be executed unless the script is accessed by a valid authenticated user. Pay particular attention to the fact that PHP files can be executed individually and not as a part of the application. One effective technique to prevent PHP files from being executed independently from the main application is to place all code in supplementary files inside class definitions. Another method is to check the value of a variable that is defined by the application before executing any code in the supplementary PHP files. The supplementary files are the files that contain application code but are not intended to be directly executed by the user by being accessed via HTTP requests - these files are intended to be executed by the application when needed using the include() function.
Another mitigation technique is to store the uploaded files in a location that is not web-accessible. There are several options for doing so. It's possible to store uploaded files outside of the web root, in a database, or in a folder that is configured as inaccessible using the web server configuration. A web application developer should know what the web root folder is (the folder that is accessible from the web). Placing uploaded files a level above the web root folder makes them inaccessible from the web. The result is that even if an attacker is able to upload a shell, the attacker won't be able to access it.
Storing uploaded files outside the web root is a strong and easy to implement measure, but it might make installing the application on a large amount of servers slightly more difficult. Because the servers then need to be configured to allow storing files outside of the web root by creating a folder to store the uploaded files and granting the web server permissions to write to that folder, this additional configuration work is a primary reason why many commercial applications store uploaded files in a web accessible locations – and subsequently, suffer from shell upload vulnerabilities. Another mitigation method, which is virtually identical, is to store uploaded files in a database. Files stored in a database cannot be accessed directly via HTTP requests, so even if an attacker is able to upload a shell, they won't be able to access it. If the application is already using a database, there is no additional end-user configuration required for using the database method to contain the uploaded files; however, it is harder to code and there is some maintenance overhead because the database might become quite large and therefore the backups also.
Configuring a folder inside of web root as web inaccessible using web server configuration directive is another mitigation technique, however it’s also the easiest to implement incorrectly. Most commonly this is accomplished using .htaccess files. The challenge is that protecting the upload folder then becomes the responsibility of the end user. When done correctly, using web server configuration to block access to the uploads folder is just as effective as the other methods, but many users don't set it up correctly.
When it comes to accessing the uploaded files, a PHP script should be used to read the specified file and return its contents. This can be used to show uploaded images in the browser or for any other purpose where it is necessary for the users to access the uploaded files. The fact that a PHP script returns the contents of the uploaded files rather than the web server processing the uploaded files as a result of direct requests means that there is no chance that the uploaded files will be executed as code.
Do not eval or include uploaded data. No realistic application requirements where executing uploaded user files as server-side code would be a good idea come to mind, so this is just a technical note. If you are for some reason tempted to include() or eval() user uploaded files, just don't :)
The file names and extensions of uploaded files should be changed to prevent possible execution. If the original file names need to be preserved, they should be stored in a lookup table, either in a database or in a XML file. Web servers execute PHP files as code based on file extensions. If a file has an extension that is defined as code in the server's configuration, it will be executed. Common PHP file extensions are .php and .php5, but there may be others, depending on the server configuration. It is important not to allow attackers to upload files with extensions that allow the files to be interpreted as code. Scrambling the file extensions, or even removing them completely, accomplishes that. Scrambling the file names also provides the added bonus that it makes it more difficult for the attacker to find the uploaded file(s) and thus makes it harder to create HTTP requests that access those files directly.
Lastly, be sure to define valid types of files that the user is allowed to upload. The application should define possible valid file extensions and the developer should make sure that none of the allowed file extensions can be interpreted as application code by the web server. Do not bother validating the MIME type of the upload - that can be easily faked by the attacker. However, it does help to validate the extension of the file being uploaded and after passing such validation, all uploaded files should still be treated as dangerous. Don't rely on file type validation as a sufficient defense - it should be used in addition to other countermeasures described in this blog.
In summary, shell upload vulnerabilities can be effectively prevented by blocking direct access to uploaded files and limiting the ability of users to upload files.