Wordpress allows users with Author permissions and above to upload files with a variety of extensions. In some cases, it is possible for a user to mount a cross-site scripting attack using those uploaded files.

How Does It Work?

File uploads are allowed by default for users with Author permissions and above. Wordpress uses a list of file extensions to determine whether a particular upload should be allowed or not. It has a general set of extensions that are allowed for single-site Wordpress installs and a more restrictive set of extensions for multi-site Wordpress installations (formerly known as Wordpress MU). Individual administrators may also change the allowed extensions using the plugin system. There is no validation to make sure the content of an uploaded file matches its extension.

Due to this behavior, a hostile user can mount an XSS attack using the file upload feature in a number of ways; I’ve provided a list of some possibilities below. In cases where an attack will work on a single-site install, I’ve marked it Single-Site: in cases where an attack will work on a multi-site install, I’ve marked it Multi-Site:

  1. (Single-Site) Wordpress allows files with .htm/.html extensions to be uploaded. This is an obvious XSS vector.
  2. (Single-Site) Wordpress allows files with a .txt extension (and other files that can be served up as text/plain by a web browser) to be uploaded. IE6, IE7, IE8, and Safari will all perform content sniffing on files served up as text/plain (source: http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors). That means HTML included in those files will be executed in IE and Safari.
  3. (Single-Site, Multi-Site) Wordpress allows files with a .swf extension to be uploaded on single-site installs. In addition, SWF applets can be uploaded to the server using any other file extension. If the file is on the same domain as the Wordpress installation, it can make requests back to the Wordpress installation without the need for explicit authorization via a crossdomain.xml file.
  4. (Single-Site, Multi-Site) IE6/7 will sniff for HTML when they encounter “corrupted” images (files served with image/* headers that don’t actually contain valid image data). So, lets say you upload a file with a GIF extension that contains all HTML: Wordpress will accept it, the web server will serve it up as image/gif, and IE6/7 will parse the HTML and be vulnerable to an attack.
  5. (Single-Site, Multi-Site) The configuration of the webserver for certain file extensions may trigger unexpected content-sniffing behaviors in specific browsers. For example, if a file (regardless of extension) gets served up as application/octet-stream, IE, Safari, and Opera will all sniff for HTML (see http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors).

Note that uploads only allow for cross-site scripting when they are uploaded to the same domain as Wordpress itself. In other instances, while malicious content may be able to be uploaded, it will not be able to make requests back to Wordpress. Regardless, since the default behavior for Wordpress is to upload content into the wp-content/uploads folder on the same domain as the website, any site that has not reconfigured their upload directory is at risk.

This vulnerability is symptomatic of a larger design choice in Wordpress: by allowing the webserver to serve uploaded files directly, Wordpress has seceded control over how those files are presented to the user. This decision has led to an arbitrary code execution vulnerability in the not so distant past and there’s no guarantee that certain server configurations won’t lead to similar vulnerabilities in the future. Existing plugins may even open the door for an attack. Other software, like phpBB, serves all attachments to users through a PHP script: this design decision allows the software to more fully protect users from malicious uploads.

Where do we go from here?

There are a couple different workarounds for people who are hosting uploads from the same domain as their Wordpress installation:

  1. Move your uploaded files to a separate domain (or a subdomain)
    Thanks to the way browsers handle cross-domain communication, moving uploaded files off of your main domain will prevent them from having access to your Wordpress installation, mitigating the impact of this vulnerability. Once you’ve moved your content, you will need to update your Wordpress settings.
    If you’re considering moving your files to a subdomain, keep in mind that under certain circumstances, different subdomains of the same website may actually be able to communicate (ie: by setting document.domain equal to the same value). If you need to allow that kind of communication with your main domain, host your uploads on an entirely separate domain.
  2. Disable file uploads
    If an attacker can’t upload files, they can’t exploit the vulnerability.

In the longer term, there needs to be a discussion about how to balance the simplicity of the current system with the need to be secure.